Merge "libtimeinstate: correctly handle devices with no boost freqs"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4f7cdf3..3fa5430 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
include/input/
+ libs/binder/fuzzer/
libs/binder/ndk/
libs/graphicsenv/
libs/gui/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 86558ef..0a91a07 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -300,12 +300,10 @@
* Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
* The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
* is set, the vector only contains files that were written in the last 30 minutes.
- * If |limit_by_count| is set, the vector only contains the ten latest files.
*/
static std::vector<DumpData> GetDumpFds(const std::string& dir_path,
const std::string& file_prefix,
- bool limit_by_mtime,
- bool limit_by_count = true) {
+ bool limit_by_mtime) {
const time_t thirty_minutes_ago = ds.now_ - 60 * 30;
std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir);
@@ -349,15 +347,6 @@
dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime});
}
- // Sort in descending modification time so that we only keep the newest
- // reports if |limit_by_count| is true.
- std::sort(dump_data.begin(), dump_data.end(),
- [](const DumpData& d1, const DumpData& d2) { return d1.mtime > d2.mtime; });
-
- if (limit_by_count && dump_data.size() > 10) {
- dump_data.erase(dump_data.begin() + 10, dump_data.end());
- }
-
return dump_data;
}
@@ -918,6 +907,31 @@
"-v", "uid", "-d", "*:v"});
}
+static void DumpIncidentReport() {
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
+ return;
+ }
+ DurationReporter duration_reporter("INCIDENT REPORT");
+ const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
+ auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
+ return;
+ }
+ RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build());
+ bool empty = 0 == lseek(fd, 0, SEEK_END);
+ if (!empty) {
+ // Use a different name from "incident.proto"
+ // /proto/incident.proto is reserved for incident service dump
+ // i.e. metadata for debugging.
+ ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
+ }
+ unlink(path.c_str());
+}
+
static void DumpIpTablesAsRoot() {
RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
@@ -1490,6 +1504,9 @@
printf("========================================================\n");
// This differs from the usual dumpsys stats, which is the stats report data.
RunDumpsys("STATSDSTATS", {"stats", "--metadata"});
+
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
+
return Dumpstate::RunStatus::OK;
}
@@ -2708,7 +2725,7 @@
}
/* tell activity manager we're done */
- if (options_->do_broadcast) {
+ if (options_->do_broadcast && !CalledByApi()) {
SendBugreportFinishedBroadcast();
// Note that listener_ is notified in Run();
}
diff --git a/cmds/dumpsys/TEST_MAPPING b/cmds/dumpsys/TEST_MAPPING
new file mode 100644
index 0000000..dc88ada
--- /dev/null
+++ b/cmds/dumpsys/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ // small test which assumes the output format of dumpsys, however
+ // there are many other parts of Android that expect the output
+ // to be a specific way (see b/141728094)
+ "name": "timezone_data_e2e_tests"
+ },
+ {
+ "name": "dumpsys_test"
+ }
+ ]
+}
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 4811927..9f65425 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -201,7 +201,13 @@
if (i == optind) {
services.add(String16(argv[i]));
} else {
- args.add(String16(argv[i]));
+ const String16 arg(argv[i]);
+ args.add(arg);
+ // For backward compatible, if the proto argument is passed to the service, the
+ // dump request is also considered to use proto.
+ if (!asProto && !arg.compare(String16(PriorityDumper::PROTO_ARG))) {
+ asProto = true;
+ }
}
}
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index dd51898..4026f29 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -832,7 +832,7 @@
};
LOG(DEBUG) << "Copying " << from << " to " << to;
- return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+ return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
}
binder::Status InstalldNativeService::snapshotAppData(
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 7eee749..838d11d 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -303,6 +303,9 @@
// Location of the apex image.
static const char* kApexImage = "/system/framework/apex.art";
+// Phenotype property name for enabling profiling the boot class path.
+static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
+
class RunDex2Oat : public ExecVHelper {
public:
RunDex2Oat(int zip_fd,
@@ -402,7 +405,15 @@
server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
ENABLE_APEX_IMAGE,
/*default_value=*/ "");
- if (use_apex_image == "true") {
+
+ std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
+ profile_boot_class_path =
+ server_configurable_flags::GetServerConfigurableFlag(
+ RUNTIME_NATIVE_BOOT_NAMESPACE,
+ PROFILE_BOOT_CLASS_PATH,
+ /*default_value=*/ profile_boot_class_path);
+
+ if (use_apex_image == "true" || profile_boot_class_path == "true") {
boot_image = StringPrintf("-Ximage:%s", kApexImage);
} else {
boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
@@ -709,8 +720,7 @@
const unique_fd& reference_profile_fd,
const std::vector<unique_fd>& apk_fds,
const std::vector<std::string>& dex_locations,
- bool copy_and_update,
- bool store_aggregation_counters) {
+ bool copy_and_update) {
// TODO(calin): Assume for now we run in the bg compile job (which is in
// most of the invocation). With the current data flow, is not very easy or
@@ -742,10 +752,6 @@
AddArg("--copy-and-update-profile-key");
}
- if (store_aggregation_counters) {
- AddArg("--store-aggregation-counters");
- }
-
// Do not add after dex2oat_flags, they should override others for debugging.
PrepareArgs(profman_bin);
}
@@ -753,14 +759,12 @@
void SetupMerge(const std::vector<unique_fd>& profiles_fd,
const unique_fd& reference_profile_fd,
const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(),
- const std::vector<std::string>& dex_locations = std::vector<std::string>(),
- bool store_aggregation_counters = false) {
+ const std::vector<std::string>& dex_locations = std::vector<std::string>()) {
SetupArgs(profiles_fd,
reference_profile_fd,
apk_fds,
dex_locations,
- /*copy_and_update=*/false,
- store_aggregation_counters);
+ /*copy_and_update=*/false);
}
void SetupCopyAndUpdate(unique_fd&& profile_fd,
@@ -777,8 +781,7 @@
reference_profile_fd_,
apk_fds_,
dex_locations,
- /*copy_and_update=*/true,
- /*store_aggregation_counters=*/false);
+ /*copy_and_update=*/true);
}
void SetupDump(const std::vector<unique_fd>& profiles_fd,
@@ -792,8 +795,7 @@
reference_profile_fd,
apk_fds,
dex_locations,
- /*copy_and_update=*/false,
- /*store_aggregation_counters=*/false);
+ /*copy_and_update=*/false);
}
void Exec() {
@@ -2828,8 +2830,7 @@
args.SetupMerge(profiles_fd,
snapshot_fd,
apk_fds,
- dex_locations,
- /*store_aggregation_counters=*/true);
+ dex_locations);
pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 6fba0ac..01cf2f8 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -64,6 +64,20 @@
ANDROID_BITMAP_FORMAT_RGBA_F16 = 9,
};
+/** Bitmap alpha format */
+enum {
+ /** Pixel components are premultiplied by alpha. */
+ ANDROID_BITMAP_FLAGS_ALPHA_PREMUL = 0,
+ /** Pixels are opaque. */
+ ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE = 1,
+ /** Pixel components are independent of alpha. */
+ ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL = 2,
+ /** Bit mask for AndroidBitmapFormat.flags to isolate the alpha. */
+ ANDROID_BITMAP_FLAGS_ALPHA_MASK = 0x3,
+ /** Shift for AndroidBitmapFormat.flags to isolate the alpha. */
+ ANDROID_BITMAP_FLAGS_ALPHA_SHIFT = 0,
+};
+
/** Bitmap info, see AndroidBitmap_getInfo(). */
typedef struct {
/** The bitmap width in pixels. */
@@ -74,8 +88,9 @@
uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
- /** Unused. */
- uint32_t flags; // 0 for now
+ /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK
+ * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. */
+ uint32_t flags;
} AndroidBitmapInfo;
/**
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 4365a3c..d23e3b7 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -37,7 +37,6 @@
virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
};
@@ -51,8 +50,7 @@
enum {
SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_INPUT_CHANNEL_TRANSACTION,
- UNREGISTER_INPUT_CHANNEL_TRANSACTION,
- TRANSFER_TOUCH_FOCUS
+ UNREGISTER_INPUT_CHANNEL_TRANSACTION
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 59d16d1..eaa562b 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -359,6 +359,9 @@
DEFINE_AXIS(BRAKE),
DEFINE_AXIS(DISTANCE),
DEFINE_AXIS(TILT),
+ DEFINE_AXIS(SCROLL),
+ DEFINE_AXIS(RELATIVE_X),
+ DEFINE_AXIS(RELATIVE_Y),
DEFINE_AXIS(GENERIC_1),
DEFINE_AXIS(GENERIC_2),
DEFINE_AXIS(GENERIC_3),
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 28b8d80..c056c97 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -35,7 +35,6 @@
#include <binder/IBinder.h>
#include <input/Input.h>
-#include <input/LatencyStatistics.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -289,12 +288,8 @@
status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
private:
- static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min;
sp<InputChannel> mChannel;
- LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
-
- void reportTouchEventForStatistics(nsecs_t evdevTime);
};
/*
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 9284acb..2d6292c 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -34,6 +34,7 @@
name: "libandroid_runtime_lazy",
vendor_available: true,
double_loadable: true,
+ host_supported: true,
cflags: [
"-Wall",
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 49a9414..5e4c98f 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -114,4 +114,4 @@
return INVALID_OPERATION;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 8af2872..296e3f6 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -51,7 +51,7 @@
"IpPrefix.cpp",
]
-cc_library_shared {
+cc_library {
name: "libbinder",
// for vndbinder
@@ -101,9 +101,11 @@
target: {
android: {
srcs: libbinder_device_interface_sources,
- },
- host: {
- cflags: ["-D__ANDROID_HOST__"],
+
+ // NOT static to keep the wire protocol unfrozen
+ static: {
+ enabled: false,
+ },
},
vendor: {
exclude_srcs: libbinder_device_interface_sources,
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 711fed9..9da9c13 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -223,4 +223,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 693045e..2f6e9c3 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -99,6 +99,32 @@
return reply.readNullableStrongBinder(out);
}
+status_t IBinder::getDebugPid(pid_t* out) {
+ BBinder* local = this->localBinder();
+ if (local != nullptr) {
+ *out = local->getDebugPid();
+ return OK;
+ }
+
+ BpBinder* proxy = this->remoteBinder();
+ LOG_ALWAYS_FATAL_IF(proxy == nullptr);
+
+ Parcel data;
+ Parcel reply;
+ status_t status = transact(DEBUG_PID_TRANSACTION, data, &reply);
+ if (status != OK) return status;
+
+ int32_t pid;
+ status = reply.readInt32(&pid);
+ if (status != OK) return status;
+
+ if (pid < 0 || pid > std::numeric_limits<pid_t>::max()) {
+ return BAD_VALUE;
+ }
+ *out = pid;
+ return OK;
+}
+
// ---------------------------------------------------------------------------
class BBinder::Extras
@@ -152,6 +178,9 @@
case EXTENSION_TRANSACTION:
err = reply->writeStrongBinder(getExtension());
break;
+ case DEBUG_PID_TRANSACTION:
+ err = reply->writeInt32(getDebugPid());
+ break;
default:
err = onTransact(code, data, reply, flags);
break;
@@ -250,6 +279,10 @@
return e->mExtension;
}
+pid_t BBinder::getDebugPid() {
+ return getpid();
+}
+
void BBinder::setExtension(const sp<IBinder>& extension) {
Extras* e = getOrCreateExtras();
e->mExtension = extension;
@@ -386,4 +419,4 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index c5aa007..50c7053 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -491,4 +491,4 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index a7d5240..fb424fd 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -280,4 +280,4 @@
return mGlobalState;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index a1c2a8b..64c1ff6 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -308,5 +308,5 @@
return proc->getKernelReferences(count, buf);
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 377f604..1eb5363 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -110,4 +110,4 @@
IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 4c151e7..0ce1dd5 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -66,4 +66,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index c58ea02..6c16c2d 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -309,4 +309,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index cc0022a..a47dbac 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -240,4 +240,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index 59d51ed..b19004d 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -46,4 +46,4 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 77e3d23..4198e49 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -59,4 +59,4 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index a7662e9..c2bb811 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -512,4 +512,4 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 7c45c77..4981d7a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -597,9 +597,8 @@
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
- ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
+ LOG_ALWAYS_FATAL("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
mProcess->mDriverFD, result);
- abort();
}
// Let this thread exit the thread pool if it is no longer
@@ -1326,4 +1325,4 @@
state->mOut.writePointer((uintptr_t)data);
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index bf2f20a..d9bf3cc 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -172,4 +172,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 96e1a8c..a38a27a 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -88,4 +88,4 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 1e11941..556288c 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -65,4 +65,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index b6f3d7b..4bea2179 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -30,7 +30,7 @@
#include <binder/IPermissionController.h>
#endif
-#ifndef __ANDROID_HOST__
+#ifdef __ANDROID__
#include <cutils/properties.h>
#endif
@@ -43,6 +43,17 @@
using AidlServiceManager = android::os::IServiceManager;
using android::binder::Status;
+// libbinder's IServiceManager.h can't rely on the values generated by AIDL
+// because many places use its headers via include_dirs (meaning, without
+// declaring the dependency in the build system). So, for now, we can just check
+// the values here.
+static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_CRITICAL == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
+static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_HIGH == IServiceManager::DUMP_FLAG_PRIORITY_HIGH);
+static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_NORMAL == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL);
+static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_DEFAULT == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_ALL == IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+static_assert(AidlServiceManager::DUMP_FLAG_PROTO == IServiceManager::DUMP_FLAG_PROTO);
+
sp<IServiceManager> defaultServiceManager()
{
static Mutex gDefaultServiceManagerLock;
@@ -63,7 +74,7 @@
return gDefaultServiceManager;
}
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_HOST__)
+#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
// IPermissionController is not accessible to vendors
bool checkCallingPermission(const String16& permission)
@@ -166,14 +177,14 @@
const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
const long timeout = uptimeMillis() + 5000;
+ // Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
-#ifdef __ANDROID_HOST__
- gSystemBootCompleted = true;
-#else
- // Vendor code can't access system properties
+#ifdef __ANDROID__
char bootCompleted[PROPERTY_VALUE_MAX];
property_get("sys.boot_completed", bootCompleted, "0");
gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false;
+#else
+ gSystemBootCompleted = true;
#endif
}
// retry interval in millisecond; note that vendor services stay at 100ms
@@ -285,4 +296,4 @@
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
index 88cc603..a3e2b67 100644
--- a/libs/binder/IShellCallback.cpp
+++ b/libs/binder/IShellCallback.cpp
@@ -85,4 +85,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 82f9047..038e6bf 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -112,4 +112,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/MemoryBase.cpp b/libs/binder/MemoryBase.cpp
index 033066b..32300df 100644
--- a/libs/binder/MemoryBase.cpp
+++ b/libs/binder/MemoryBase.cpp
@@ -43,4 +43,4 @@
}
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index eacad3b..ebf91f9 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -481,4 +481,4 @@
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 4c300b4..e4ea60f 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -181,4 +181,4 @@
}
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 2a75619..573a038 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -68,7 +68,7 @@
static size_t pad_size(size_t s) {
if (s > (std::numeric_limits<size_t>::max() - 3)) {
- abort();
+ LOG_ALWAYS_FATAL("pad size too big %zu", s);
}
return PAD_SIZE_UNSAFE(s);
}
@@ -295,7 +295,7 @@
{
size_t result = dataSize() - dataPosition();
if (result > INT32_MAX) {
- abort();
+ LOG_ALWAYS_FATAL("result too big: %zu", result);
}
return result;
}
@@ -332,7 +332,7 @@
if (pos > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
- abort();
+ LOG_ALWAYS_FATAL("pos too big: %zu", pos);
}
mDataPos = pos;
@@ -509,7 +509,7 @@
}
}
-#ifdef __ANDROID_VNDK__
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
@@ -2793,4 +2793,4 @@
mMutable = false;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index a4c28ad..75a6d22 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -110,4 +110,4 @@
}
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/PermissionController.cpp b/libs/binder/PermissionController.cpp
index 34b2ca5..0c89245 100644
--- a/libs/binder/PermissionController.cpp
+++ b/libs/binder/PermissionController.cpp
@@ -85,4 +85,4 @@
return service != nullptr ? service->getPackageUid(package, flags) : -1;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index 5cb2033..00d6eef 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -101,4 +101,4 @@
ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService);
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index eb828c3..0336d3e 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -387,4 +387,4 @@
mDriverFD = -1;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 101eba3..684a7dc 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -69,4 +69,4 @@
return to;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
new file mode 100644
index 0000000..d2f0d37
--- /dev/null
+++ b/libs/binder/fuzzer/Android.bp
@@ -0,0 +1,39 @@
+cc_fuzz {
+ name: "binder_parcel_fuzzer",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+ srcs: [
+ "binder.cpp",
+ "binder_ndk.cpp",
+ "hwbinder.cpp",
+ "main.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libbinderthreadstate",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libprocessgroup",
+ "libjsoncpp",
+ "libutils",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ },
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
new file mode 100644
index 0000000..86264db
--- /dev/null
+++ b/libs/binder/fuzzer/binder.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder"
+
+#include "binder.h"
+#include "util.h"
+
+#include <android/os/IServiceManager.h>
+
+using ::android::status_t;
+
+class ExampleParcelable : public android::Parcelable {
+public:
+ status_t writeToParcel(android::Parcel* /*parcel*/) const override {
+ FUZZ_LOG() << "should not reach";
+ abort();
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override {
+ mExampleExtraField++;
+ return parcel->readInt64(&(this->mExampleUsedData));
+ }
+private:
+ int64_t mExampleExtraField = 0;
+ int64_t mExampleUsedData = 0;
+};
+
+struct ExampleFlattenable : public android::Flattenable<ExampleFlattenable> {
+public:
+ size_t getFlattenedSize() const { return sizeof(mValue); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& /*buffer*/, size_t& /*size*/, int*& /*fds*/, size_t& /*count*/) const {
+ FUZZ_LOG() << "should not reach";
+ abort();
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ if (size < sizeof(mValue)) {
+ return android::NO_MEMORY;
+ }
+ android::FlattenableUtils::read(buffer, size, mValue);
+ return android::OK;
+ }
+private:
+ int32_t mValue = 0xFEEDBEEF;
+};
+
+struct ExampleLightFlattenable : public android::LightFlattenablePod<ExampleLightFlattenable> {
+ int32_t mValue = 0;
+};
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+ T t{};\
+ status_t status = p.FUN(&t);\
+ FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\
+ }
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+ T t = p.FUN();\
+ (void) t;\
+ FUZZ_LOG() << #T " done " /* << " value: " << t*/;\
+ }
+
+#define PARCEL_READ_OPT_STATUS(T, FUN) \
+ PARCEL_READ_WITH_STATUS(T, FUN), \
+ PARCEL_READ_NO_STATUS(T, FUN)
+
+std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
+ PARCEL_READ_NO_STATUS(size_t, dataSize),
+ PARCEL_READ_NO_STATUS(size_t, dataAvail),
+ PARCEL_READ_NO_STATUS(size_t, dataPosition),
+ PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ [] (const ::android::Parcel& p, uint8_t pos) {
+ FUZZ_LOG() << "about to setDataPosition: " << pos;
+ p.setDataPosition(pos);
+ FUZZ_LOG() << "setDataPosition done";
+ },
+ PARCEL_READ_NO_STATUS(size_t, allowFds),
+ PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
+ [] (const ::android::Parcel& p, uint8_t len) {
+#ifdef __ANDROID__
+ std::string interface(len, 'a');
+ FUZZ_LOG() << "about to enforceInterface: " << interface;
+ bool b = p.enforceInterface(::android::String16(interface.c_str()));
+ FUZZ_LOG() << "enforced interface: " << b;
+#else
+ FUZZ_LOG() << "skipping enforceInterface";
+ (void)p;
+ (void)len;
+#endif // __ANDROID__
+ },
+ [] (const ::android::Parcel& p, uint8_t /*len*/) {
+#ifdef __ANDROID__
+ FUZZ_LOG() << "about to checkInterface";
+ bool b = p.checkInterface(new android::BBinder());
+ FUZZ_LOG() << "checked interface: " << b;
+#else
+ FUZZ_LOG() << "skipping checkInterface";
+ (void)p;
+#endif // __ANDROID__
+ },
+ PARCEL_READ_NO_STATUS(size_t, objectsCount),
+ PARCEL_READ_NO_STATUS(status_t, errorCheck),
+ [] (const ::android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to read void*";
+ std::vector<uint8_t> data(len);
+ status_t status = p.read(data.data(), len);
+ FUZZ_LOG() << "read status: " << status;
+ },
+ [] (const ::android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to readInplace";
+ const void* r = p.readInplace(len);
+ FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len);
+ },
+ PARCEL_READ_OPT_STATUS(int32_t, readInt32),
+ PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
+ PARCEL_READ_OPT_STATUS(int64_t, readInt64),
+ PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
+ PARCEL_READ_OPT_STATUS(float, readFloat),
+ PARCEL_READ_OPT_STATUS(double, readDouble),
+ PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr),
+ PARCEL_READ_OPT_STATUS(bool, readBool),
+ PARCEL_READ_OPT_STATUS(char16_t, readChar),
+ PARCEL_READ_OPT_STATUS(int8_t, readByte),
+
+ PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to read c-str";
+ const char* str = p.readCString();
+ FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
+ },
+ PARCEL_READ_OPT_STATUS(android::String8, readString8),
+ PARCEL_READ_OPT_STATUS(android::String16, readString16),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+ [] (const ::android::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readString16Inplace";
+ size_t outLen = 0;
+ const char16_t* str = p.readString16Inplace(&outLen);
+ FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
+ << " size: " << outLen;
+ },
+ PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
+
+ // only reading one parcelable type for now
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
+ PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+
+ // only reading one binder type for now
+ PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to read flattenable";
+ ExampleFlattenable f;
+ status_t status = p.read(f);
+ FUZZ_LOG() << "read flattenable: " << status;
+ },
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to read lite flattenable";
+ ExampleLightFlattenable f;
+ status_t status = p.read(f);
+ FUZZ_LOG() << "read lite flattenable: " << status;
+ },
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // TODO: resizeOutVector
+
+ PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
+ [] (const android::Parcel& p, uint8_t /*len*/) {
+ FUZZ_LOG() << "about to readNativeHandle";
+ native_handle_t* t = p.readNativeHandle();
+ FUZZ_LOG() << "readNativeHandle: " << t;
+ if (t != nullptr) {
+ FUZZ_LOG() << "about to free readNativeHandle";
+ native_handle_close(t);
+ native_handle_delete(t);
+ FUZZ_LOG() << "readNativeHandle freed";
+ }
+ },
+ PARCEL_READ_NO_STATUS(int, readFileDescriptor),
+ PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
+ PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
+
+ // TODO(b/131868573): can force read of arbitrarily sized vector
+ // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+
+ [] (const android::Parcel& p, uint8_t len) {
+ FUZZ_LOG() << "about to readBlob";
+ ::android::Parcel::ReadableBlob blob;
+ status_t status = p.readBlob(len, &blob);
+ FUZZ_LOG() << "readBlob status: " << status;
+ },
+ [] (const android::Parcel& p, uint8_t options) {
+ FUZZ_LOG() << "about to readObject";
+ bool nullMetaData = options & 0x1;
+ const void* obj = static_cast<const void*>(p.readObject(nullMetaData));
+ FUZZ_LOG() << "readObject: " << obj;
+ },
+ PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
+ PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
+ PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+};
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/fuzzer/binder.h
new file mode 100644
index 0000000..b224ef4
--- /dev/null
+++ b/libs/binder/fuzzer/binder.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <vector>
+
+#include "parcel_fuzzer.h"
+
+extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/fuzzer/binder_ndk.cpp
new file mode 100644
index 0000000..29da8f7
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder_ndk"
+
+#include "binder_ndk.h"
+
+#include <android/binder_parcel_utils.h>
+
+#include "util.h"
+
+// TODO(b/142061461): parent class
+class SomeParcelable {
+public:
+ binder_status_t readFromParcel(const AParcel* parcel) {
+ return AParcel_readInt32(parcel, &mValue);
+ }
+
+private:
+ int32_t mValue = 0;
+};
+
+#define PARCEL_READ(T, FUN) \
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) { \
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+ T t{}; \
+ binder_status_t status = FUN(p.aParcel(), &t); \
+ FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \
+ }
+
+// clang-format off
+std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
+ // methods from binder_parcel.h
+ [](const NdkParcelAdapter& p, uint8_t pos) {
+ FUZZ_LOG() << "about to set data position to " << pos;
+ binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos);
+ FUZZ_LOG() << "set data position: " << status;
+ },
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to read status header";
+ ndk::ScopedAStatus t;
+ binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
+ FUZZ_LOG() << "read status header: " << status;
+ },
+ PARCEL_READ(int32_t, AParcel_readInt32),
+ PARCEL_READ(uint32_t, AParcel_readUint32),
+ PARCEL_READ(int64_t, AParcel_readInt64),
+ PARCEL_READ(uint64_t, AParcel_readUint64),
+ PARCEL_READ(float, AParcel_readFloat),
+ PARCEL_READ(double, AParcel_readDouble),
+ PARCEL_READ(bool, AParcel_readBool),
+ PARCEL_READ(char16_t, AParcel_readChar),
+ PARCEL_READ(int8_t, AParcel_readByte),
+
+ // methods from binder_parcel_utils.h
+ PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readNullableStrongBinder),
+ PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readRequiredStrongBinder),
+ PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readNullableParcelFileDescriptor),
+ PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor),
+ PARCEL_READ(std::string, ndk::AParcel_readString),
+ PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString),
+ // TODO(b/131868573): can force process to allocate arbitrary amount of
+ // memory
+ // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>,
+ // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>,
+ // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
+ // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
+ // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+};
+// clang-format on
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/fuzzer/binder_ndk.h
new file mode 100644
index 0000000..622cafc
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_auto_utils.h>
+#include <vector>
+
+#include <android/binder_parcel.h>
+#include "parcel_fuzzer.h"
+
+// libbinder_ndk doesn't export this header which breaks down its API for NDK
+// and APEX users, but we need access to it to fuzz.
+#include "../ndk/parcel_internal.h"
+
+class NdkParcelAdapter {
+public:
+ NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {}
+
+ const AParcel* aParcel() const { return mParcel.get(); }
+ AParcel* aParcel() { return mParcel.get(); }
+
+ size_t dataSize() const { return aParcel()->get()->dataSize(); }
+ size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
+ size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
+ size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); }
+ android::status_t setData(const uint8_t* buffer, size_t len) {
+ return aParcel()->get()->setData(buffer, len);
+ }
+
+private:
+ ndk::ScopedAParcel mParcel;
+};
+
+extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/fuzzer/hwbinder.cpp
new file mode 100644
index 0000000..b8cce72
--- /dev/null
+++ b/libs/binder/fuzzer/hwbinder.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "hwbinder"
+
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <hwbinder/Parcel.h>
+
+using ::android::status_t;
+
+// TODO: support scatter-gather types
+
+std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardware::IBinder>& binder) {
+ os << binder.get();
+ return os;
+}
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+ T t = p.FUN();\
+ FUZZ_LOG() << #T " value: " << t;\
+ }
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+ T t;\
+ status_t status = p.FUN(&t);\
+ FUZZ_LOG() << #T " status: " << status << " value: " << t;\
+ }
+
+std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS {
+ PARCEL_READ_NO_STATUS(size_t, dataSize),
+ PARCEL_READ_NO_STATUS(size_t, dataAvail),
+ PARCEL_READ_NO_STATUS(size_t, dataPosition),
+ PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ [] (const ::android::hardware::Parcel& p, uint8_t pos) {
+ FUZZ_LOG() << "about to setDataPosition: " << pos;
+ p.setDataPosition(pos);
+ FUZZ_LOG() << "setDataPosition done";
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t length) {
+ FUZZ_LOG() << "about to enforceInterface";
+ std::string interfaceName(length, 'a');
+ bool okay = p.enforceInterface(interfaceName.c_str());
+ FUZZ_LOG() << "enforceInterface status: " << okay;
+ },
+ PARCEL_READ_NO_STATUS(size_t, objectsCount),
+ PARCEL_READ_WITH_STATUS(int8_t, readInt8),
+ PARCEL_READ_WITH_STATUS(uint8_t, readUint8),
+ PARCEL_READ_WITH_STATUS(int16_t, readInt16),
+ PARCEL_READ_WITH_STATUS(uint16_t, readUint16),
+ PARCEL_READ_WITH_STATUS(int32_t, readInt32),
+ PARCEL_READ_WITH_STATUS(uint32_t, readUint32),
+ PARCEL_READ_WITH_STATUS(int64_t, readInt64),
+ PARCEL_READ_WITH_STATUS(uint64_t, readUint64),
+ PARCEL_READ_WITH_STATUS(float, readFloat),
+ PARCEL_READ_WITH_STATUS(double, readDouble),
+ PARCEL_READ_WITH_STATUS(bool, readBool),
+ PARCEL_READ_WITH_STATUS(::android::String16, readString16),
+ PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
+ PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
+ [] (const ::android::hardware::Parcel& p, uint8_t amount) {
+ FUZZ_LOG() << "about to readInPlace " << amount;
+ const uint8_t* data = (const uint8_t*)p.readInplace(amount);
+ if (data) {
+ std::vector<uint8_t> vdata(data, data + amount);
+ FUZZ_LOG() << "readInPlace " << amount << " data: " << hexString(vdata);
+ } else {
+ FUZZ_LOG() << "readInPlace " << amount << " no data";
+ }
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readBuffer";
+ size_t handle = 0;
+ const void* data = nullptr;
+ status_t status = p.readBuffer(size, &handle, &data);
+ FUZZ_LOG() << "readBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readNullableBuffer";
+ size_t handle = 0;
+ const void* data = nullptr;
+ status_t status = p.readNullableBuffer(size, &handle, &data);
+ FUZZ_LOG() << "readNullableBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readEmbeddedBuffer";
+ size_t handle = 0;
+ size_t parent_buffer_handle = 0;
+ size_t parent_offset = 3;
+ const void* data = nullptr;
+ status_t status = p.readEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+ FUZZ_LOG() << "readEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t size) {
+ FUZZ_LOG() << "about to readNullableEmbeddedBuffer";
+ size_t handle = 0;
+ size_t parent_buffer_handle = 0;
+ size_t parent_offset = 3;
+ const void* data = nullptr;
+ status_t status = p.readNullableEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+ FUZZ_LOG() << "readNullableEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+ // should be null since we don't create any IPC objects
+ CHECK(data == nullptr) << data;
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readNativeHandleNoDup";
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readNativeHandleNoDup(&handle);
+ FUZZ_LOG() << "readNativeHandleNoDup status: " << status << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ CHECK(status != ::android::OK);
+ },
+ [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to readNullableNativeHandleNoDup";
+ const native_handle_t* handle = nullptr;
+ status_t status = p.readNullableNativeHandleNoDup(&handle);
+ FUZZ_LOG() << "readNullableNativeHandleNoDup status: " << status << " handle: " << handle;
+
+ // should be null since we don't create any IPC objects
+ CHECK(handle == nullptr) << handle;
+ },
+};
diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/fuzzer/hwbinder.h
new file mode 100644
index 0000000..a6c66be
--- /dev/null
+++ b/libs/binder/fuzzer/hwbinder.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hwbinder/Parcel.h>
+#include <vector>
+
+#include "parcel_fuzzer.h"
+
+extern std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
new file mode 100644
index 0000000..369aa34
--- /dev/null
+++ b/libs/binder/fuzzer/main.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <cstdlib>
+#include <ctime>
+
+template <typename P>
+void doFuzz(
+ const std::vector<ParcelRead<P>>& reads,
+ const std::vector<uint8_t>& input,
+ const std::vector<uint8_t>& instructions) {
+
+ P p;
+ p.setData(input.data(), input.size());
+
+ for (size_t i = 0; i < instructions.size() - 1; i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "size: " << p.dataSize() << " avail: " << p.dataAvail()
+ << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
+
+ reads[a % reads.size()](p, b);
+ }
+}
+
+void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
+ (void) options;
+
+ // although they will do completely different things, might as well fuzz both
+ doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input, instructions);
+ doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
+ doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+ uint8_t options = *data;
+ data++;
+ size--;
+
+ // TODO: generate 'objects' data
+
+ // data to fill out parcel
+ size_t inputLen = size / 2;
+ std::vector<uint8_t> input(data, data + inputLen);
+ data += inputLen;
+ size -= inputLen;
+
+ // data to use to determine what to do
+ size_t instructionLen = size;
+ std::vector<uint8_t> instructions(data, data + instructionLen);
+ data += instructionLen;
+ size -= instructionLen;
+
+ CHECK(size == 0) << "size: " << size;
+
+ FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
+ FUZZ_LOG() << "input: " << hexString(input);
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ fuzz(options, input, instructions);
+ return 0;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/fuzzer/parcel_fuzzer.h
new file mode 100644
index 0000000..10cf17c
--- /dev/null
+++ b/libs/binder/fuzzer/parcel_fuzzer.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+template <typename P>
+using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/fuzzer/util.cpp
new file mode 100644
index 0000000..b1213e9
--- /dev/null
+++ b/libs/binder/fuzzer/util.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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 FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ std::ostringstream s;
+ s << std::hex << std::setfill('0');
+ for (size_t i = 0; i < len; i++) {
+ s << std::setw(2) << static_cast<int>(
+ static_cast<const uint8_t*>(bytes)[i]);
+ }
+ return s.str();
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+ return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/fuzzer/util.h
new file mode 100644
index 0000000..416c3a7
--- /dev/null
+++ b/libs/binder/fuzzer/util.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define ENABLE_LOG_FUZZ 1
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG, ENABLE_LOG_FUZZ).log()
+
+class FuzzLog {
+public:
+ FuzzLog(const std::string& tag, bool log) : mTag(tag), mLog(log) {}
+ ~FuzzLog() {
+ if (mLog) {
+ std::cout << mTag << ": " << mOs.str() << std::endl;
+ }
+ }
+
+ std::stringstream& log() {
+ return mOs;
+ }
+
+private:
+ std::string mTag;
+ bool mLog;
+ std::stringstream mOs;
+};
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 5f324c7..9108e31 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -89,7 +89,7 @@
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index f5a54ce..0ab40b8 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -160,7 +160,7 @@
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 1095c7f..3be61f9 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -68,6 +68,8 @@
// This must be called before the object is sent to another process. Not thread safe.
void setExtension(const sp<IBinder>& extension);
+ pid_t getDebugPid();
+
protected:
virtual ~BBinder();
@@ -112,7 +114,7 @@
std::atomic<int32_t> mState;
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
index 9230e89..c17ae6f 100644
--- a/libs/binder/include/binder/BinderService.h
+++ b/libs/binder/include/binder/BinderService.h
@@ -62,6 +62,6 @@
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_BINDER_SERVICE_H
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 28599f4..7dca733 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -144,7 +144,7 @@
static bool sBinderProxyThrottleCreate;
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/include/binder/BufferedTextOutput.h
index feae93d..1b27bb2 100644
--- a/libs/binder/include/binder/BufferedTextOutput.h
+++ b/libs/binder/include/binder/BufferedTextOutput.h
@@ -62,6 +62,6 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_BUFFEREDTEXTOUTPUT_H
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
index 58e2b32..324e5c1 100644
--- a/libs/binder/include/binder/Debug.h
+++ b/libs/binder/include/binder/Debug.h
@@ -44,6 +44,6 @@
__END_DECLS
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_BINDER_DEBUG_H
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index 6abc071..e0248f6 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -51,7 +51,7 @@
// ------------------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
index b500219..7664260 100644
--- a/libs/binder/include/binder/IAppOpsCallback.h
+++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -52,7 +52,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 9784003..8b8a3c2 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -88,6 +88,6 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
index 48da865..b786f89 100644
--- a/libs/binder/include/binder/IBatteryStats.h
+++ b/libs/binder/include/binder/IBatteryStats.h
@@ -77,7 +77,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 027e088..64f3052 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -59,6 +59,7 @@
INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
+ DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
// Corresponds to TF_ONE_WAY -- an asynchronous call.
FLAG_ONEWAY = 0x00000001
@@ -129,6 +130,11 @@
*/
status_t getExtension(sp<IBinder>* out);
+ /**
+ * Dump PID for a binder, for debugging.
+ */
+ status_t getDebugPid(pid_t* outPid);
+
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t transact( uint32_t code,
const Parcel& data,
@@ -236,7 +242,7 @@
private:
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 0d30560..28ffa48 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -88,8 +88,12 @@
public: \
+#define __IINTF_CONCAT(x, y) (x ## y)
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const ::android::String16 I##INTERFACE::descriptor(NAME); \
+ const ::android::StaticString16 \
+ I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
+ const ::android::String16 I##INTERFACE::descriptor( \
+ I##INTERFACE##_descriptor_static_str16); \
const ::android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
@@ -168,6 +172,6 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
index 213ee63..da2b7cf 100644
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -52,7 +52,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 8791741..1a36eb0 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -123,6 +123,6 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_IMEMORY_H
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index b810f7e..ff9244e 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -196,7 +196,7 @@
ProcessState::CallRestriction mCallRestriction;
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
index 26a1b23..4b66df8 100644
--- a/libs/binder/include/binder/IPermissionController.h
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -65,7 +65,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
index 033c145..ca30ad3 100644
--- a/libs/binder/include/binder/IProcessInfoService.h
+++ b/libs/binder/include/binder/IProcessInfoService.h
@@ -46,7 +46,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
index 00b3d89..70e99e7 100644
--- a/libs/binder/include/binder/IResultReceiver.h
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -50,7 +50,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_IRESULT_RECEIVER_H
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 8ae860d..cd63a58 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -31,7 +31,7 @@
public:
DECLARE_META_INTERFACE(ServiceManager)
/**
- * Must match values in IServiceManager.java
+ * Must match values in IServiceManager.aidl
*/
/* Allows services to dump sections according to priorities. */
static const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0;
@@ -104,7 +104,7 @@
int32_t* outPid, int32_t* outUid);
bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
-}; // namespace android
+} // namespace android
#endif // ANDROID_ISERVICE_MANAGER_H
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
index 6715678..b7ab6ea 100644
--- a/libs/binder/include/binder/IShellCallback.h
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -51,7 +51,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_ISHELL_CALLBACK_H
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index a1f530d..09e50a9 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -58,7 +58,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
index 463e26d..4dd3638 100644
--- a/libs/binder/include/binder/MemoryBase.h
+++ b/libs/binder/include/binder/MemoryBase.h
@@ -46,6 +46,6 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_MEMORY_BASE_H
diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
index b483be0..6c1c412 100644
--- a/libs/binder/include/binder/MemoryDealer.h
+++ b/libs/binder/include/binder/MemoryDealer.h
@@ -59,6 +59,6 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_MEMORY_DEALER_H
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 100d784..3fccddc 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -98,6 +98,6 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_MEMORY_HEAP_BASE_H
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 3471e13..8726681 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -921,7 +921,7 @@
return to;
}
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 95eabff..c258215 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -77,7 +77,7 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h
index d81f514..4db522a 100644
--- a/libs/binder/include/binder/PermissionController.h
+++ b/libs/binder/include/binder/PermissionController.h
@@ -60,7 +60,7 @@
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
index a03aae9..6bfd1bc 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -78,7 +78,7 @@
// ----------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 8339976..f7c38f4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -126,7 +126,7 @@
CallRestriction mCallRestriction;
};
-}; // namespace android
+} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index b84657a..2894482 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,7 +81,7 @@
VINTF = 0b111111,
};
-#ifdef __ANDROID_VNDK__
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
static constexpr Level kLocalStability = Level::VENDOR;
#else
static constexpr Level kLocalStability = Level::SYSTEM;
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index 5b5f766..f66406f 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -199,6 +199,6 @@
inline bool HexDump::carrayStyle() const { return mCArrayStyle; }
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_TEXTOUTPUT_H
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 09e6ba0..c22be9f 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -24,7 +24,7 @@
/* obtain structures and constants from the kernel header */
// TODO(b/31559095): bionic on host
-#ifdef __ANDROID_HOST__
+#ifndef __ANDROID__
#define __packed __attribute__((__packed__))
#endif
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 734a928..22344b6 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -14,9 +14,25 @@
* limitations under the License.
*/
-cc_library_shared {
+// TODO(b/31559095): bionic on host should define this
+cc_defaults {
+ name: "libbinder_ndk_host_user",
+ target: {
+ host: {
+ cflags: [
+ "-D__INTRODUCED_IN(n)=",
+ "-D__assert(a,b,c)=",
+ ],
+ },
+ },
+}
+
+cc_library {
name: "libbinder_ndk",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+
export_include_dirs: [
"include_ndk",
"include_platform",
@@ -52,10 +68,23 @@
"jni_headers",
],
- version_script: "libbinder_ndk.map.txt",
+ target: {
+ android: {
+ // Only one copy of this library on an Android device
+ static: {
+ enabled: false,
+ },
+ },
+ linux: {
+ version_script: "libbinder_ndk.map.txt",
+ },
+ },
stubs: {
symbol_file: "libbinder_ndk.map.txt",
- versions: ["29", "30"],
+ versions: [
+ "29",
+ "30",
+ ],
},
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 2258210..8c41707 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,6 +26,7 @@
#pragma once
+#include <stddef.h>
#include <sys/cdefs.h>
#include <android/binder_status.h>
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index e6aeb04..b03fce1 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -20,7 +20,7 @@
__BEGIN_DECLS
-#ifdef __ANDROID_VNDK__
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/**
* This interface has the stability of the vendor image.
@@ -31,7 +31,7 @@
AIBinder_markVendorStability(binder);
}
-#else // ndef defined __ANDROID_VNDK__
+#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/**
* This interface has the stability of the system image.
@@ -42,7 +42,7 @@
AIBinder_markSystemStability(binder);
}
-#endif // ifdef __ANDROID_VNDK__
+#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/**
* This interface has system<->vendor stability
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index f25e954..635ea69 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -166,4 +166,5 @@
],
test_suites: ["device-tests"],
+ require_root: true,
}
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
new file mode 100644
index 0000000..c0bb75f
--- /dev/null
+++ b/libs/graphicsenv/OWNERS
@@ -0,0 +1,6 @@
+chrisforbes@google.com
+cnorthrop@google.com
+courtneygo@google.com
+lpy@google.com
+timvp@google.com
+zzyiwei@google.com
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index add3ef0..058cd9a 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -28,8 +28,8 @@
size += sizeof(cta8613);
}
if (validTypes & HDR10PLUS) {
- size += sizeof(size_t);
- size += hdr10plus.size();
+ size += sizeof(uint32_t);
+ size += hdr10plus.size() * sizeof(hdr10plus[0]);
}
return size;
}
@@ -47,10 +47,11 @@
FlattenableUtils::write(buffer, size, cta8613);
}
if (validTypes & HDR10PLUS) {
- size_t metadataSize = hdr10plus.size();
+ uint32_t metadataSize = hdr10plus.size();
FlattenableUtils::write(buffer, size, metadataSize);
- memcpy(buffer, hdr10plus.data(), metadataSize);
- FlattenableUtils::advance(buffer, size, metadataSize);
+ size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+ memcpy(buffer, hdr10plus.data(), metadataSizeinByte);
+ FlattenableUtils::advance(buffer, size, metadataSizeinByte);
}
return NO_ERROR;
@@ -74,20 +75,21 @@
FlattenableUtils::read(buffer, size, cta8613);
}
if (validTypes & HDR10PLUS) {
- if (size < sizeof(size_t)) {
+ if (size < sizeof(uint32_t)) {
return NO_MEMORY;
}
- size_t metadataSize;
+ uint32_t metadataSize;
FlattenableUtils::read(buffer, size, metadataSize);
- if (size < metadataSize) {
+ size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+ if (size < metadataSizeinByte) {
return NO_MEMORY;
}
hdr10plus.resize(metadataSize);
- memcpy(hdr10plus.data(), buffer, metadataSize);
- FlattenableUtils::advance(buffer, size, metadataSize);
+ memcpy(hdr10plus.data(), buffer, metadataSizeinByte);
+ FlattenableUtils::advance(buffer, size, metadataSizeinByte);
}
return NO_ERROR;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index dc161b7..5805797 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -69,7 +69,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& commands,
int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -90,6 +90,7 @@
data.writeInt64(desiredPresentTime);
data.writeStrongBinder(uncacheBuffer.token.promote());
data.writeUint64(uncacheBuffer.id);
+ data.writeBool(hasListenerCallbacks);
if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
for (const auto& [listener, callbackIds] : listenerCallbacks) {
@@ -1039,6 +1040,8 @@
uncachedBuffer.token = data.readStrongBinder();
uncachedBuffer.id = data.readUint64();
+ bool hasListenerCallbacks = data.readBool();
+
std::vector<ListenerCallbacks> listenerCallbacks;
int32_t listenersSize = data.readInt32();
for (int32_t i = 0; i < listenersSize; i++) {
@@ -1047,9 +1050,9 @@
data.readInt64Vector(&callbackIds);
listenerCallbacks.emplace_back(listener, callbackIds);
}
-
setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime, uncachedBuffer, listenerCallbacks);
+ desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
+ listenerCallbacks);
return NO_ERROR;
}
case BOOT_FINISHED: {
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 129558b..b98e48b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -34,7 +34,8 @@
CREATE_WITH_SURFACE_PARENT,
CLEAR_LAYER_FRAME_STATS,
GET_LAYER_FRAME_STATS,
- LAST = GET_LAYER_FRAME_STATS,
+ MIRROR_SURFACE,
+ LAST = MIRROR_SURFACE,
};
} // Anonymous namespace
@@ -80,6 +81,12 @@
&ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
outStats);
}
+
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+ return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
+ mirrorFromHandle,
+ outHandle);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -105,6 +112,8 @@
return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
case Tag::GET_LAYER_FRAME_STATS:
return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
+ case Tag::MIRROR_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::mirrorSurface);
}
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 523ed1d..e004e95 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,7 +86,6 @@
memcpy(output.writeInplace(16 * sizeof(float)),
colorTransform.asArray(), 16 * sizeof(float));
output.writeFloat(cornerRadius);
- output.writeBool(hasListenerCallbacks);
output.writeStrongBinder(cachedBuffer.token.promote());
output.writeUint64(cachedBuffer.id);
output.writeParcelable(metadata);
@@ -95,6 +94,22 @@
output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
output.writeBool(colorSpaceAgnostic);
+ auto err = output.writeVectorSize(listeners);
+ if (err) {
+ return err;
+ }
+
+ for (auto listener : listeners) {
+ err = output.writeStrongBinder(listener.transactionCompletedListener);
+ if (err) {
+ return err;
+ }
+ err = output.writeInt64Vector(listener.callbackIds);
+ if (err) {
+ return err;
+ }
+ }
+
return NO_ERROR;
}
@@ -156,7 +171,6 @@
colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
cornerRadius = input.readFloat();
- hasListenerCallbacks = input.readBool();
cachedBuffer.token = input.readStrongBinder();
cachedBuffer.id = input.readUint64();
input.readParcelable(&metadata);
@@ -165,6 +179,14 @@
bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
colorSpaceAgnostic = input.readBool();
+ int32_t numListeners = input.readInt32();
+ listeners.clear();
+ for (int i = 0; i < numListeners; i++) {
+ auto listener = input.readStrongBinder();
+ std::vector<CallbackId> callbackIds;
+ input.readInt64Vector(&callbackIds);
+ listeners.emplace_back(listener, callbackIds);
+ }
return NO_ERROR;
}
@@ -361,7 +383,6 @@
}
if (other.what & eHasListenerCallbacksChanged) {
what |= eHasListenerCallbacksChanged;
- hasListenerCallbacks = other.hasListenerCallbacks;
}
#ifndef NO_INPUT
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index aad1849..e490d6d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1084,6 +1084,15 @@
case NATIVE_WINDOW_GET_LAST_DEQUEUE_START:
res = dispatchGetLastDequeueStartTime(args);
break;
+ case NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT:
+ res = dispatchSetDequeueTimeout(args);
+ break;
+ case NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION:
+ res = dispatchGetLastDequeueDuration(args);
+ break;
+ case NATIVE_WINDOW_GET_LAST_QUEUE_DURATION:
+ res = dispatchGetLastQueueDuration(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1295,6 +1304,23 @@
return NO_ERROR;
}
+int Surface::dispatchSetDequeueTimeout(va_list args) {
+ nsecs_t timeout = va_arg(args, int64_t);
+ return setDequeueTimeout(timeout);
+}
+
+int Surface::dispatchGetLastDequeueDuration(va_list args) {
+ int64_t* lastDequeueDuration = va_arg(args, int64_t*);
+ *lastDequeueDuration = mLastDequeueDuration;
+ return NO_ERROR;
+}
+
+int Surface::dispatchGetLastQueueDuration(va_list args) {
+ int64_t* lastQueueDuration = va_arg(args, int64_t*);
+ *lastQueueDuration = mLastQueueDuration;
+ return NO_ERROR;
+}
+
bool Surface::transformToDisplayInverse() {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 67dd726..7b256f5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -366,6 +366,33 @@
if (count > parcel->dataSize()) {
return BAD_VALUE;
}
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
+ listenerCallbacks.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ sp<ITransactionCompletedListener> listener =
+ interface_cast<ITransactionCompletedListener>(parcel->readStrongBinder());
+ size_t numCallbackIds = parcel->readUint32();
+ if (numCallbackIds > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ for (size_t j = 0; j < numCallbackIds; j++) {
+ listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+ }
+ size_t numSurfaces = parcel->readUint32();
+ if (numSurfaces > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ for (size_t j = 0; j < numSurfaces; j++) {
+ sp<SurfaceControl> surface;
+ surface = SurfaceControl::readFromParcel(parcel);
+ listenerCallbacks[listener].surfaceControls.insert(surface);
+ }
+ }
+
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
composerStates.reserve(count);
for (size_t i = 0; i < count; i++) {
@@ -389,10 +416,9 @@
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
mDisplayStates = displayStates;
+ mListenerCallbacks = listenerCallbacks;
mComposerStates = composerStates;
mInputWindowCommands = inputWindowCommands;
- // listener callbacks contain function pointer addresses and may not be safe to parcel.
- mListenerCallbacks.clear();
return NO_ERROR;
}
@@ -408,6 +434,19 @@
displayState.write(*parcel);
}
+ parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
+ for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
+ parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
+ parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
+ for (auto callbackId : callbackInfo.callbackIds) {
+ parcel->writeInt64(callbackId);
+ }
+ parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
+ for (auto surfaceControl : callbackInfo.surfaceControls) {
+ surfaceControl->writeToParcel(parcel);
+ }
+ }
+
parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
for (auto const& [surfaceHandle, composerState] : mComposerStates) {
parcel->writeStrongBinder(surfaceHandle);
@@ -441,6 +480,11 @@
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
callbackIds.begin()),
std::make_move_iterator(callbackIds.end()));
+ // register surface controls for this listener that is merging
+ for (const auto& surfaceControl : surfaceControls) {
+ registerSurfaceControlForCallback(surfaceControl);
+ }
+
mListenerCallbacks[listener]
.surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
std::make_move_iterator(surfaceControls.end()));
@@ -479,7 +523,7 @@
composerStates.add(s);
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {});
+ sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, false, {});
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -490,7 +534,7 @@
uncacheBuffer.id = cacheId;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {});
+ sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -539,8 +583,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ bool hasListenerCallbacks = !mListenerCallbacks.empty();
std::vector<ListenerCallbacks> listenerCallbacks;
-
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -548,19 +592,24 @@
continue;
}
- listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
-
- // If the listener has any SurfaceControls set on this Transaction update the surface state
- for (const auto& surfaceControl : surfaceControls) {
- layer_state_t* s = getLayerState(surfaceControl);
- if (!s) {
- ALOGE("failed to get layer state");
- continue;
+ if (surfaceControls.empty()) {
+ listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+ } else {
+ // If the listener has any SurfaceControls set on this Transaction update the surface
+ // state
+ for (const auto& surfaceControl : surfaceControls) {
+ layer_state_t* s = getLayerState(surfaceControl);
+ if (!s) {
+ ALOGE("failed to get layer state");
+ continue;
+ }
+ std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
+ s->what |= layer_state_t::eHasListenerCallbacksChanged;
+ s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
}
- s->what |= layer_state_t::eHasListenerCallbacksChanged;
- s->hasListenerCallbacks = true;
}
}
+
mListenerCallbacks.clear();
cacheBuffers();
@@ -598,7 +647,7 @@
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
mDesiredPresentTime,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- listenerCallbacks);
+ hasListenerCallbacks, listenerCallbacks);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
@@ -1278,7 +1327,9 @@
break;
}
setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
- setPosition(sc, x, y);
+ float offsetX = xScale * source.left;
+ float offsetY = yScale * source.top;
+ setPosition(sc, x - offsetX, y - offsetY);
return *this;
}
@@ -1454,6 +1505,20 @@
return err;
}
+sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFromSurface) {
+ if (mirrorFromSurface == nullptr) {
+ return nullptr;
+ }
+
+ sp<IBinder> handle;
+ sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
+ status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+ if (err == NO_ERROR) {
+ return new SurfaceControl(this, handle, nullptr, true /* owned */);
+ }
+ return nullptr;
+}
+
status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
if (mStatus != NO_ERROR) {
return mStatus;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index c84910b..06be1b3 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -140,7 +140,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
/* signal that we're done booting.
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 32ac9e8..5fe7ca5 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -76,6 +76,8 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
+
+ virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
};
class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2eb5492..a49ed52 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#ifndef NO_INPUT
@@ -123,7 +124,6 @@
surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
- hasListenerCallbacks(false),
bgColorAlpha(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
colorSpaceAgnostic(false) {
@@ -186,7 +186,6 @@
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
- bool hasListenerCallbacks;
#ifndef NO_INPUT
InputWindowInfo inputInfo;
#endif
@@ -203,6 +202,8 @@
// A color space agnostic layer means the color of this layer can be
// interpreted in any color space.
bool colorSpaceAgnostic;
+
+ std::vector<ListenerCallbacks> listeners;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5bcfcda..e582509 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -245,6 +245,9 @@
int dispatchGetConsumerUsage64(va_list args);
int dispatchSetAutoPrerotation(va_list args);
int dispatchGetLastDequeueStartTime(va_list args);
+ int dispatchSetDequeueTimeout(va_list args);
+ int dispatchGetLastDequeueDuration(va_list args);
+ int dispatchGetLastQueueDuration(va_list args);
bool transformToDisplayInverse();
protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8a6920c..6676be4 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -245,6 +245,17 @@
LayerMetadata metadata = LayerMetadata() // metadata
);
+ // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl
+ // which is a parent of the root of the mirrored hierarchy.
+ //
+ // Real Hierarchy Mirror
+ // SC (value that's returned)
+ // |
+ // A A'
+ // | |
+ // B B'
+ sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface);
+
//! Create a virtual display
static sp<IBinder> createDisplay(const String8& displayName, bool secure);
@@ -291,6 +302,7 @@
};
class Transaction : public Parcelable {
+ protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
SortedVector<DisplayState > mDisplayStates;
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 2ab34da..09eeaea 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -133,27 +133,6 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
}
- void expectMotionEvent(int motionEventType, int x, int y) {
- InputEvent *ev = consumeEvent();
- ASSERT_NE(ev, nullptr);
- ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION);
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
- EXPECT_EQ(motionEventType, mev->getAction());
- EXPECT_EQ(x, mev->getX(0));
- EXPECT_EQ(y, mev->getY(0));
- }
-
- void expectNoMotionEvent(int motionEventType) {
- InputEvent *ev = consumeEvent();
- if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) {
- // Didn't find an event or a motion event so assume action didn't occur.
- return;
- }
-
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
- EXPECT_NE(motionEventType, mev->getAction());
- }
-
~InputSurface() {
mInputFlinger->unregisterInputChannel(mServerChannel);
}
@@ -278,15 +257,6 @@
}
}
-void injectMotionEvent(std::string event, int x, int y) {
- char *buf1, *buf2;
- asprintf(&buf1, "%d", x);
- asprintf(&buf2, "%d", y);
- if (fork() == 0) {
- execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL);
- }
-}
-
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
@@ -508,26 +478,6 @@
bgSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, transfer_touch_focus) {
- std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100);
-
- fromSurface->showAt(10, 10);
- injectMotionEvent("DOWN", 11, 11);
- fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1);
-
- std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100);
- toSurface->showAt(10, 10);
-
- sp<IBinder> fromToken = fromSurface->mServerChannel->getToken();
- sp<IBinder> toToken = toSurface->mServerChannel->getToken();
- SurfaceComposerClient::Transaction t;
- t.transferTouchFocus(fromToken, toToken).apply(true);
-
- injectMotionEvent("UP", 11, 11);
- toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
- fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
-}
-
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0b61d92..a4fdb35 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -688,6 +688,7 @@
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
+ bool /*hasListenerCallbacks*/,
const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 7749e66..8efaf3d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -57,7 +57,6 @@
"libutils",
"libbinder",
"libui",
- "libstatslog",
],
sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index 90f6e09..8ec5165 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -45,16 +45,6 @@
IBinder::FLAG_ONEWAY);
}
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
- data.writeStrongBinder(fromToken);
- data.writeStrongBinder(toToken);
- remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
virtual void registerInputChannel(const sp<InputChannel>& channel) {
Parcel data, reply;
data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
@@ -102,13 +92,6 @@
unregisterInputChannel(channel);
break;
}
- case TRANSFER_TOUCH_FOCUS: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<IBinder> fromToken = data.readStrongBinder();
- sp<IBinder> toToken = data.readStrongBinder();
- transferTouchFocus(fromToken, toToken);
- break;
- }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 8a2fc2a..366c93c 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -34,7 +34,6 @@
#include <utils/Trace.h>
#include <input/InputTransport.h>
-#include <statslog.h>
using android::base::StringPrintf;
@@ -538,9 +537,6 @@
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
- if (source == AINPUT_SOURCE_TOUCHSCREEN) {
- reportTouchEventForStatistics(eventTime);
- }
return mChannel->sendMessage(&msg);
}
@@ -567,17 +563,6 @@
return OK;
}
-void InputPublisher::reportTouchEventForStatistics(nsecs_t evdevTime) {
- if (mTouchStatistics.shouldReport()) {
- android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(),
- mTouchStatistics.getMax(), mTouchStatistics.getMean(),
- mTouchStatistics.getStDev(), mTouchStatistics.getCount());
- mTouchStatistics.reset();
- }
- nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime);
- mTouchStatistics.addValue(latency);
-}
-
// --- InputConsumer ---
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp
index e343578..394da22 100644
--- a/libs/input/LatencyStatistics.cpp
+++ b/libs/input/LatencyStatistics.cpp
@@ -84,7 +84,7 @@
bool LatencyStatistics::shouldReport() {
std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime;
- return mCount != 0 && timeSinceReport > mReportPeriod;
+ return mCount != 0 && timeSinceReport >= mReportPeriod;
}
} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 3b195f7..0ba01f4 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -33,6 +33,12 @@
return res < 0 ? res : value;
}
+static int64_t query64(ANativeWindow* window, int what) {
+ int64_t value;
+ int res = window->perform(window, what, &value);
+ return res < 0 ? res : value;
+}
+
static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) {
bool supported = false;
switch (dataSpace) {
@@ -271,16 +277,18 @@
* apex-stable
**************************************************************************************************/
-int ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
- return query(window, NATIVE_WINDOW_LAST_DEQUEUE_DURATION);
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION);
}
-int ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
- return query(window, NATIVE_WINDOW_LAST_QUEUE_DURATION);
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION);
}
int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
- int64_t time;
- int success = window->perform(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START, &time);
- return success < 0 ? success : time;
+ return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START);
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 3ddd549..869b22e 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -27,17 +27,17 @@
* Retrieves how long it took for the last time a buffer was dequeued.
*
* \return a negative value on error, otherwise returns the duration in
- * microseconds.
+ * nanoseconds
*/
-int ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
/**
* Retrieves how long it took for the last time a buffer was queued.
*
* \return a negative value on error, otherwise returns the duration in
- * microseconds
+ * nanoseconds.
*/
-int ANativeWindow_getLastQueueDuration(ANativeWindow* window);
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window);
/**
* Retrieves the system time in nanoseconds when the last time a buffer
@@ -48,4 +48,13 @@
*/
int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
+/**
+ * Sets a timeout in nanoseconds for dequeue calls. All subsequent dequeue calls
+ * made by the window will return -ETIMEDOUT after the timeout if the dequeue
+ * takes too long.
+ *
+ * \return NO_ERROR on succes, -errno on error.
+ */
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
+
__END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b7ae28a..1814ab5 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -63,9 +63,9 @@
/* attributes queriable with query() */
enum {
- NATIVE_WINDOW_WIDTH = 0,
- NATIVE_WINDOW_HEIGHT = 1,
- NATIVE_WINDOW_FORMAT = 2,
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
/* see ANativeWindowQuery in vndk/window.h */
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS,
@@ -147,11 +147,15 @@
/*
* Returns the duration of the last dequeueBuffer call in microseconds
+ * Deprecated: please use NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION in
+ * perform() instead, which supports nanosecond precision.
*/
NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
/*
* Returns the duration of the last queueBuffer call in microseconds
+ * Deprecated: please use NATIVE_WINDOW_GET_LAST_QUEUE_DURATION in
+ * perform() instead, which supports nanosecond precision.
*/
NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
@@ -240,6 +244,9 @@
NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
NATIVE_WINDOW_SET_AUTO_PREROTATION = 35,
NATIVE_WINDOW_GET_LAST_DEQUEUE_START = 36, /* private */
+ NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT = 37, /* private */
+ NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION = 38, /* private */
+ NATIVE_WINDOW_GET_LAST_QUEUE_DURATION = 39, /* private */
// clang-format on
};
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 33c6400..b741faa 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -40,6 +40,7 @@
ANativeWindow_setBuffersGeometry;
ANativeWindow_setBuffersTimestamp; # vndk
ANativeWindow_setBuffersTransform;
+ ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setSharedBufferMode; # vndk
ANativeWindow_setSwapInterval; # vndk
ANativeWindow_setUsage; # vndk
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 947217d..6cf8291 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -74,7 +74,7 @@
TEST_F(ANativeWindowTest, getLastDequeueDuration_noDequeue_returnsZero) {
int result = ANativeWindow_getLastDequeueDuration(mWindow.get());
EXPECT_EQ(0, result);
- EXPECT_EQ(0, mWindow->getLastDequeueDuration() / 1000);
+ EXPECT_EQ(0, mWindow->getLastDequeueDuration());
}
TEST_F(ANativeWindowTest, getLastDequeueDuration_withDequeue_returnsTime) {
@@ -86,7 +86,7 @@
result = ANativeWindow_getLastDequeueDuration(mWindow.get());
EXPECT_GT(result, 0);
- EXPECT_EQ(result, mWindow->getLastDequeueDuration() / 1000);
+ EXPECT_EQ(result, mWindow->getLastDequeueDuration());
}
TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) {
@@ -118,7 +118,7 @@
result = ANativeWindow_getLastQueueDuration(mWindow.get());
EXPECT_GT(result, 0);
- EXPECT_EQ(result, mWindow->getLastQueueDuration() / 1000);
+ EXPECT_EQ(result, mWindow->getLastQueueDuration());
}
TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) {
@@ -138,3 +138,31 @@
EXPECT_GT(result, 0);
EXPECT_EQ(result, mWindow->getLastDequeueStartTime());
}
+
+TEST_F(ANativeWindowTest, setDequeueTimeout_causesDequeueTimeout) {
+ nsecs_t timeout = milliseconds_to_nanoseconds(100);
+ int result = ANativeWindow_setDequeueTimeout(mWindow.get(), timeout);
+ EXPECT_EQ(0, result);
+
+ // The two dequeues should not timeout...
+ ANativeWindowBuffer* buffer;
+ int fd;
+ int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, dequeueResult);
+ int queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+ EXPECT_EQ(0, queueResult);
+ dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ close(fd);
+ EXPECT_EQ(0, dequeueResult);
+ queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+ EXPECT_EQ(0, queueResult);
+
+ // ...but the third one should since the queue depth is too deep.
+ nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+ dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+ nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+ close(fd);
+ EXPECT_EQ(TIMED_OUT, dequeueResult);
+ EXPECT_GE(end - start, timeout);
+}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index dd4c55d..f39f066 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -827,11 +827,14 @@
drawMesh(mesh);
// The middle part of the layer can turn off blending.
- const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
- setScissor(middleRect);
- mState.cornerRadius = 0.0;
- disableBlending();
- drawMesh(mesh);
+ if (topRect.bottom < bottomRect.top) {
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
+ bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ }
disableScissor();
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 96d5eb9..bf8b9f7 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -150,7 +150,7 @@
class DeathObserver : public IBinder::DeathRecipient {
SensorManager& mSensorManager;
virtual void binderDied(const wp<IBinder>& who) {
- ALOGW("sensorservice died [%p]", who.unsafe_get());
+ ALOGW("sensorservice died [%p]", static_cast<void*>(who.unsafe_get()));
mSensorManager.sensorManagerDied();
}
public:
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index bb85acc..47137f1 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -47,6 +47,7 @@
"-Wno-padded",
"-Wno-switch-enum",
+ "-Wno-format-pedantic",
],
sanitize: {
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 8976d2d..0772210 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -24,6 +24,8 @@
namespace android {
+constexpr uint32_t NO_LAYER_STACK = static_cast<uint32_t>(-1);
+
struct DisplayInfo {
uint32_t w{0};
uint32_t h{0};
@@ -37,6 +39,7 @@
nsecs_t presentationDeadline{0};
uint32_t viewportW{0};
uint32_t viewportH{0};
+ uint32_t layerStack{NO_LAYER_STACK};
};
/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
@@ -47,6 +50,6 @@
DISPLAY_ORIENTATION_270 = 3
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 2053344..04493f0 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -40,6 +40,7 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"libbinder",
"libbase",
"libbufferhubqueue",
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 881f1b8..b505712 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,16 +1,7 @@
-# alanward@google.com
-chiur@google.com
chrisforbes@google.com
cnorthrop@google.com
courtneygo@google.com
-hliatis@google.com
ianelliott@google.com
jessehall@google.com
lpy@google.com
-marissaw@google.com
-nduca@google.com
-pmuetschard@google.com
-timvp@google.com
-tobine@google.com
-vhau@google.com
zzyiwei@google.com
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 693e48e..48a68af 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -72,15 +72,13 @@
"libarect",
],
header_libs: [
+ "bionic_libc_platform_headers",
"gl_headers",
"libsystem_headers",
"libhardware_headers",
"libnativebase_headers",
],
export_header_lib_headers: ["gl_headers"],
-
- // we need to access the private Bionic header <bionic_tls.h>
- include_dirs: ["bionic/libc/private"],
}
//##############################################################################
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 63a0e14..86fec21 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -46,7 +46,7 @@
#define MAX_NUMBER_OF_GL_EXTENSIONS 256
-#include <bionic_tls.h> /* special private C library header */
+#include <bionic/tls.h> /* special private C library header */
// ----------------------------------------------------------------------------
namespace android {
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index 19c8b37..8bfe517 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -28,9 +28,11 @@
],
include_dirs: [
- "bionic/libc/private",
"frameworks/native/opengl/libs",
"frameworks/native/opengl/libs/EGL",
],
+ header_libs: [
+ "bionic_libc_platform_headers",
+ ],
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index bdee6fe..f6b5935 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -30,7 +30,6 @@
srcs: [
"InputClassifier.cpp",
"InputClassifierConverter.cpp",
- "InputDispatcher.cpp",
"InputManager.cpp",
],
@@ -45,11 +44,16 @@
"libhidlbase",
"libinput",
"liblog",
+ "libstatslog",
"libutils",
"libui",
"server_configurable_flags",
],
+ static_libs: [
+ "libinputdispatcher",
+ ],
+
cflags: [
// TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
@@ -60,43 +64,16 @@
"include",
],
+ export_static_lib_headers: [
+ "libinputdispatcher",
+ ],
}
cc_library_headers {
name: "libinputflinger_headers",
+ header_libs: ["libinputreporter_headers"],
export_include_dirs: ["include"],
-}
-
-cc_library_shared {
- name: "libinputreader",
- defaults: ["inputflinger_defaults"],
-
- srcs: [
- "EventHub.cpp",
- "InputReader.cpp",
- "InputReaderFactory.cpp",
- "TouchVideoDevice.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "libinputflinger_base",
- "libcrypto",
- "libcutils",
- "libinput",
- "liblog",
- "libui",
- "libutils",
- "libhardware_legacy",
- ],
-
- header_libs: [
- "libinputflinger_headers",
- ],
-
- export_header_lib_headers: [
- "libinputflinger_headers",
- ],
+ export_header_lib_headers: ["libinputreporter_headers"],
}
cc_library_shared {
@@ -124,28 +101,6 @@
],
}
-cc_library_shared {
- name: "libinputreporter",
- defaults: ["inputflinger_defaults"],
-
- srcs: [
- "InputReporter.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libutils",
- ],
-
- header_libs: [
- "libinputflinger_headers",
- ],
-
- export_header_lib_headers: [
- "libinputflinger_headers",
- ],
-}
-
subdirs = [
"host",
"tests",
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
deleted file mode 100644
index e35ba27..0000000
--- a/services/inputflinger/InputDispatcher.h
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*
- * Copyright (C) 2010 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 _UI_INPUT_DISPATCHER_H
-#define _UI_INPUT_DISPATCHER_H
-
-#include <condition_variable>
-#include <input/Input.h>
-#include <input/InputApplication.h>
-#include <input/InputTransport.h>
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-#include <optional>
-#include <ui/Region.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-#include <utils/BitSet.h>
-#include <cutils/atomic.h>
-#include <unordered_map>
-
-#include <limits.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <deque>
-#include <unordered_map>
-
-#include "InputListener.h"
-#include "InputReporterInterface.h"
-
-namespace android {
-
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
- /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
- INPUT_EVENT_INJECTION_PENDING = -1,
-
- /* Injection succeeded. */
- INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
- /* Injection failed because the injector did not have permission to inject
- * into the application with input focus. */
- INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
- /* Injection failed because there were no available input targets. */
- INPUT_EVENT_INJECTION_FAILED = 2,
-
- /* Injection failed due to a timeout. */
- INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
- /* Injection is asynchronous and is assumed always to be successful. */
- INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
- /* Waits for previous events to be dispatched so that the input dispatcher can determine
- * whether input event injection willbe permitted based on the current input focus.
- * Does not wait for the input event to finish processing. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
- /* Waits for the input event to be completely processed. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
-
-
-/*
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- */
-struct InputTarget {
- enum {
- /* This flag indicates that the event is being delivered to a foreground application. */
- FLAG_FOREGROUND = 1 << 0,
-
- /* This flag indicates that the MotionEvent falls within the area of the target
- * obscured by another visible window above it. The motion event should be
- * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
- FLAG_WINDOW_IS_OBSCURED = 1 << 1,
-
- /* This flag indicates that a motion event is being split across multiple windows. */
- FLAG_SPLIT = 1 << 2,
-
- /* This flag indicates that the pointer coordinates dispatched to the application
- * will be zeroed out to avoid revealing information to an application. This is
- * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
- * the same UID from watching all touches. */
- FLAG_ZERO_COORDS = 1 << 3,
-
- /* This flag indicates that the event should be sent as is.
- * Should always be set unless the event is to be transmuted. */
- FLAG_DISPATCH_AS_IS = 1 << 8,
-
- /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
- * of the area of this target and so should instead be delivered as an
- * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
- FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
-
- /* This flag indicates that a hover sequence is starting in the given window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
-
- /* This flag indicates that a hover event happened outside of a window which handled
- * previous hover events, signifying the end of the current hover sequence for that
- * window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
-
- /* This flag indicates that the event should be canceled.
- * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
- * outside of a window. */
- FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
-
- /* This flag indicates that the event should be dispatched as an initial down.
- * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
- * into a new window. */
- FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
-
- /* Mask for all dispatch modes. */
- FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
- | FLAG_DISPATCH_AS_OUTSIDE
- | FLAG_DISPATCH_AS_HOVER_ENTER
- | FLAG_DISPATCH_AS_HOVER_EXIT
- | FLAG_DISPATCH_AS_SLIPPERY_EXIT
- | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
-
- /* This flag indicates that the target of a MotionEvent is partly or wholly
- * obscured by another visible window above it. The motion event should be
- * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
- FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
-
- };
-
- // The input channel to be targeted.
- sp<InputChannel> inputChannel;
-
- // Flags for the input target.
- int32_t flags;
-
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset, yOffset;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float globalScaleFactor;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
-
- // The subset of pointer ids to include in motion events dispatched to this input target
- // if FLAG_SPLIT is set.
- BitSet32 pointerIds;
-};
-
-
-/*
- * Input dispatcher configuration.
- *
- * Specifies various options that modify the behavior of the input dispatcher.
- * The values provided here are merely defaults. The actual values will come from ViewConfiguration
- * and are passed into the dispatcher during initialization.
- */
-struct InputDispatcherConfiguration {
- // The key repeat initial timeout.
- nsecs_t keyRepeatTimeout;
-
- // The key repeat inter-key delay.
- nsecs_t keyRepeatDelay;
-
- InputDispatcherConfiguration() :
- keyRepeatTimeout(500 * 1000000LL),
- keyRepeatDelay(50 * 1000000LL) { }
-};
-
-
-/*
- * Input dispatcher policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI. This interface is also mocked in the unit tests.
- */
-class InputDispatcherPolicyInterface : public virtual RefBase {
-protected:
- InputDispatcherPolicyInterface() { }
- virtual ~InputDispatcherPolicyInterface() { }
-
-public:
- /* Notifies the system that a configuration change has occurred. */
- virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-
- /* Notifies the system that an application is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token,
- const std::string& reason) = 0;
-
- /* Notifies the system that an input channel is unrecoverably broken. */
- virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
-
- /* Gets the input dispatcher configuration. */
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
-
- /* Filters an input event.
- * Return true to dispatch the event unmodified, false to consume the event.
- * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
- * to injectInputEvent.
- */
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
-
- /* Intercepts a key event immediately before queueing it.
- * The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing such as updating policy flags.
- *
- * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
- * should be dispatched to applications.
- */
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
-
- /* Intercepts a touch, trackball or other motion event before queueing it.
- * The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing such as updating policy flags.
- *
- * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
- * should be dispatched to applications.
- */
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) = 0;
-
- /* Allows the policy a chance to intercept a key before dispatching. */
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
-
- /* Allows the policy a chance to perform default processing for an unhandled key.
- * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
-
- /* Notifies the policy about switch events.
- */
- virtual void notifySwitch(nsecs_t when,
- uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0;
-
- /* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
-
- /* Checks whether a given application pid/uid has permission to inject input events
- * into other applications.
- *
- * This method is special in that its implementation promises to be non-reentrant and
- * is safe to call while holding other locks. (Most other methods make no such guarantees!)
- */
- virtual bool checkInjectEventsPermissionNonReentrant(
- int32_t injectorPid, int32_t injectorUid) = 0;
-
- /* Notifies the policy that a pointer down event has occurred outside the current focused
- * window.
- *
- * The touchedToken passed as an argument is the window that received the input event.
- */
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
-};
-
-
-/* Notifies the system about input events generated by the input reader.
- * The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
-protected:
- InputDispatcherInterface() { }
- virtual ~InputDispatcherInterface() { }
-
-public:
- /* Dumps the state of the input dispatcher.
- *
- * This method may be called on any thread (usually by the input manager). */
- virtual void dump(std::string& dump) = 0;
-
- /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
- virtual void monitor() = 0;
-
- /* Runs a single iteration of the dispatch loop.
- * Nominally processes one queued event, a timeout, or a response from an input consumer.
- *
- * This method should only be called on the input dispatcher thread.
- */
- virtual void dispatchOnce() = 0;
-
- /* Injects an input event and optionally waits for sync.
- * The synchronization mode determines whether the method blocks while waiting for
- * input injection to proceed.
- * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) = 0;
-
- /* Sets the list of input windows.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
- int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
-
- /* Sets the focused application on the given display.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
-
- /* Sets the focused display.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setFocusedDisplay(int32_t displayId) = 0;
-
- /* Sets the input dispatching mode.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
-
- /* Sets whether input event filtering is enabled.
- * When enabled, incoming input events are sent to the policy's filterInputEvent
- * method instead of being dispatched. The filter is expected to use
- * injectInputEvent to inject the events it would like to have dispatched.
- * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
- */
- virtual void setInputFilterEnabled(bool enabled) = 0;
-
- /* Transfers touch focus from one window to another window.
- *
- * Returns true on success. False if the window did not actually have touch focus.
- */
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
- /* Registers input channels that may be used as targets for input events.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual status_t registerInputChannel(
- const sp<InputChannel>& inputChannel, int32_t displayId) = 0;
-
- /* Registers input channels to be used to monitor input events.
- *
- * Each monitor must target a specific display and will only receive input events sent to that
- * display. If the monitor is a gesture monitor, it will only receive pointer events on the
- * targeted display.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual status_t registerInputMonitor(
- const sp<InputChannel>& inputChannel, int32_t displayId, bool gestureMonitor) = 0;
-
- /* Unregister input channels that will no longer receive input events.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
- /* Allows an input monitor steal the current pointer stream away from normal input windows.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
-
-};
-
-/* Dispatches events to input targets. Some functions of the input dispatcher, such as
- * identifying input targets, are controlled by a separate policy object.
- *
- * IMPORTANT INVARIANT:
- * Because the policy can potentially block or cause re-entrance into the input dispatcher,
- * the input dispatcher never calls into the policy while holding its internal locks.
- * The implementation is also carefully designed to recover from scenarios such as an
- * input channel becoming unregistered while identifying input targets or processing timeouts.
- *
- * Methods marked 'Locked' must be called with the lock acquired.
- *
- * Methods marked 'LockedInterruptible' must be called with the lock acquired but
- * may during the course of their execution release the lock, call into the policy, and
- * then reacquire the lock. The caller is responsible for recovering gracefully.
- *
- * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
- */
-class InputDispatcher : public InputDispatcherInterface {
-protected:
- virtual ~InputDispatcher();
-
-public:
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
-
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
-
- virtual void dispatchOnce() override;
-
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
- virtual void notifyKey(const NotifyKeyArgs* args) override;
- virtual void notifyMotion(const NotifyMotionArgs* args) override;
- virtual void notifySwitch(const NotifySwitchArgs* args) override;
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-
- virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) override;
-
- virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
- int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
- virtual void setFocusedApplication(int32_t displayId,
- const sp<InputApplicationHandle>& inputApplicationHandle) override;
- virtual void setFocusedDisplay(int32_t displayId) override;
- virtual void setInputDispatchMode(bool enabled, bool frozen) override;
- virtual void setInputFilterEnabled(bool enabled) override;
-
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken)
- override;
-
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- int32_t displayId) override;
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
- virtual status_t pilferPointers(const sp<IBinder>& token) override;
-
-private:
-
- struct InjectionState {
- mutable int32_t refCount;
-
- int32_t injectorPid;
- int32_t injectorUid;
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
- bool injectionIsAsync; // set to true if injection is not waiting for the result
- int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
-
- InjectionState(int32_t injectorPid, int32_t injectorUid);
- void release();
-
- private:
- ~InjectionState();
- };
-
- struct EventEntry {
- enum {
- TYPE_CONFIGURATION_CHANGED,
- TYPE_DEVICE_RESET,
- TYPE_KEY,
- TYPE_MOTION
- };
-
- uint32_t sequenceNum;
- mutable int32_t refCount;
- int32_t type;
- nsecs_t eventTime;
- uint32_t policyFlags;
- InjectionState* injectionState;
-
- bool dispatchInProgress; // initially false, set to true while dispatching
-
- inline bool isInjected() const { return injectionState != nullptr; }
-
- void release();
-
- virtual void appendDescription(std::string& msg) const = 0;
-
- protected:
- EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
- virtual ~EventEntry();
- void releaseInjectionState();
- };
-
- struct ConfigurationChangedEntry : EventEntry {
- explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
- virtual void appendDescription(std::string& msg) const;
-
- protected:
- virtual ~ConfigurationChangedEntry();
- };
-
- struct DeviceResetEntry : EventEntry {
- int32_t deviceId;
-
- DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
- virtual void appendDescription(std::string& msg) const;
-
- protected:
- virtual ~DeviceResetEntry();
- };
-
- struct KeyEntry : EventEntry {
- int32_t deviceId;
- 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
-
- enum InterceptKeyResult {
- INTERCEPT_KEY_RESULT_UNKNOWN,
- INTERCEPT_KEY_RESULT_SKIP,
- INTERCEPT_KEY_RESULT_CONTINUE,
- INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
- };
- InterceptKeyResult interceptKeyResult; // set based on the interception result
- nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
-
- KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime);
- virtual void appendDescription(std::string& msg) const;
- void recycle();
-
- protected:
- virtual ~KeyEntry();
- };
-
- struct MotionEntry : EventEntry {
- nsecs_t eventTime;
- int32_t deviceId;
- uint32_t source;
- int32_t displayId;
- int32_t action;
- int32_t actionButton;
- int32_t flags;
- int32_t metaState;
- int32_t buttonState;
- MotionClassification classification;
- int32_t edgeFlags;
- float xPrecision;
- float yPrecision;
- float xCursorPosition;
- float yCursorPosition;
- nsecs_t downTime;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
-
- MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, int32_t edgeFlags, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xOffset, float yOffset);
- virtual void appendDescription(std::string& msg) const;
-
- protected:
- virtual ~MotionEntry();
- };
-
- // Tracks the progress of dispatching a particular event to a particular connection.
- struct DispatchEntry {
- const uint32_t seq; // unique sequence number, never 0
-
- EventEntry* eventEntry; // the event to dispatch
- int32_t targetFlags;
- float xOffset;
- float yOffset;
- float globalScaleFactor;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
- nsecs_t deliveryTime; // time when the event was actually delivered
-
- // Set to the resolved action and flags when the event is enqueued.
- int32_t resolvedAction;
- int32_t resolvedFlags;
-
- DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset,
- float globalScaleFactor, float windowXScale, float windowYScale);
- ~DispatchEntry();
-
- inline bool hasForegroundTarget() const {
- return targetFlags & InputTarget::FLAG_FOREGROUND;
- }
-
- inline bool isSplit() const {
- return targetFlags & InputTarget::FLAG_SPLIT;
- }
-
- private:
- static volatile int32_t sNextSeqAtomic;
-
- static uint32_t nextSeq();
- };
-
- // 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
- // a kind of continuation used to postpone sensitive policy interactions to a point
- // in the dispatch loop where it is safe to release the lock (generally after finishing
- // the critical parts of the dispatch cycle).
- //
- // The special thing about commands is that they can voluntarily release and reacquire
- // the dispatcher lock at will. Initially when the command starts running, the
- // dispatcher lock is held. However, if the command needs to call into the policy to
- // do some work, it can release the lock, do the work, then reacquire the lock again
- // before returning.
- //
- // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
- // never calls into the policy while holding its lock.
- //
- // Commands are implicitly 'LockedInterruptible'.
- struct CommandEntry;
- typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
-
- class Connection;
- struct CommandEntry {
- explicit CommandEntry(Command command);
- ~CommandEntry();
-
- Command command;
-
- // parameters for the command (usage varies by command)
- sp<Connection> connection;
- nsecs_t eventTime;
- KeyEntry* keyEntry;
- sp<InputApplicationHandle> inputApplicationHandle;
- std::string reason;
- int32_t userActivityEventType;
- uint32_t seq;
- bool handled;
- sp<InputChannel> inputChannel;
- sp<IBinder> oldToken;
- sp<IBinder> newToken;
- };
-
- /* Specifies which events are to be canceled and why. */
- struct CancelationOptions {
- enum Mode {
- CANCEL_ALL_EVENTS = 0,
- CANCEL_POINTER_EVENTS = 1,
- CANCEL_NON_POINTER_EVENTS = 2,
- CANCEL_FALLBACK_EVENTS = 3,
- };
-
- // The criterion to use to determine which events should be canceled.
- Mode mode;
-
- // Descriptive reason for the cancelation.
- const char* reason;
-
- // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
- std::optional<int32_t> keyCode = std::nullopt;
-
- // The specific device id of events to cancel, or nullopt to cancel events from any device.
- std::optional<int32_t> deviceId = std::nullopt;
-
- // The specific display id of events to cancel, or nullopt to cancel events on any display.
- std::optional<int32_t> displayId = std::nullopt;
-
- CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { }
- };
-
- /* Tracks dispatched key and motion event state so that cancelation events can be
- * synthesized when events are dropped. */
- class InputState {
- public:
- InputState();
- ~InputState();
-
- // Returns true if there is no state to be canceled.
- bool isNeutral() const;
-
- // Returns true if the specified source is known to have received a hover enter
- // motion event.
- bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
-
- // 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);
-
- // 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);
-
- // Synthesizes cancelation events for the current state and resets the tracked state.
- void synthesizeCancelationEvents(nsecs_t currentTime,
- std::vector<EventEntry*>& outEvents, const CancelationOptions& options);
-
- // Clears the current state.
- void clear();
-
- // Copies pointer-related parts of the input state to another instance.
- void copyPointerStateTo(InputState& other) const;
-
- // Gets the fallback key associated with a keycode.
- // Returns -1 if none.
- // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
- int32_t getFallbackKey(int32_t originalKeyCode);
-
- // Sets the fallback key for a particular keycode.
- void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
-
- // Removes the fallback key for a particular keycode.
- void removeFallbackKey(int32_t originalKeyCode);
-
- inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const {
- return mFallbackKeys;
- }
-
- private:
- struct KeyMemento {
- int32_t deviceId;
- uint32_t source;
- int32_t displayId;
- int32_t keyCode;
- int32_t scanCode;
- int32_t metaState;
- int32_t flags;
- nsecs_t downTime;
- uint32_t policyFlags;
- };
-
- struct MotionMemento {
- int32_t deviceId;
- uint32_t source;
- int32_t displayId;
- int32_t flags;
- float xPrecision;
- float yPrecision;
- float xCursorPosition;
- float yCursorPosition;
- nsecs_t downTime;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- bool hovering;
- uint32_t policyFlags;
-
- void setPointers(const MotionEntry* entry);
- };
-
- std::vector<KeyMemento> mKeyMementos;
- std::vector<MotionMemento> mMotionMementos;
- KeyedVector<int32_t, int32_t> mFallbackKeys;
-
- ssize_t findKeyMemento(const KeyEntry* entry) const;
- ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
-
- void addKeyMemento(const KeyEntry* entry, int32_t flags);
- void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
-
- static bool shouldCancelKey(const KeyMemento& memento,
- const CancelationOptions& options);
- static bool shouldCancelMotion(const MotionMemento& memento,
- const CancelationOptions& options);
- };
-
- /* Manages the dispatch state associated with a single input channel. */
- class Connection : public RefBase {
- protected:
- virtual ~Connection();
-
- public:
- enum Status {
- // Everything is peachy.
- STATUS_NORMAL,
- // An unrecoverable communication error has occurred.
- STATUS_BROKEN,
- // The input channel has been unregistered.
- STATUS_ZOMBIE
- };
-
- Status status;
- sp<InputChannel> inputChannel; // never null
- bool monitor;
- InputPublisher inputPublisher;
- InputState inputState;
-
- // True if the socket is full and no further events can be published until
- // the application consumes some of the input.
- bool inputPublisherBlocked;
-
- // Queue of events that need to be published to the connection.
- std::deque<DispatchEntry*> outboundQueue;
-
- // Queue of events that have been published to the connection but that have not
- // yet received a "finished" response from the application.
- std::deque<DispatchEntry*> waitQueue;
-
- explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
-
- inline const std::string getInputChannelName() const { return inputChannel->getName(); }
-
- const std::string getWindowName() const;
- const char* getStatusLabel() const;
-
- std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
- };
-
- struct Monitor {
- sp<InputChannel> inputChannel; // never null
-
- explicit Monitor(const sp<InputChannel>& inputChannel);
- };
-
- enum DropReason {
- DROP_REASON_NOT_DROPPED = 0,
- DROP_REASON_POLICY = 1,
- DROP_REASON_APP_SWITCH = 2,
- DROP_REASON_DISABLED = 3,
- DROP_REASON_BLOCKED = 4,
- DROP_REASON_STALE = 5,
- };
-
- sp<InputDispatcherPolicyInterface> mPolicy;
- InputDispatcherConfiguration mConfig;
-
- std::mutex mLock;
-
- std::condition_variable mDispatcherIsAlive;
-
- sp<Looper> mLooper;
-
- EventEntry* mPendingEvent GUARDED_BY(mLock);
- std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
- std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
- std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
-
- DropReason mLastDropReason GUARDED_BY(mLock);
-
- void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
-
- // Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
-
- // Cleans up input state when dropping an inbound event.
- void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
-
- // Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
-
- // App switch latency optimization.
- bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
- nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
-
- bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
- bool isAppSwitchPendingLocked() REQUIRES(mLock);
- void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
-
- // Stale event latency optimization.
- static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
-
- // Blocked event latency optimization. Drops old events when the user intends
- // to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
-
- sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
- bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock);
-
- // All registered connections mapped by channel file descriptor.
- std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
-
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& b) const {
- return std::hash<IBinder *>{}(b.get());
- }
- };
- std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
- GUARDED_BY(mLock);
-
- // Finds the display ID of the gesture monitor identified by the provided token.
- std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
- REQUIRES(mLock);
-
- sp<Connection> getConnectionLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
-
- // Input channels that will receive a copy of all input events sent to the provided display.
- std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay
- GUARDED_BY(mLock);
-
- // Input channels that will receive pointer events that start within the corresponding display.
- // These are a bit special when compared to global monitors since they'll cause gesture streams
- // to continue even when there isn't a touched window,and have the ability to steal the rest of
- // the pointer stream in order to claim it for a system gesture.
- std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay
- GUARDED_BY(mLock);
-
-
- // Event injection and synchronization.
- std::condition_variable mInjectionResultAvailable;
- bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResult(EventEntry* entry, int32_t injectionResult);
-
- std::condition_variable mInjectionSyncFinished;
- void incrementPendingForegroundDispatches(EventEntry* entry);
- void decrementPendingForegroundDispatches(EventEntry* entry);
-
- // Key repeat tracking.
- struct KeyRepeatState {
- KeyEntry* lastKeyEntry; // or null if no repeat
- nsecs_t nextRepeatTime;
- } mKeyRepeatState GUARDED_BY(mLock);
-
- void resetKeyRepeatLocked() REQUIRES(mLock);
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
-
- // Key replacement tracking
- struct KeyReplacement {
- int32_t keyCode;
- int32_t deviceId;
- bool operator==(const KeyReplacement& rhs) const {
- return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
- }
- bool operator<(const KeyReplacement& rhs) const {
- return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
- }
- };
- // Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
- // Process certain Meta + Key combinations
- void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
- int32_t& keyCode, int32_t& metaState);
-
- // Deferred command processing.
- bool haveCommandsLocked() const REQUIRES(mLock);
- bool runCommandsLockedInterruptible() REQUIRES(mLock);
- void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
-
- // Input filter processing.
- bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
- bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
-
- // Inbound event processing.
- void drainInboundQueueLocked() REQUIRES(mLock);
- void releasePendingEventLocked() REQUIRES(mLock);
- void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
-
- // Dispatch state.
- bool mDispatchEnabled GUARDED_BY(mLock);
- bool mDispatchFrozen GUARDED_BY(mLock);
- bool mInputFilterEnabled GUARDED_BY(mLock);
-
- std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
- GUARDED_BY(mLock);
- // Get window handles by display, return an empty vector if not found.
- std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
- REQUIRES(mLock);
- sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
- REQUIRES(mLock);
- sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
-
- /*
- * Validate and update InputWindowHandles for a given display.
- */
- void updateWindowHandlesForDisplayLocked(
- const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
- REQUIRES(mLock);
-
- // Focus tracking for keys, trackball, etc.
- std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
- GUARDED_BY(mLock);
-
- // Focus tracking for touch.
- struct TouchedWindow {
- sp<InputWindowHandle> windowHandle;
- int32_t targetFlags;
- BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
- };
-
- // For tracking the offsets we need to apply when adding gesture monitor targets.
- struct TouchedMonitor {
- Monitor monitor;
- float xOffset = 0.f;
- float yOffset = 0.f;
-
- explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
- };
-
- struct TouchState {
- bool down;
- bool split;
- int32_t deviceId; // id of the device that is currently down, others are rejected
- uint32_t source; // source of the device that is current down, others are rejected
- int32_t displayId; // id to the display that currently has a touch, others are rejected
- std::vector<TouchedWindow> windows;
-
- // This collects the portal windows that the touch has gone through. Each portal window
- // targets a display (embedded display for most cases). With this info, we can add the
- // monitoring channels of the displays touched.
- std::vector<sp<InputWindowHandle>> portalWindows;
-
- std::vector<TouchedMonitor> gestureMonitors;
-
- TouchState();
- ~TouchState();
- void reset();
- void copyFrom(const TouchState& other);
- void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds);
- void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
- void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
- void removeWindow(const sp<InputWindowHandle>& windowHandle);
- void removeWindowByToken(const sp<IBinder>& token);
- void filterNonAsIsTouchWindows();
- void filterNonMonitors();
- sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
- bool isSlippery() const;
- };
-
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
- TouchState mTempTouchState GUARDED_BY(mLock);
-
- // Focused applications.
- std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
- GUARDED_BY(mLock);
-
- // Top focused display.
- int32_t mFocusedDisplayId GUARDED_BY(mLock);
-
- // Dispatcher state at time of last ANR.
- std::string mLastANRState GUARDED_BY(mLock);
-
- // Dispatch inbound events.
- bool dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock);
- bool dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
- bool dispatchKeyLocked(
- nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- bool dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
- const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-
- void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
- void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
-
- // Keeping track of ANR timeouts.
- enum InputTargetWaitCause {
- INPUT_TARGET_WAIT_CAUSE_NONE,
- INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
- INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
- };
-
- InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
- bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
- sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
-
- // Contains the last window which received a hover event.
- sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
-
- // Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock);
-
- void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel) REQUIRES(mLock);
- nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
- void resetANRTimeoutsLocked() REQUIRES(mLock);
-
- int32_t getTargetDisplayId(const EventEntry* entry);
- int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
- std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
- std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) REQUIRES(mLock);
- std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId,
- const std::vector<sp<InputWindowHandle>>& portalWindows) REQUIRES(mLock);
- void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, float yOffset = 0);
-
- void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
- REQUIRES(mLock);
- void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
- std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
- void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
- bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
- const InjectionState* injectionState);
- bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t x, int32_t y) const REQUIRES(mLock);
- bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
- std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle);
-
- std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
- const char* targetType) REQUIRES(mLock);
-
- // Manage the dispatch cycle for a single connection.
- // These methods are deliberately not Interruptible because doing all of the work
- // with the mutex held makes it easier to ensure that connection invariants are maintained.
- // If needed, the methods post commands to run later once the critical bits are done.
- void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
- REQUIRES(mLock);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
- REQUIRES(mLock);
- void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled) REQUIRES(mLock);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool notify) REQUIRES(mLock);
- void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
- void releaseDispatchEntry(DispatchEntry* dispatchEntry);
- static int handleReceiveCallback(int fd, int events, void* data);
- // The action sent should only be of type AMOTION_EVENT_*
- void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
- const sp<IBinder>& newToken) REQUIRES(mLock);
-
- void synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options) REQUIRES(mLock);
- void synthesizeCancelationEventsForMonitorsLocked(
- const CancelationOptions& options) REQUIRES(mLock);
- void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options,
- std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options) REQUIRES(mLock);
- void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- const CancelationOptions& options) REQUIRES(mLock);
-
- // Splitting motion events across windows.
- MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
-
- // Reset and drop everything the dispatcher is doing.
- void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
-
- // Dump state.
- void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
- void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
- void logDispatchStateLocked() REQUIRES(mLock);
-
- // Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
- std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay)
- REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
- REQUIRES(mLock);
-
- // Interesting events that we might like to log or tell the framework about.
- void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled)
- REQUIRES(mLock);
- void onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock);
- void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onANRLocked(
- nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
-
- // Outbound policy interactions.
- void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
- void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
- void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
- bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
- void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
- void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
-
- // Statistics gathering.
- void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
- int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
- void traceInboundQueueLengthLocked() REQUIRES(mLock);
- void traceOutboundQueueLength(const sp<Connection>& connection);
- void traceWaitQueueLength(const sp<Connection>& connection);
-
- sp<InputReporterInterface> mReporter;
-};
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
- ~InputDispatcherThread();
-
-private:
- virtual bool threadLoop();
-
- sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3996cca..7d30672 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -19,6 +19,8 @@
//#define LOG_NDEBUG 0
#include "InputManager.h"
+#include "InputDispatcherFactory.h"
+#include "InputDispatcherThread.h"
#include "InputReaderFactory.h"
#include <binder/IPCThreadState.h>
@@ -33,7 +35,7 @@
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
- mDispatcher = new InputDispatcher(dispatcherPolicy);
+ mDispatcher = createInputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
mReader = createInputReader(readerPolicy, mClassifier);
initialize();
@@ -117,10 +119,6 @@
}
}
-void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- mDispatcher->transferTouchFocus(fromToken, toToken);
-}
-
// Used by tests only.
void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index e568df5..40f66d8 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -21,15 +21,14 @@
* Native input manager.
*/
-#include "EventHub.h"
-#include "InputReaderBase.h"
#include "InputClassifier.h"
-#include "InputDispatcher.h"
-#include "InputReader.h"
+#include "InputReaderBase.h"
+#include <InputDispatcherInterface.h>
+#include <InputDispatcherPolicyInterface.h>
+#include <input/ISetInputWindowsListener.h>
#include <input/Input.h>
#include <input/InputTransport.h>
-#include <input/ISetInputWindowsListener.h>
#include <input/IInputFlinger.h>
#include <utils/Errors.h>
@@ -39,6 +38,7 @@
namespace android {
class InputChannel;
+class InputDispatcherThread;
/*
* The input manager is the core of the system event processing.
@@ -96,7 +96,6 @@
virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener);
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
deleted file mode 100644
index d59f274..0000000
--- a/services/inputflinger/InputReader.cpp
+++ /dev/null
@@ -1,7575 +0,0 @@
-/*
- * Copyright (C) 2010 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 "InputReader"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
-
-// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
-
-// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
-
-// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
-
-// Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
-
-// Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
-
-// Log debug messages about fusing stylus data.
-#define DEBUG_STYLUS_FUSION 0
-
-#include "InputReader.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <math.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include <android-base/stringprintf.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
-#define INDENT " "
-#define INDENT2 " "
-#define INDENT3 " "
-#define INDENT4 " "
-#define INDENT5 " "
-
-using android::base::StringPrintf;
-
-namespace android {
-
-// --- Constants ---
-
-// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static constexpr size_t MAX_SLOTS = 32;
-
-// Maximum amount of latency to add to touch events while waiting for data from an
-// external stylus.
-static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
-
-// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
-
-// Artificial latency on synthetic events created from stylus data without corresponding touch
-// data.
-static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
-
-// --- Static Functions ---
-
-template<typename T>
-inline static T abs(const T& value) {
- return value < 0 ? - value : value;
-}
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
-
-template<typename T>
-inline static void swap(T& a, T& b) {
- T temp = a;
- a = b;
- b = temp;
-}
-
-inline static float avg(float x, float y) {
- return (x + y) / 2;
-}
-
-inline static float distance(float x1, float y1, float x2, float y2) {
- return hypotf(x1 - x2, y1 - y2);
-}
-
-inline static int32_t signExtendNybble(int32_t value) {
- return value >= 8 ? value - 16 : value;
-}
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
- const int32_t map[][4], size_t mapSize) {
- if (orientation != DISPLAY_ORIENTATION_0) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][orientation];
- }
- }
- }
- return value;
-}
-
-static const int32_t keyCodeRotationMap[][4] = {
- // key codes enumerated counter-clockwise with the original (unrotated) key first
- // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
- { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
- { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
- { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
- { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
- { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
- AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT },
- { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
- AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN },
- { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
- AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT },
- { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
- AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP },
-};
-static const size_t keyCodeRotationMapSize =
- sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateStemKey(int32_t value, int32_t orientation,
- const int32_t map[][2], size_t mapSize) {
- if (orientation == DISPLAY_ORIENTATION_180) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][1];
- }
- }
- }
- return value;
-}
-
-// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
-static int32_t stemKeyRotationMap[][2] = {
- // key codes enumerated with the original (unrotated) key first
- // no rotation, 180 degree rotation
- { AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY },
- { AKEYCODE_STEM_1, AKEYCODE_STEM_1 },
- { AKEYCODE_STEM_2, AKEYCODE_STEM_2 },
- { AKEYCODE_STEM_3, AKEYCODE_STEM_3 },
-};
-static const size_t stemKeyRotationMapSize =
- sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
- keyCode = rotateStemKey(keyCode, orientation,
- stemKeyRotationMap, stemKeyRotationMapSize);
- return rotateValueUsingRotationMap(keyCode, orientation,
- keyCodeRotationMap, keyCodeRotationMapSize);
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case DISPLAY_ORIENTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case DISPLAY_ORIENTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
- }
-}
-
-static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
- return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
-}
-
-// Returns true if the pointer should be reported as being down given the specified
-// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY
- | AMOTION_EVENT_BUTTON_TERTIARY);
-}
-
-static float calculateCommonVector(float a, float b) {
- if (a > 0 && b > 0) {
- return a < b ? a : b;
- } else if (a < 0 && b < 0) {
- return a > b ? a : b;
- } else {
- return 0;
- }
-}
-
-static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
- int32_t buttonState, int32_t keyCode) {
- if (
- (action == AKEY_EVENT_ACTION_DOWN
- && !(lastButtonState & buttonState)
- && (currentButtonState & buttonState))
- || (action == AKEY_EVENT_ACTION_UP
- && (lastButtonState & buttonState)
- && !(currentButtonState & buttonState))) {
- NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId,
- policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
- context->getListener()->notifyKey(&args);
- }
-}
-
-static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
-}
-
-
-// --- InputReader ---
-
-InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener)
- : mContext(this),
- mEventHub(eventHub),
- mPolicy(policy),
- mNextSequenceNum(1),
- mGlobalMetaState(0),
- mGeneration(1),
- mDisableVirtualKeysTimeout(LLONG_MIN),
- mNextTimeout(LLONG_MAX),
- mConfigurationChangesToRefresh(0) {
- mQueuedListener = new QueuedInputListener(listener);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- refreshConfigurationLocked(0);
- updateGlobalMetaStateLocked();
- } // release lock
-}
-
-InputReader::~InputReader() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
- }
-}
-
-void InputReader::loopOnce() {
- int32_t oldGeneration;
- int32_t timeoutMillis;
- bool inputDevicesChanged = false;
- std::vector<InputDeviceInfo> inputDevices;
- { // acquire lock
- AutoMutex _l(mLock);
-
- oldGeneration = mGeneration;
- timeoutMillis = -1;
-
- uint32_t changes = mConfigurationChangesToRefresh;
- if (changes) {
- mConfigurationChangesToRefresh = 0;
- timeoutMillis = 0;
- refreshConfigurationLocked(changes);
- } else if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
- }
- } // release lock
-
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
-
- { // acquire lock
- AutoMutex _l(mLock);
- mReaderIsAliveCondition.broadcast();
-
- if (count) {
- processEventsLocked(mEventBuffer, count);
- }
-
- if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
- mNextTimeout = LLONG_MAX;
- timeoutExpiredLocked(now);
- }
- }
-
- if (oldGeneration != mGeneration) {
- inputDevicesChanged = true;
- getInputDevicesLocked(inputDevices);
- }
- } // release lock
-
- // Send out a message that the describes the changed input devices.
- if (inputDevicesChanged) {
- mPolicy->notifyInputDevicesChanged(inputDevices);
- }
-
- // Flush queued events out to the listener.
- // This must happen outside of the lock because the listener could potentially call
- // back into the InputReader's methods, such as getScanCodeState, or become blocked
- // on another thread similarly waiting to acquire the InputReader lock thereby
- // resulting in a deadlock. This situation is actually quite plausible because the
- // listener is actually the input dispatcher, which calls into the window manager,
- // which occasionally calls into the input reader.
- mQueuedListener->flush();
-}
-
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
- for (const RawEvent* rawEvent = rawEvents; count;) {
- int32_t type = rawEvent->type;
- size_t batchSize = 1;
- if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
- int32_t deviceId = rawEvent->deviceId;
- while (batchSize < count) {
- if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
- || rawEvent[batchSize].deviceId != deviceId) {
- break;
- }
- batchSize += 1;
- }
-#if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-#endif
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
- } else {
- switch (rawEvent->type) {
- case EventHubInterface::DEVICE_ADDED:
- addDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::DEVICE_REMOVED:
- removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
- break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChangedLocked(rawEvent->when);
- break;
- default:
- ALOG_ASSERT(false); // can't happen
- break;
- }
- }
- count -= batchSize;
- rawEvent += batchSize;
- }
-}
-
-void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
- return;
- }
-
- InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
- uint32_t classes = mEventHub->getDeviceClasses(deviceId);
- int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
-
- InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
- device->configure(when, &mConfig, 0);
- device->reset(when);
-
- if (device->isIgnored()) {
- ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
- identifier.name.c_str());
- } else {
- ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
- identifier.name.c_str(), device->getSources());
- }
-
- mDevices.add(deviceId, device);
- bumpGenerationLocked();
-
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
- notifyExternalStylusPresenceChanged();
- }
-}
-
-void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
- InputDevice* device = nullptr;
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
- return;
- }
-
- device = mDevices.valueAt(deviceIndex);
- mDevices.removeItemsAt(deviceIndex, 1);
- bumpGenerationLocked();
-
- if (device->isIgnored()) {
- ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
- device->getId(), device->getName().c_str());
- } else {
- ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
- device->getId(), device->getName().c_str(), device->getSources());
- }
-
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
- notifyExternalStylusPresenceChanged();
- }
-
- device->reset(when);
- delete device;
-}
-
-InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes) {
- InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
- controllerNumber, identifier, classes);
-
- // External devices.
- if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
- device->setExternal(true);
- }
-
- // Devices with mics.
- if (classes & INPUT_DEVICE_CLASS_MIC) {
- device->setMic(true);
- }
-
- // Switch-like devices.
- if (classes & INPUT_DEVICE_CLASS_SWITCH) {
- device->addMapper(new SwitchInputMapper(device));
- }
-
- // Scroll wheel-like devices.
- if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
- device->addMapper(new RotaryEncoderInputMapper(device));
- }
-
- // Vibrator-like devices.
- if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
- device->addMapper(new VibratorInputMapper(device));
- }
-
- // Keyboard-like devices.
- uint32_t keyboardSource = 0;
- int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
- if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
- keyboardSource |= AINPUT_SOURCE_KEYBOARD;
- }
- if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
- keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
- }
- if (classes & INPUT_DEVICE_CLASS_DPAD) {
- keyboardSource |= AINPUT_SOURCE_DPAD;
- }
- if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- keyboardSource |= AINPUT_SOURCE_GAMEPAD;
- }
-
- if (keyboardSource != 0) {
- device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
- }
-
- // Cursor-like devices.
- if (classes & INPUT_DEVICE_CLASS_CURSOR) {
- device->addMapper(new CursorInputMapper(device));
- }
-
- // Touchscreens and touchpad devices.
- if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
- device->addMapper(new MultiTouchInputMapper(device));
- } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
- device->addMapper(new SingleTouchInputMapper(device));
- }
-
- // Joystick-like devices.
- if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
- device->addMapper(new JoystickInputMapper(device));
- }
-
- // External stylus-like devices.
- if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
- device->addMapper(new ExternalStylusInputMapper(device));
- }
-
- return device;
-}
-
-void InputReader::processEventsForDeviceLocked(int32_t deviceId,
- const RawEvent* rawEvents, size_t count) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Discarding event for unknown deviceId %d.", deviceId);
- return;
- }
-
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (device->isIgnored()) {
- //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
- return;
- }
-
- device->process(rawEvents, count);
-}
-
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (!device->isIgnored()) {
- device->timeoutExpired(when);
- }
- }
-}
-
-void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
- // Reset global meta state because it depends on the list of all configured devices.
- updateGlobalMetaStateLocked();
-
- // Enqueue configuration changed.
- NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
- mQueuedListener->notifyConfigurationChanged(&args);
-}
-
-void InputReader::refreshConfigurationLocked(uint32_t changes) {
- mPolicy->getReaderConfiguration(&mConfig);
- mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
-
- if (changes) {
- ALOGI("Reconfiguring input devices, changes=%s",
- InputReaderConfiguration::changesToString(changes).c_str());
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
- if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
- mEventHub->requestReopenDevices();
- } else {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- device->configure(now, &mConfig, changes);
- }
- }
- }
-}
-
-void InputReader::updateGlobalMetaStateLocked() {
- mGlobalMetaState = 0;
-
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- mGlobalMetaState |= device->getMetaState();
- }
-}
-
-int32_t InputReader::getGlobalMetaStateLocked() {
- return mGlobalMetaState;
-}
-
-void InputReader::notifyExternalStylusPresenceChanged() {
- refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
-}
-
-void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- outDevices.push_back(info);
- }
- }
-}
-
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- device->updateExternalStylusState(state);
- }
-}
-
-void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
- mDisableVirtualKeysTimeout = time;
-}
-
-bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) {
- if (now < mDisableVirtualKeysTimeout) {
- ALOGI("Dropping virtual key from device %s because virtual keys are "
- "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d",
- device->getName().c_str(),
- (mDisableVirtualKeysTimeout - now) * 0.000001,
- keyCode, scanCode);
- return true;
- } else {
- return false;
- }
-}
-
-void InputReader::fadePointerLocked() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- device->fadePointer();
- }
-}
-
-void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
- if (when < mNextTimeout) {
- mNextTimeout = when;
- mEventHub->wake();
- }
-}
-
-int32_t InputReader::bumpGenerationLocked() {
- return ++mGeneration;
-}
-
-void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
- AutoMutex _l(mLock);
- getInputDevicesLocked(outInputDevices);
-}
-
-void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
- outInputDevices.clear();
-
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (!device->isIgnored()) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- outInputDevices.push_back(info);
- }
- }
-}
-
-int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
-}
-
-int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
-}
-
-int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
- AutoMutex _l(mLock);
-
- return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
-}
-
-int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
- GetStateFunc getStateFunc) {
- int32_t result = AKEY_STATE_UNKNOWN;
- if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result = (device->*getStateFunc)(sourceMask, code);
- }
- }
- } else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
- // value. Otherwise, return AKEY_STATE_UP as long as one device reports it.
- int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
- if (currentResult >= AKEY_STATE_DOWN) {
- return currentResult;
- } else if (currentResult == AKEY_STATE_UP) {
- result = currentResult;
- }
- }
- }
- }
- return result;
-}
-
-void InputReader::toggleCapsLockState(int32_t deviceId) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
- return;
- }
-
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (device->isIgnored()) {
- return;
- }
-
- device->updateMetaState(AKEYCODE_CAPS_LOCK);
-}
-
-bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
- AutoMutex _l(mLock);
-
- memset(outFlags, 0, numCodes);
- return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
-}
-
-bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
- if (deviceId >= 0) {
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result = device->markSupportedKeyCodes(sourceMask,
- numCodes, keyCodes, outFlags);
- }
- }
- } else {
- size_t numDevices = mDevices.size();
- for (size_t i = 0; i < numDevices; i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result |= device->markSupportedKeyCodes(sourceMask,
- numCodes, keyCodes, outFlags);
- }
- }
- }
- return result;
-}
-
-void InputReader::requestRefreshConfiguration(uint32_t changes) {
- AutoMutex _l(mLock);
-
- if (changes) {
- bool needWake = !mConfigurationChangesToRefresh;
- mConfigurationChangesToRefresh |= changes;
-
- if (needWake) {
- mEventHub->wake();
- }
- }
-}
-
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- device->vibrate(pattern, patternSize, repeat, token);
- }
-}
-
-void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- device->cancelVibrate(token);
- }
-}
-
-bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex >= 0) {
- InputDevice* device = mDevices.valueAt(deviceIndex);
- return device->isEnabled();
- }
- ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
- return false;
-}
-
-bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
- AutoMutex _l(mLock);
-
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
- return false;
- }
-
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (!device->isEnabled()) {
- ALOGW("Ignoring disabled device %s", device->getName().c_str());
- return false;
- }
-
- std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
- // No associated display. By default, can dispatch to all displays.
- if (!associatedDisplayId) {
- return true;
- }
-
- if (*associatedDisplayId == ADISPLAY_ID_NONE) {
- ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
- return true;
- }
-
- return *associatedDisplayId == displayId;
-}
-
-void InputReader::dump(std::string& dump) {
- AutoMutex _l(mLock);
-
- mEventHub->dump(dump);
- dump += "\n";
-
- dump += "Input Reader State:\n";
-
- for (size_t i = 0; i < mDevices.size(); i++) {
- mDevices.valueAt(i)->dump(dump);
- }
-
- dump += INDENT "Configuration:\n";
- dump += INDENT2 "ExcludedDeviceNames: [";
- for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
- if (i != 0) {
- dump += ", ";
- }
- dump += mConfig.excludedDeviceNames[i];
- }
- dump += "]\n";
- dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
- mConfig.virtualKeyQuietTime * 0.000001f);
-
- dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: "
- "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
- mConfig.pointerVelocityControlParameters.scale,
- mConfig.pointerVelocityControlParameters.lowThreshold,
- mConfig.pointerVelocityControlParameters.highThreshold,
- mConfig.pointerVelocityControlParameters.acceleration);
-
- dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: "
- "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
- mConfig.wheelVelocityControlParameters.scale,
- mConfig.wheelVelocityControlParameters.lowThreshold,
- mConfig.wheelVelocityControlParameters.highThreshold,
- mConfig.wheelVelocityControlParameters.acceleration);
-
- dump += StringPrintf(INDENT2 "PointerGesture:\n");
- dump += StringPrintf(INDENT3 "Enabled: %s\n",
- toString(mConfig.pointerGesturesEnabled));
- dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n",
- mConfig.pointerGestureQuietInterval * 0.000001f);
- dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
- mConfig.pointerGestureDragMinSwitchSpeed);
- dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n",
- mConfig.pointerGestureTapInterval * 0.000001f);
- dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n",
- mConfig.pointerGestureTapDragInterval * 0.000001f);
- dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n",
- mConfig.pointerGestureTapSlop);
- dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
- mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
- dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
- mConfig.pointerGestureMultitouchMinDistance);
- dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
- mConfig.pointerGestureSwipeMaxWidthRatio);
- dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n",
- mConfig.pointerGestureMovementSpeedRatio);
- dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n",
- mConfig.pointerGestureZoomSpeedRatio);
-
- dump += INDENT3 "Viewports:\n";
- mConfig.dump(dump);
-}
-
-void InputReader::monitor() {
- // Acquire and release the lock to ensure that the reader has not deadlocked.
- mLock.lock();
- mEventHub->wake();
- mReaderIsAliveCondition.wait(mLock);
- mLock.unlock();
-
- // Check the EventHub
- mEventHub->monitor();
-}
-
-
-// --- InputReader::ContextImpl ---
-
-InputReader::ContextImpl::ContextImpl(InputReader* reader) :
- mReader(reader) {
-}
-
-void InputReader::ContextImpl::updateGlobalMetaState() {
- // lock is already held by the input loop
- mReader->updateGlobalMetaStateLocked();
-}
-
-int32_t InputReader::ContextImpl::getGlobalMetaState() {
- // lock is already held by the input loop
- return mReader->getGlobalMetaStateLocked();
-}
-
-void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
- // lock is already held by the input loop
- mReader->disableVirtualKeysUntilLocked(time);
-}
-
-bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) {
- // lock is already held by the input loop
- return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
-}
-
-void InputReader::ContextImpl::fadePointer() {
- // lock is already held by the input loop
- mReader->fadePointerLocked();
-}
-
-void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
- // lock is already held by the input loop
- mReader->requestTimeoutAtTimeLocked(when);
-}
-
-int32_t InputReader::ContextImpl::bumpGeneration() {
- // lock is already held by the input loop
- return mReader->bumpGenerationLocked();
-}
-
-void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
- // lock is already held by whatever called refreshConfigurationLocked
- mReader->getExternalStylusDevicesLocked(outDevices);
-}
-
-void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
- mReader->dispatchExternalStylusState(state);
-}
-
-InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
- return mReader->mPolicy.get();
-}
-
-InputListenerInterface* InputReader::ContextImpl::getListener() {
- return mReader->mQueuedListener.get();
-}
-
-EventHubInterface* InputReader::ContextImpl::getEventHub() {
- return mReader->mEventHub.get();
-}
-
-uint32_t InputReader::ContextImpl::getNextSequenceNum() {
- return (mReader->mNextSequenceNum)++;
-}
-
-// --- InputDevice ---
-
-InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
- int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) :
- mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber),
- mIdentifier(identifier), mClasses(classes),
- mSources(0), mIsExternal(false), mHasMic(false), mDropUntilNextSync(false) {
-}
-
-InputDevice::~InputDevice() {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- delete mMappers[i];
- }
- mMappers.clear();
-}
-
-bool InputDevice::isEnabled() {
- return getEventHub()->isDeviceEnabled(mId);
-}
-
-void InputDevice::setEnabled(bool enabled, nsecs_t when) {
- if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
- ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
- "but the corresponding viewport is not found",
- getName().c_str(), *mAssociatedDisplayPort);
- enabled = false;
- }
-
- if (isEnabled() == enabled) {
- return;
- }
-
- if (enabled) {
- getEventHub()->enableDevice(mId);
- reset(when);
- } else {
- reset(when);
- getEventHub()->disableDevice(mId);
- }
- // Must change generation to flag this device as changed
- bumpGeneration();
-}
-
-void InputDevice::dump(std::string& dump) {
- InputDeviceInfo deviceInfo;
- getDeviceInfo(&deviceInfo);
-
- dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
- deviceInfo.getDisplayName().c_str());
- dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
- dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
- dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
- if (mAssociatedDisplayPort) {
- dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
- } else {
- dump += "<none>\n";
- }
- dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
- dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
- dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
-
- const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
- if (!ranges.empty()) {
- dump += INDENT2 "Motion Ranges:\n";
- for (size_t i = 0; i < ranges.size(); i++) {
- const InputDeviceInfo::MotionRange& range = ranges[i];
- const char* label = getAxisLabel(range.axis);
- char name[32];
- if (label) {
- strncpy(name, label, sizeof(name));
- name[sizeof(name) - 1] = '\0';
- } else {
- snprintf(name, sizeof(name), "%d", range.axis);
- }
- dump += StringPrintf(INDENT3 "%s: source=0x%08x, "
- "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
- name, range.source, range.min, range.max, range.flat, range.fuzz,
- range.resolution);
- }
- }
-
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->dump(dump);
- }
-}
-
-void InputDevice::addMapper(InputMapper* mapper) {
- mMappers.push_back(mapper);
-}
-
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
- mSources = 0;
-
- if (!isIgnored()) {
- if (!changes) { // first time only
- mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- sp<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
- bumpGeneration();
- }
- }
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
- if (mAlias != alias) {
- mAlias = alias;
- bumpGeneration();
- }
- }
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
- auto it = config->disabledDevices.find(mId);
- bool enabled = it == config->disabledDevices.end();
- setEnabled(enabled, when);
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- // In most situations, no port will be specified.
- mAssociatedDisplayPort = std::nullopt;
- mAssociatedViewport = std::nullopt;
- // Find the display port that corresponds to the current input port.
- const std::string& inputPort = mIdentifier.location;
- if (!inputPort.empty()) {
- const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
- const auto& displayPort = ports.find(inputPort);
- if (displayPort != ports.end()) {
- mAssociatedDisplayPort = std::make_optional(displayPort->second);
- }
- }
-
- // If the device was explicitly disabled by the user, it would be present in the
- // "disabledDevices" list. If it is associated with a specific display, and it was not
- // explicitly disabled, then enable/disable the device based on whether we can find the
- // corresponding viewport.
- bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
- if (mAssociatedDisplayPort) {
- mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
- if (!mAssociatedViewport) {
- ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
- "but the corresponding viewport is not found.",
- getName().c_str(), *mAssociatedDisplayPort);
- enabled = false;
- }
- }
-
- if (changes) {
- // For first-time configuration, only allow device to be disabled after mappers have
- // finished configuring. This is because we need to read some of the properties from
- // the device's open fd.
- setEnabled(enabled, when);
- }
- }
-
- for (InputMapper* mapper : mMappers) {
- mapper->configure(when, config, changes);
- mSources |= mapper->getSources();
- }
-
- // If a device is just plugged but it might be disabled, we need to update some info like
- // axis range of touch from each InputMapper first, then disable it.
- if (!changes) {
- setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
- }
- }
-}
-
-void InputDevice::reset(nsecs_t when) {
- for (InputMapper* mapper : mMappers) {
- mapper->reset(when);
- }
-
- mContext->updateGlobalMetaState();
-
- notifyReset(when);
-}
-
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
- // Process all of the events in order for each mapper.
- // We cannot simply ask each mapper to process them in bulk because mappers may
- // have side-effects that must be interleaved. For example, joystick movement events and
- // gamepad button presses are handled by different mappers but they should be dispatched
- // in the order received.
- for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
- rawEvent->when);
-#endif
-
- if (mDropUntilNextSync) {
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
- ALOGD("Recovered from input event buffer overrun.");
-#endif
- } else {
-#if DEBUG_RAW_EVENTS
- ALOGD("Dropped input event while waiting for next input sync.");
-#endif
- }
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
- ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
- mDropUntilNextSync = true;
- reset(rawEvent->when);
- } else {
- for (InputMapper* mapper : mMappers) {
- mapper->process(rawEvent);
- }
- }
- --count;
- }
-}
-
-void InputDevice::timeoutExpired(nsecs_t when) {
- for (InputMapper* mapper : mMappers) {
- mapper->timeoutExpired(when);
- }
-}
-
-void InputDevice::updateExternalStylusState(const StylusState& state) {
- for (InputMapper* mapper : mMappers) {
- mapper->updateExternalStylusState(state);
- }
-}
-
-void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
- outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias,
- mIsExternal, mHasMic);
- for (InputMapper* mapper : mMappers) {
- mapper->populateDeviceInfo(outDeviceInfo);
- }
-}
-
-int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
-}
-
-int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
-}
-
-int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
-}
-
-int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
- int32_t result = AKEY_STATE_UNKNOWN;
- for (InputMapper* mapper : mMappers) {
- if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
- // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
- // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
- int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
- if (currentResult >= AKEY_STATE_DOWN) {
- return currentResult;
- } else if (currentResult == AKEY_STATE_UP) {
- result = currentResult;
- }
- }
- }
- return result;
-}
-
-bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
- for (InputMapper* mapper : mMappers) {
- if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
- result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
- }
- }
- return result;
-}
-
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
- for (InputMapper* mapper : mMappers) {
- mapper->vibrate(pattern, patternSize, repeat, token);
- }
-}
-
-void InputDevice::cancelVibrate(int32_t token) {
- for (InputMapper* mapper : mMappers) {
- mapper->cancelVibrate(token);
- }
-}
-
-void InputDevice::cancelTouch(nsecs_t when) {
- for (InputMapper* mapper : mMappers) {
- mapper->cancelTouch(when);
- }
-}
-
-int32_t InputDevice::getMetaState() {
- int32_t result = 0;
- for (InputMapper* mapper : mMappers) {
- result |= mapper->getMetaState();
- }
- return result;
-}
-
-void InputDevice::updateMetaState(int32_t keyCode) {
- for (InputMapper* mapper : mMappers) {
- mapper->updateMetaState(keyCode);
- }
-}
-
-void InputDevice::fadePointer() {
- for (InputMapper* mapper : mMappers) {
- mapper->fadePointer();
- }
-}
-
-void InputDevice::bumpGeneration() {
- mGeneration = mContext->bumpGeneration();
-}
-
-void InputDevice::notifyReset(nsecs_t when) {
- NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
- mContext->getListener()->notifyDeviceReset(&args);
-}
-
-std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
- // Check if we had associated to the specific display.
- if (mAssociatedViewport) {
- return mAssociatedViewport->displayId;
- }
-
- // No associated display port, check if some InputMapper is associated.
- for (InputMapper* mapper : mMappers) {
- std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
- if (associatedDisplayId) {
- return associatedDisplayId;
- }
- }
-
- return std::nullopt;
-}
-
-// --- CursorButtonAccumulator ---
-
-CursorButtonAccumulator::CursorButtonAccumulator() {
- clearButtons();
-}
-
-void CursorButtonAccumulator::reset(InputDevice* device) {
- mBtnLeft = device->isKeyPressed(BTN_LEFT);
- mBtnRight = device->isKeyPressed(BTN_RIGHT);
- mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
- mBtnBack = device->isKeyPressed(BTN_BACK);
- mBtnSide = device->isKeyPressed(BTN_SIDE);
- mBtnForward = device->isKeyPressed(BTN_FORWARD);
- mBtnExtra = device->isKeyPressed(BTN_EXTRA);
- mBtnTask = device->isKeyPressed(BTN_TASK);
-}
-
-void CursorButtonAccumulator::clearButtons() {
- mBtnLeft = 0;
- mBtnRight = 0;
- mBtnMiddle = 0;
- mBtnBack = 0;
- mBtnSide = 0;
- mBtnForward = 0;
- mBtnExtra = 0;
- mBtnTask = 0;
-}
-
-void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
- case BTN_LEFT:
- mBtnLeft = rawEvent->value;
- break;
- case BTN_RIGHT:
- mBtnRight = rawEvent->value;
- break;
- case BTN_MIDDLE:
- mBtnMiddle = rawEvent->value;
- break;
- case BTN_BACK:
- mBtnBack = rawEvent->value;
- break;
- case BTN_SIDE:
- mBtnSide = rawEvent->value;
- break;
- case BTN_FORWARD:
- mBtnForward = rawEvent->value;
- break;
- case BTN_EXTRA:
- mBtnExtra = rawEvent->value;
- break;
- case BTN_TASK:
- mBtnTask = rawEvent->value;
- break;
- }
- }
-}
-
-uint32_t CursorButtonAccumulator::getButtonState() const {
- uint32_t result = 0;
- if (mBtnLeft) {
- result |= AMOTION_EVENT_BUTTON_PRIMARY;
- }
- if (mBtnRight) {
- result |= AMOTION_EVENT_BUTTON_SECONDARY;
- }
- if (mBtnMiddle) {
- result |= AMOTION_EVENT_BUTTON_TERTIARY;
- }
- if (mBtnBack || mBtnSide) {
- result |= AMOTION_EVENT_BUTTON_BACK;
- }
- if (mBtnForward || mBtnExtra) {
- result |= AMOTION_EVENT_BUTTON_FORWARD;
- }
- return result;
-}
-
-
-// --- CursorMotionAccumulator ---
-
-CursorMotionAccumulator::CursorMotionAccumulator() {
- clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::reset(InputDevice* device) {
- clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::clearRelativeAxes() {
- mRelX = 0;
- mRelY = 0;
-}
-
-void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
- case REL_X:
- mRelX = rawEvent->value;
- break;
- case REL_Y:
- mRelY = rawEvent->value;
- break;
- }
- }
-}
-
-void CursorMotionAccumulator::finishSync() {
- clearRelativeAxes();
-}
-
-
-// --- CursorScrollAccumulator ---
-
-CursorScrollAccumulator::CursorScrollAccumulator() :
- mHaveRelWheel(false), mHaveRelHWheel(false) {
- clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::configure(InputDevice* device) {
- mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
- mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
-}
-
-void CursorScrollAccumulator::reset(InputDevice* device) {
- clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::clearRelativeAxes() {
- mRelWheel = 0;
- mRelHWheel = 0;
-}
-
-void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
- case REL_WHEEL:
- mRelWheel = rawEvent->value;
- break;
- case REL_HWHEEL:
- mRelHWheel = rawEvent->value;
- break;
- }
- }
-}
-
-void CursorScrollAccumulator::finishSync() {
- clearRelativeAxes();
-}
-
-
-// --- TouchButtonAccumulator ---
-
-TouchButtonAccumulator::TouchButtonAccumulator() :
- mHaveBtnTouch(false), mHaveStylus(false) {
- clearButtons();
-}
-
-void TouchButtonAccumulator::configure(InputDevice* device) {
- mHaveBtnTouch = device->hasKey(BTN_TOUCH);
- mHaveStylus = device->hasKey(BTN_TOOL_PEN)
- || device->hasKey(BTN_TOOL_RUBBER)
- || device->hasKey(BTN_TOOL_BRUSH)
- || device->hasKey(BTN_TOOL_PENCIL)
- || device->hasKey(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDevice* device) {
- mBtnTouch = device->isKeyPressed(BTN_TOUCH);
- mBtnStylus = device->isKeyPressed(BTN_STYLUS);
- // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
- mBtnStylus2 =
- device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
- mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
- mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
- mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
- mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
- mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
- mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
- mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
- mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
- mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
- mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
- mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
- mBtnTouch = 0;
- mBtnStylus = 0;
- mBtnStylus2 = 0;
- mBtnToolFinger = 0;
- mBtnToolPen = 0;
- mBtnToolRubber = 0;
- mBtnToolBrush = 0;
- mBtnToolPencil = 0;
- mBtnToolAirbrush = 0;
- mBtnToolMouse = 0;
- mBtnToolLens = 0;
- mBtnToolDoubleTap = 0;
- mBtnToolTripleTap = 0;
- mBtnToolQuadTap = 0;
-}
-
-void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
- case BTN_TOUCH:
- mBtnTouch = rawEvent->value;
- break;
- case BTN_STYLUS:
- mBtnStylus = rawEvent->value;
- break;
- case BTN_STYLUS2:
- case BTN_0:// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
- mBtnStylus2 = rawEvent->value;
- break;
- case BTN_TOOL_FINGER:
- mBtnToolFinger = rawEvent->value;
- break;
- case BTN_TOOL_PEN:
- mBtnToolPen = rawEvent->value;
- break;
- case BTN_TOOL_RUBBER:
- mBtnToolRubber = rawEvent->value;
- break;
- case BTN_TOOL_BRUSH:
- mBtnToolBrush = rawEvent->value;
- break;
- case BTN_TOOL_PENCIL:
- mBtnToolPencil = rawEvent->value;
- break;
- case BTN_TOOL_AIRBRUSH:
- mBtnToolAirbrush = rawEvent->value;
- break;
- case BTN_TOOL_MOUSE:
- mBtnToolMouse = rawEvent->value;
- break;
- case BTN_TOOL_LENS:
- mBtnToolLens = rawEvent->value;
- break;
- case BTN_TOOL_DOUBLETAP:
- mBtnToolDoubleTap = rawEvent->value;
- break;
- case BTN_TOOL_TRIPLETAP:
- mBtnToolTripleTap = rawEvent->value;
- break;
- case BTN_TOOL_QUADTAP:
- mBtnToolQuadTap = rawEvent->value;
- break;
- }
- }
-}
-
-uint32_t TouchButtonAccumulator::getButtonState() const {
- uint32_t result = 0;
- if (mBtnStylus) {
- result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
- }
- if (mBtnStylus2) {
- result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY;
- }
- return result;
-}
-
-int32_t TouchButtonAccumulator::getToolType() const {
- if (mBtnToolMouse || mBtnToolLens) {
- return AMOTION_EVENT_TOOL_TYPE_MOUSE;
- }
- if (mBtnToolRubber) {
- return AMOTION_EVENT_TOOL_TYPE_ERASER;
- }
- if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
- return AMOTION_EVENT_TOOL_TYPE_STYLUS;
- }
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
- return AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-bool TouchButtonAccumulator::isToolActive() const {
- return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber
- || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush
- || mBtnToolMouse || mBtnToolLens
- || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
-}
-
-bool TouchButtonAccumulator::isHovering() const {
- return mHaveBtnTouch && !mBtnTouch;
-}
-
-bool TouchButtonAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
-
-// --- RawPointerAxes ---
-
-RawPointerAxes::RawPointerAxes() {
- clear();
-}
-
-void RawPointerAxes::clear() {
- x.clear();
- y.clear();
- pressure.clear();
- touchMajor.clear();
- touchMinor.clear();
- toolMajor.clear();
- toolMinor.clear();
- orientation.clear();
- distance.clear();
- tiltX.clear();
- tiltY.clear();
- trackingId.clear();
- slot.clear();
-}
-
-
-// --- RawPointerData ---
-
-RawPointerData::RawPointerData() {
- clear();
-}
-
-void RawPointerData::clear() {
- pointerCount = 0;
- clearIdBits();
-}
-
-void RawPointerData::copyFrom(const RawPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointers[i] = other.pointers[i];
-
- int id = pointers[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
-void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
- float x = 0, y = 0;
- uint32_t count = touchingIdBits.count();
- if (count) {
- for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const Pointer& pointer = pointerForId(id);
- x += pointer.x;
- y += pointer.y;
- }
- x /= count;
- y /= count;
- }
- *outX = x;
- *outY = y;
-}
-
-
-// --- CookedPointerData ---
-
-CookedPointerData::CookedPointerData() {
- clear();
-}
-
-void CookedPointerData::clear() {
- pointerCount = 0;
- hoveringIdBits.clear();
- touchingIdBits.clear();
-}
-
-void CookedPointerData::copyFrom(const CookedPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
-
- int id = pointerProperties[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
-
-// --- SingleTouchMotionAccumulator ---
-
-SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
- clearAbsoluteAxes();
-}
-
-void SingleTouchMotionAccumulator::reset(InputDevice* device) {
- mAbsX = device->getAbsoluteAxisValue(ABS_X);
- mAbsY = device->getAbsoluteAxisValue(ABS_Y);
- mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
- mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
- mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
- mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
- mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
-}
-
-void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
- mAbsX = 0;
- mAbsY = 0;
- mAbsPressure = 0;
- mAbsToolWidth = 0;
- mAbsDistance = 0;
- mAbsTiltX = 0;
- mAbsTiltY = 0;
-}
-
-void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- switch (rawEvent->code) {
- case ABS_X:
- mAbsX = rawEvent->value;
- break;
- case ABS_Y:
- mAbsY = rawEvent->value;
- break;
- case ABS_PRESSURE:
- mAbsPressure = rawEvent->value;
- break;
- case ABS_TOOL_WIDTH:
- mAbsToolWidth = rawEvent->value;
- break;
- case ABS_DISTANCE:
- mAbsDistance = rawEvent->value;
- break;
- case ABS_TILT_X:
- mAbsTiltX = rawEvent->value;
- break;
- case ABS_TILT_Y:
- mAbsTiltY = rawEvent->value;
- break;
- }
- }
-}
-
-
-// --- MultiTouchMotionAccumulator ---
-
-MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
- : mCurrentSlot(-1),
- mSlots(nullptr),
- mSlotCount(0),
- mUsingSlotsProtocol(false),
- mHaveStylus(false) {}
-
-MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
- delete[] mSlots;
-}
-
-void MultiTouchMotionAccumulator::configure(InputDevice* device,
- size_t slotCount, bool usingSlotsProtocol) {
- mSlotCount = slotCount;
- mUsingSlotsProtocol = usingSlotsProtocol;
- mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
-
- delete[] mSlots;
- mSlots = new Slot[slotCount];
-}
-
-void MultiTouchMotionAccumulator::reset(InputDevice* device) {
- // Unfortunately there is no way to read the initial contents of the slots.
- // So when we reset the accumulator, we must assume they are all zeroes.
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot
- // before we start reading events from the device. It is possible that the
- // current slot index will not be the same as it was when the first event was
- // written into the evdev buffer, which means the input mapper could start
- // out of sync with the initial state of the events in the evdev buffer.
- // In the extremely unlikely case that this happens, the data from
- // two slots will be confused until the next ABS_MT_SLOT event is received.
- // This can cause the touch point to "jump", but at least there will be
- // no stuck touches.
- int32_t initialSlot;
- status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(),
- ABS_MT_SLOT, &initialSlot);
- if (status) {
- ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
- initialSlot = -1;
- }
- clearSlots(initialSlot);
- } else {
- clearSlots(-1);
- }
-}
-
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
- if (mSlots) {
- for (size_t i = 0; i < mSlotCount; i++) {
- mSlots[i].clear();
- }
- }
- mCurrentSlot = initialSlot;
-}
-
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- bool newSlot = false;
- if (mUsingSlotsProtocol) {
- if (rawEvent->code == ABS_MT_SLOT) {
- mCurrentSlot = rawEvent->value;
- newSlot = true;
- }
- } else if (mCurrentSlot < 0) {
- mCurrentSlot = 0;
- }
-
- if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
- if (newSlot) {
- ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %zd; ignoring this slot.",
- mCurrentSlot, mSlotCount - 1);
- }
-#endif
- } else {
- Slot* slot = &mSlots[mCurrentSlot];
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot->mInUse = true;
- slot->mAbsMTPositionX = rawEvent->value;
- break;
- case ABS_MT_POSITION_Y:
- slot->mInUse = true;
- slot->mAbsMTPositionY = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot->mInUse = true;
- slot->mAbsMTTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot->mInUse = true;
- slot->mAbsMTTouchMinor = rawEvent->value;
- slot->mHaveAbsMTTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot->mInUse = true;
- slot->mAbsMTWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot->mInUse = true;
- slot->mAbsMTWidthMinor = rawEvent->value;
- slot->mHaveAbsMTWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot->mInUse = true;
- slot->mAbsMTOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot->mInUse = false;
- } else {
- slot->mInUse = true;
- slot->mAbsMTTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot->mInUse = true;
- slot->mAbsMTPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot->mInUse = true;
- slot->mAbsMTDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot->mInUse = true;
- slot->mAbsMTToolType = rawEvent->value;
- slot->mHaveAbsMTToolType = true;
- break;
- }
- }
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
- // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
- mCurrentSlot += 1;
- }
-}
-
-void MultiTouchMotionAccumulator::finishSync() {
- if (!mUsingSlotsProtocol) {
- clearSlots(-1);
- }
-}
-
-bool MultiTouchMotionAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
-
-// --- MultiTouchMotionAccumulator::Slot ---
-
-MultiTouchMotionAccumulator::Slot::Slot() {
- clear();
-}
-
-void MultiTouchMotionAccumulator::Slot::clear() {
- mInUse = false;
- mHaveAbsMTTouchMinor = false;
- mHaveAbsMTWidthMinor = false;
- mHaveAbsMTToolType = false;
- mAbsMTPositionX = 0;
- mAbsMTPositionY = 0;
- mAbsMTTouchMajor = 0;
- mAbsMTTouchMinor = 0;
- mAbsMTWidthMajor = 0;
- mAbsMTWidthMinor = 0;
- mAbsMTOrientation = 0;
- mAbsMTTrackingId = -1;
- mAbsMTPressure = 0;
- mAbsMTDistance = 0;
- mAbsMTToolType = 0;
-}
-
-int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
- if (mHaveAbsMTToolType) {
- switch (mAbsMTToolType) {
- case MT_TOOL_FINGER:
- return AMOTION_EVENT_TOOL_TYPE_FINGER;
- case MT_TOOL_PEN:
- return AMOTION_EVENT_TOOL_TYPE_STYLUS;
- }
- }
- return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-
-// --- InputMapper ---
-
-InputMapper::InputMapper(InputDevice* device) :
- mDevice(device), mContext(device->getContext()) {
-}
-
-InputMapper::~InputMapper() {
-}
-
-void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- info->addSource(getSources());
-}
-
-void InputMapper::dump(std::string& dump) {
-}
-
-void InputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
-}
-
-void InputMapper::reset(nsecs_t when) {
-}
-
-void InputMapper::timeoutExpired(nsecs_t when) {
-}
-
-int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return AKEY_STATE_UNKNOWN;
-}
-
-bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- return false;
-}
-
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
-}
-
-void InputMapper::cancelVibrate(int32_t token) {
-}
-
-void InputMapper::cancelTouch(nsecs_t when) {
-}
-
-int32_t InputMapper::getMetaState() {
- return 0;
-}
-
-void InputMapper::updateMetaState(int32_t keyCode) {
-}
-
-void InputMapper::updateExternalStylusState(const StylusState& state) {
-
-}
-
-void InputMapper::fadePointer() {
-}
-
-status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
- return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
-}
-
-void InputMapper::bumpGeneration() {
- mDevice->bumpGeneration();
-}
-
-void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump,
- const RawAbsoluteAxisInfo& axis, const char* name) {
- if (axis.valid) {
- dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n",
- name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution);
- } else {
- dump += StringPrintf(INDENT4 "%s: unknown range\n", name);
- }
-}
-
-void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
- dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
- dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
- dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
- dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
-}
-
-// --- SwitchInputMapper ---
-
-SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
- InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {
-}
-
-SwitchInputMapper::~SwitchInputMapper() {
-}
-
-uint32_t SwitchInputMapper::getSources() {
- return AINPUT_SOURCE_SWITCH;
-}
-
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_SW:
- processSwitch(rawEvent->code, rawEvent->value);
- break;
-
- case EV_SYN:
- if (rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
- }
-}
-
-void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
- if (switchCode >= 0 && switchCode < 32) {
- if (switchValue) {
- mSwitchValues |= 1 << switchCode;
- } else {
- mSwitchValues &= ~(1 << switchCode);
- }
- mUpdatedSwitchMask |= 1 << switchCode;
- }
-}
-
-void SwitchInputMapper::sync(nsecs_t when) {
- if (mUpdatedSwitchMask) {
- uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
- mUpdatedSwitchMask);
- getListener()->notifySwitch(&args);
-
- mUpdatedSwitchMask = 0;
- }
-}
-
-int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
- return getEventHub()->getSwitchState(getDeviceId(), switchCode);
-}
-
-void SwitchInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Switch Input Mapper:\n";
- dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues);
-}
-
-// --- VibratorInputMapper ---
-
-VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
- InputMapper(device), mVibrating(false) {
-}
-
-VibratorInputMapper::~VibratorInputMapper() {
-}
-
-uint32_t VibratorInputMapper::getSources() {
- return 0;
-}
-
-void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- info->setVibrator(true);
-}
-
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
- // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
-}
-
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token) {
-#if DEBUG_VIBRATOR
- std::string patternStr;
- for (size_t i = 0; i < patternSize; i++) {
- if (i != 0) {
- patternStr += ", ";
- }
- patternStr += StringPrintf("%" PRId64, pattern[i]);
- }
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
- getDeviceId(), patternStr.c_str(), repeat, token);
-#endif
-
- mVibrating = true;
- memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
- mPatternSize = patternSize;
- mRepeat = repeat;
- mToken = token;
- mIndex = -1;
-
- nextStep();
-}
-
-void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
-
- if (mVibrating && mToken == token) {
- stopVibrating();
- }
-}
-
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
- if (mVibrating) {
- if (when >= mNextStepTime) {
- nextStep();
- } else {
- getContext()->requestTimeoutAtTime(mNextStepTime);
- }
- }
-}
-
-void VibratorInputMapper::nextStep() {
- mIndex += 1;
- if (size_t(mIndex) >= mPatternSize) {
- if (mRepeat < 0) {
- // We are done.
- stopVibrating();
- return;
- }
- mIndex = mRepeat;
- }
-
- bool vibratorOn = mIndex & 1;
- nsecs_t duration = mPattern[mIndex];
- if (vibratorOn) {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
-#endif
- getEventHub()->vibrate(getDeviceId(), duration);
- } else {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
- getEventHub()->cancelVibrate(getDeviceId());
- }
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- mNextStepTime = now + duration;
- getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
-#endif
-}
-
-void VibratorInputMapper::stopVibrating() {
- mVibrating = false;
-#if DEBUG_VIBRATOR
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
- getEventHub()->cancelVibrate(getDeviceId());
-}
-
-void VibratorInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Vibrator Input Mapper:\n";
- dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
-}
-
-
-// --- KeyboardInputMapper ---
-
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
- uint32_t source, int32_t keyboardType) :
- InputMapper(device), mSource(source), mKeyboardType(keyboardType) {
-}
-
-KeyboardInputMapper::~KeyboardInputMapper() {
-}
-
-uint32_t KeyboardInputMapper::getSources() {
- return mSource;
-}
-
-int32_t KeyboardInputMapper::getOrientation() {
- if (mViewport) {
- return mViewport->orientation;
- }
- return DISPLAY_ORIENTATION_0;
-}
-
-int32_t KeyboardInputMapper::getDisplayId() {
- if (mViewport) {
- return mViewport->displayId;
- }
- return ADISPLAY_ID_NONE;
-}
-
-void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- info->setKeyboardType(mKeyboardType);
- info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
-}
-
-void KeyboardInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Keyboard Input Mapper:\n";
- dumpParameters(dump);
- dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
- dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
- dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
- dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
- dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
-}
-
-std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
- nsecs_t when, const InputReaderConfiguration* config) {
- const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
- if (displayPort) {
- // Find the viewport that contains the same port
- return mDevice->getAssociatedViewport();
- }
-
- // No associated display defined, try to find default display if orientationAware.
- if (mParameters.orientationAware) {
- return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- }
-
- return std::nullopt;
-}
-
-void KeyboardInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- // Configure basic parameters.
- configureParameters();
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mViewport = findViewport(when, config);
- }
-}
-
-static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const *property) {
- int32_t mapped = 0;
- if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
- for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
- if (stemKeyRotationMap[i][0] == keyCode) {
- stemKeyRotationMap[i][1] = mapped;
- return;
- }
- }
- }
-}
-
-void KeyboardInputMapper::configureParameters() {
- mParameters.orientationAware = false;
- const PropertyMap& config = getDevice()->getConfiguration();
- config.tryGetProperty(String8("keyboard.orientationAware"),
- mParameters.orientationAware);
-
- if (mParameters.orientationAware) {
- mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
- mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
- mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
- mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
- }
-
- mParameters.handlesKeyRepeat = false;
- config.tryGetProperty(String8("keyboard.handlesKeyRepeat"),
- mParameters.handlesKeyRepeat);
-}
-
-void KeyboardInputMapper::dumpParameters(std::string& dump) {
- dump += INDENT3 "Parameters:\n";
- dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
- dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n",
- toString(mParameters.handlesKeyRepeat));
-}
-
-void KeyboardInputMapper::reset(nsecs_t when) {
- mMetaState = AMETA_NONE;
- mDownTime = 0;
- mKeyDowns.clear();
- mCurrentHidUsage = 0;
-
- resetLedState();
-
- InputMapper::reset(when);
-}
-
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_KEY: {
- int32_t scanCode = rawEvent->code;
- int32_t usageCode = mCurrentHidUsage;
- mCurrentHidUsage = 0;
-
- if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
- }
- break;
- }
- case EV_MSC: {
- if (rawEvent->code == MSC_SCAN) {
- mCurrentHidUsage = rawEvent->value;
- }
- break;
- }
- case EV_SYN: {
- if (rawEvent->code == SYN_REPORT) {
- mCurrentHidUsage = 0;
- }
- }
- }
-}
-
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
- return scanCode < BTN_MOUSE
- || scanCode >= KEY_OK
- || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE)
- || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MUTE:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_MEDIA_SKIP_FORWARD:
- case AKEYCODE_MEDIA_SKIP_BACKWARD:
- case AKEYCODE_MEDIA_STEP_FORWARD:
- case AKEYCODE_MEDIA_STEP_BACKWARD:
- case AKEYCODE_MEDIA_AUDIO_TRACK:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_TV_AUDIO_DESCRIPTION:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
- return true;
- }
- return false;
-}
-
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
- int32_t usageCode) {
- int32_t keyCode;
- int32_t keyMetaState;
- uint32_t policyFlags;
-
- if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
- &keyCode, &keyMetaState, &policyFlags)) {
- keyCode = AKEYCODE_UNKNOWN;
- keyMetaState = mMetaState;
- policyFlags = 0;
- }
-
- if (down) {
- // Rotate key codes according to orientation if needed.
- if (mParameters.orientationAware) {
- keyCode = rotateKeyCode(keyCode, getOrientation());
- }
-
- // Add key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
- // key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- } else {
- // key down
- if ((policyFlags & POLICY_FLAG_VIRTUAL)
- && mContext->shouldDropVirtualKey(when,
- getDevice(), keyCode, scanCode)) {
- return;
- }
- if (policyFlags & POLICY_FLAG_GESTURE) {
- mDevice->cancelTouch(when);
- }
-
- KeyDown keyDown;
- keyDown.keyCode = keyCode;
- keyDown.scanCode = scanCode;
- mKeyDowns.push_back(keyDown);
- }
-
- mDownTime = when;
- } else {
- // Remove key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
- // key up, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
- } else {
- // key was not actually down
- ALOGI("Dropping key up from device %s because the key was not down. "
- "keyCode=%d, scanCode=%d",
- getDeviceName().c_str(), keyCode, scanCode);
- return;
- }
- }
-
- if (updateMetaStateIfNeeded(keyCode, down)) {
- // If global meta state changed send it along with the key.
- // If it has not changed then we'll use what keymap gave us,
- // since key replacement logic might temporarily reset a few
- // meta bits for given key.
- keyMetaState = mMetaState;
- }
-
- nsecs_t downTime = mDownTime;
-
- // Key down on external an keyboard should wake the device.
- // We don't do this for internal keyboards to prevent them from waking up in your pocket.
- // For internal keyboards, the key layout file should specify the policy flags for
- // each wake key individually.
- // TODO: Use the input device configuration to control this behavior more finely.
- if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
-
- if (mParameters.handlesKeyRepeat) {
- policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
- }
-
- NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
- getListener()->notifyKey(&args);
-}
-
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
- size_t n = mKeyDowns.size();
- for (size_t i = 0; i < n; i++) {
- if (mKeyDowns[i].scanCode == scanCode) {
- return i;
- }
- }
- return -1;
-}
-
-int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
-}
-
-int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
-}
-
-bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
-}
-
-int32_t KeyboardInputMapper::getMetaState() {
- return mMetaState;
-}
-
-void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
- updateMetaStateIfNeeded(keyCode, false);
-}
-
-bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
- int32_t oldMetaState = mMetaState;
- int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
- bool metaStateChanged = oldMetaState != newMetaState;
- if (metaStateChanged) {
- mMetaState = newMetaState;
- updateLedState(false);
-
- getContext()->updateGlobalMetaState();
- }
-
- return metaStateChanged;
-}
-
-void KeyboardInputMapper::resetLedState() {
- initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
- initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
- initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK);
-
- updateLedState(true);
-}
-
-void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
- ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
- ledState.on = false;
-}
-
-void KeyboardInputMapper::updateLedState(bool reset) {
- updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK,
- AMETA_CAPS_LOCK_ON, reset);
- updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK,
- AMETA_NUM_LOCK_ON, reset);
- updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK,
- AMETA_SCROLL_LOCK_ON, reset);
-}
-
-void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState,
- int32_t led, int32_t modifier, bool reset) {
- if (ledState.avail) {
- bool desiredState = (mMetaState & modifier) != 0;
- if (reset || ledState.on != desiredState) {
- getEventHub()->setLedState(getDeviceId(), led, desiredState);
- ledState.on = desiredState;
- }
- }
-}
-
-std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
- if (mViewport) {
- return std::make_optional(mViewport->displayId);
- }
- return std::nullopt;
-}
-
-// --- CursorInputMapper ---
-
-CursorInputMapper::CursorInputMapper(InputDevice* device) :
- InputMapper(device) {
-}
-
-CursorInputMapper::~CursorInputMapper() {
-}
-
-uint32_t CursorInputMapper::getSources() {
- return mSource;
-}
-
-void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- if (mParameters.mode == Parameters::MODE_POINTER) {
- float minX, minY, maxX, maxY;
- if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
- }
- } else {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
- }
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-
- if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
- }
- if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
- }
-}
-
-void CursorInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Cursor Input Mapper:\n";
- dumpParameters(dump);
- dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
- dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
- dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
- dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
- dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
- toString(mCursorScrollAccumulator.haveRelativeVWheel()));
- dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
- toString(mCursorScrollAccumulator.haveRelativeHWheel()));
- dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
- dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
- dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
- dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
- dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
- dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
-}
-
-void CursorInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- mCursorScrollAccumulator.configure(getDevice());
-
- // Configure basic parameters.
- configureParameters();
-
- // Configure device mode.
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER_RELATIVE:
- // Should not happen during first time configuration.
- ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
- mParameters.mode = Parameters::MODE_POINTER;
- [[fallthrough]];
- case Parameters::MODE_POINTER:
- mSource = AINPUT_SOURCE_MOUSE;
- mXPrecision = 1.0f;
- mYPrecision = 1.0f;
- mXScale = 1.0f;
- mYScale = 1.0f;
- mPointerController = getPolicy()->obtainPointerController(getDeviceId());
- break;
- case Parameters::MODE_NAVIGATION:
- mSource = AINPUT_SOURCE_TRACKBALL;
- mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
- mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
- break;
- }
-
- mVWheelScale = 1.0f;
- mHWheelScale = 1.0f;
- }
-
- if ((!changes && config->pointerCapture)
- || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
- if (config->pointerCapture) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
- mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
- // Keep PointerController around in order to preserve the pointer position.
- mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- } else {
- ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
- }
- } else {
- if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
- mParameters.mode = Parameters::MODE_POINTER;
- mSource = AINPUT_SOURCE_MOUSE;
- } else {
- ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
- }
- }
- bumpGeneration();
- if (changes) {
- getDevice()->notifyReset(when);
- }
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
- mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
- mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mOrientation = DISPLAY_ORIENTATION_0;
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
- }
- }
-
- // Update the PointerController if viewports changed.
- if (mParameters.mode == Parameters::MODE_POINTER) {
- getPolicy()->obtainPointerController(getDeviceId());
- }
- bumpGeneration();
- }
-}
-
-void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
- String8 cursorModeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
- if (cursorModeString == "navigation") {
- mParameters.mode = Parameters::MODE_NAVIGATION;
- } else if (cursorModeString != "pointer" && cursorModeString != "default") {
- ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
- }
- }
-
- mParameters.orientationAware = false;
- getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
- mParameters.orientationAware);
-
- mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
- mParameters.hasAssociatedDisplay = true;
- }
-}
-
-void CursorInputMapper::dumpParameters(std::string& dump) {
- dump += INDENT3 "Parameters:\n";
- dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
- toString(mParameters.hasAssociatedDisplay));
-
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- dump += INDENT4 "Mode: pointer\n";
- break;
- case Parameters::MODE_POINTER_RELATIVE:
- dump += INDENT4 "Mode: relative pointer\n";
- break;
- case Parameters::MODE_NAVIGATION:
- dump += INDENT4 "Mode: navigation\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
-}
-
-void CursorInputMapper::reset(nsecs_t when) {
- mButtonState = 0;
- mDownTime = 0;
-
- mPointerVelocityControl.reset();
- mWheelXVelocityControl.reset();
- mWheelYVelocityControl.reset();
-
- mCursorButtonAccumulator.reset(getDevice());
- mCursorMotionAccumulator.reset(getDevice());
- mCursorScrollAccumulator.reset(getDevice());
-
- InputMapper::reset(when);
-}
-
-void CursorInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorMotionAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void CursorInputMapper::sync(nsecs_t when) {
- int32_t lastButtonState = mButtonState;
- int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
- mButtonState = currentButtonState;
-
- bool wasDown = isPointerDown(lastButtonState);
- bool down = isPointerDown(currentButtonState);
- bool downChanged;
- if (!wasDown && down) {
- mDownTime = when;
- downChanged = true;
- } else if (wasDown && !down) {
- downChanged = true;
- } else {
- downChanged = false;
- }
- nsecs_t downTime = mDownTime;
- bool buttonsChanged = currentButtonState != lastButtonState;
- int32_t buttonsPressed = currentButtonState & ~lastButtonState;
- int32_t buttonsReleased = lastButtonState & ~currentButtonState;
-
- float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
- float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
- bool moved = deltaX != 0 || deltaY != 0;
-
- // Rotate delta according to orientation if needed.
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay
- && (deltaX != 0.0f || deltaY != 0.0f)) {
- rotateDelta(mOrientation, &deltaX, &deltaY);
- }
-
- // Move the pointer.
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
- float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
- bool scrolled = vscroll != 0 || hscroll != 0;
-
- mWheelYVelocityControl.move(when, nullptr, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, nullptr);
-
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- int32_t displayId;
- float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- if (mSource == AINPUT_SOURCE_MOUSE) {
- if (moved || scrolled || buttonsChanged) {
- mPointerController->setPresentation(
- PointerControllerInterface::PRESENTATION_POINTER);
-
- if (moved) {
- mPointerController->move(deltaX, deltaY);
- }
-
- if (buttonsChanged) {
- mPointerController->setButtonState(currentButtonState);
- }
-
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- }
-
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
- displayId = mPointerController->getDisplayId();
- } else {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
- displayId = ADISPLAY_ID_NONE;
- }
-
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
-
- // Moving an external trackball or mouse should wake the device.
- // We don't do this for internal cursor devices to prevent them from waking up
- // the device in your pocket.
- // TODO: Use the input device configuration to control this behavior more finely.
- uint32_t policyFlags = 0;
- if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
-
- // Synthesize key down from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
-
- // Send motion event.
- if (downChanged || moved || scrolled || buttonsChanged) {
- int32_t metaState = mContext->getGlobalMetaState();
- int32_t buttonState = lastButtonState;
- int32_t motionEventAction;
- if (downChanged) {
- motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
- } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
- motionEventAction = AMOTION_EVENT_ACTION_MOVE;
- } else {
- motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
- }
-
- if (buttonsReleased) {
- BitSet32 released(buttonsReleased);
- while (!released.isEmpty()) {
- int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
- buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
- mSource, displayId, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&releaseArgs);
- }
- }
-
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, motionEventAction, 0, 0, metaState,
- currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
-
- if (buttonsPressed) {
- BitSet32 pressed(buttonsPressed);
- while (!pressed.isEmpty()) {
- int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
- buttonState |= actionButton;
- NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
- mSource, displayId, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&pressArgs);
- }
- }
-
- ALOG_ASSERT(buttonState == currentButtonState);
-
- // Send hover move after UP to tell the application that the mouse is hovering now.
- if (motionEventAction == AMOTION_EVENT_ACTION_UP
- && (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- 0, metaState, currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
- yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&hoverArgs);
- }
-
- // Send scroll events.
- if (scrolled) {
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
- NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
- mSource, displayId, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
- currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
- yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
- }
- }
-
- // Synthesize key up from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
-
- mCursorMotionAccumulator.finishSync();
- mCursorScrollAccumulator.finishSync();
-}
-
-int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
- return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
- } else {
- return AKEY_STATE_UNKNOWN;
- }
-}
-
-void CursorInputMapper::fadePointer() {
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
-}
-
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- return std::make_optional(mPointerController->getDisplayId());
- } else {
- // If the device is orientationAware and not a mouse,
- // it expects to dispatch events to any display
- return std::make_optional(ADISPLAY_ID_NONE);
- }
- }
- return std::nullopt;
-}
-
-// --- RotaryEncoderInputMapper ---
-
-RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
- InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
- mSource = AINPUT_SOURCE_ROTARY_ENCODER;
-}
-
-RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {
-}
-
-uint32_t RotaryEncoderInputMapper::getSources() {
- return mSource;
-}
-
-void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
- float res = 0.0f;
- if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
- ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
- }
- if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
- mScalingFactor)) {
- ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
- "default to 1.0!\n");
- mScalingFactor = 1.0f;
- }
- info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- res * mScalingFactor);
- }
-}
-
-void RotaryEncoderInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Rotary Encoder Input Mapper:\n";
- dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
- toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
-}
-
-void RotaryEncoderInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
- if (!changes) {
- mRotaryEncoderScrollAccumulator.configure(getDevice());
- }
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- }
-}
-
-void RotaryEncoderInputMapper::reset(nsecs_t when) {
- mRotaryEncoderScrollAccumulator.reset(getDevice());
-
- InputMapper::reset(when);
-}
-
-void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
- mRotaryEncoderScrollAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void RotaryEncoderInputMapper::sync(nsecs_t when) {
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-
- float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
- bool scrolled = scroll != 0;
-
- // This is not a pointer, so it's not associated with a display.
- int32_t displayId = ADISPLAY_ID_NONE;
-
- // Moving the rotary encoder should wake the device (if specified).
- uint32_t policyFlags = 0;
- if (scrolled && getDevice()->isExternal()) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
-
- if (mOrientation == DISPLAY_ORIENTATION_180) {
- scroll = -scroll;
- }
-
- // Send motion event.
- if (scrolled) {
- int32_t metaState = mContext->getGlobalMetaState();
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
-
- NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
- }
-
- mRotaryEncoderScrollAccumulator.finishSync();
-}
-
-// --- TouchInputMapper ---
-
-TouchInputMapper::TouchInputMapper(InputDevice* device) :
- InputMapper(device),
- mSource(0), mDeviceMode(DEVICE_MODE_DISABLED),
- mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0),
- mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0),
- mSurfaceOrientation(DISPLAY_ORIENTATION_0) {
-}
-
-TouchInputMapper::~TouchInputMapper() {
-}
-
-uint32_t TouchInputMapper::getSources() {
- return mSource;
-}
-
-void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- if (mDeviceMode != DEVICE_MODE_DISABLED) {
- info->addMotionRange(mOrientedRanges.x);
- info->addMotionRange(mOrientedRanges.y);
- info->addMotionRange(mOrientedRanges.pressure);
-
- if (mOrientedRanges.haveSize) {
- info->addMotionRange(mOrientedRanges.size);
- }
-
- if (mOrientedRanges.haveTouchSize) {
- info->addMotionRange(mOrientedRanges.touchMajor);
- info->addMotionRange(mOrientedRanges.touchMinor);
- }
-
- if (mOrientedRanges.haveToolSize) {
- info->addMotionRange(mOrientedRanges.toolMajor);
- info->addMotionRange(mOrientedRanges.toolMinor);
- }
-
- if (mOrientedRanges.haveOrientation) {
- info->addMotionRange(mOrientedRanges.orientation);
- }
-
- if (mOrientedRanges.haveDistance) {
- info->addMotionRange(mOrientedRanges.distance);
- }
-
- if (mOrientedRanges.haveTilt) {
- info->addMotionRange(mOrientedRanges.tilt);
- }
-
- if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- }
- info->setButtonUnderPad(mParameters.hasButtonUnderPad);
- }
-}
-
-void TouchInputMapper::dump(std::string& dump) {
- dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
- dumpParameters(dump);
- dumpVirtualKeys(dump);
- dumpRawPointerAxes(dump);
- dumpCalibration(dump);
- dumpAffineTransformation(dump);
- dumpSurface(dump);
-
- dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
- dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
- dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
- dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
- dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
- dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
- dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
- dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
- dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
- dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
- dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
- dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
- dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt));
- dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter);
- dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale);
- dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
- dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
-
- dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
- dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
- mLastRawState.rawPointerData.pointerCount);
- for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
- const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
- dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
- "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
- "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
- "toolType=%d, isHovering=%s\n", i,
- pointer.id, pointer.x, pointer.y, pointer.pressure,
- pointer.touchMajor, pointer.touchMinor,
- pointer.toolMajor, pointer.toolMinor,
- pointer.orientation, pointer.tiltX, pointer.tiltY, pointer.distance,
- pointer.toolType, toString(pointer.isHovering));
- }
-
- dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", mLastCookedState.buttonState);
- dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
- mLastCookedState.cookedPointerData.pointerCount);
- for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) {
- const PointerProperties& pointerProperties =
- mLastCookedState.cookedPointerData.pointerProperties[i];
- const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
- dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
- "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, "
- "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
- "toolType=%d, isHovering=%s\n", i,
- pointerProperties.id,
- pointerCoords.getX(),
- pointerCoords.getY(),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
- pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
- pointerProperties.toolType,
- toString(mLastCookedState.cookedPointerData.isHovering(i)));
- }
-
- dump += INDENT3 "Stylus Fusion:\n";
- dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
- toString(mExternalStylusConnected));
- dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
- dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
- mExternalStylusFusionTimeout);
- dump += INDENT3 "External Stylus State:\n";
- dumpStylusState(dump, mExternalStylusState);
-
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
- dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n",
- mPointerXMovementScale);
- dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n",
- mPointerYMovementScale);
- dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n",
- mPointerXZoomScale);
- dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n",
- mPointerYZoomScale);
- dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n",
- mPointerGestureMaxSwipeWidth);
- }
-}
-
-const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
- switch (deviceMode) {
- case DEVICE_MODE_DISABLED:
- return "disabled";
- case DEVICE_MODE_DIRECT:
- return "direct";
- case DEVICE_MODE_UNSCALED:
- return "unscaled";
- case DEVICE_MODE_NAVIGATION:
- return "navigation";
- case DEVICE_MODE_POINTER:
- return "pointer";
- }
- return "unknown";
-}
-
-void TouchInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- mConfig = *config;
-
- if (!changes) { // first time only
- // Configure basic parameters.
- configureParameters();
-
- // Configure common accumulators.
- mCursorScrollAccumulator.configure(getDevice());
- mTouchButtonAccumulator.configure(getDevice());
-
- // Configure absolute axis information.
- configureRawPointerAxes();
-
- // Prepare input device calibration.
- parseCalibration();
- resolveCalibration();
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
- // Update location calibration to reflect current settings
- updateAffineTransformation();
- }
-
- if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
- // Update pointer speed.
- mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
- mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
- }
-
- bool resetNeeded = false;
- if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
- | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
- | InputReaderConfiguration::CHANGE_SHOW_TOUCHES
- | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
- // Configure device sources, surface dimensions, orientation and
- // scaling factors.
- configureSurface(when, &resetNeeded);
- }
-
- if (changes && resetNeeded) {
- // Send reset, unless this is the first time the device has been configured,
- // in which case the reader will call reset itself after all mappers are ready.
- getDevice()->notifyReset(when);
- }
-}
-
-void TouchInputMapper::resolveExternalStylusPresence() {
- std::vector<InputDeviceInfo> devices;
- mContext->getExternalStylusDevices(devices);
- mExternalStylusConnected = !devices.empty();
-
- if (!mExternalStylusConnected) {
- resetExternalStylus();
- }
-}
-
-void TouchInputMapper::configureParameters() {
- // Use the pointer presentation mode for devices that do not support distinct
- // multitouch. The spot-based presentation relies on being able to accurately
- // locate two or more fingers on the touch pad.
- mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
- ? Parameters::GESTURE_MODE_SINGLE_TOUCH : Parameters::GESTURE_MODE_MULTI_TOUCH;
-
- String8 gestureModeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
- gestureModeString)) {
- if (gestureModeString == "single-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
- } else if (gestureModeString == "multi-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
- } else if (gestureModeString != "default") {
- ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
- }
- }
-
- if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
- // The device is a touch screen.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
- // The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
- || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
- // The device is a cursor device with a touch pad attached.
- // By default don't use the touch pad to move the pointer.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
- } else {
- // The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- }
-
- mParameters.hasButtonUnderPad=
- getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
-
- String8 deviceTypeString;
- if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
- deviceTypeString)) {
- if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- } else if (deviceTypeString == "touchPad") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
- } else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
- } else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
- } else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
- }
- }
-
- mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
- getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
- mParameters.orientationAware);
-
- mParameters.hasAssociatedDisplay = false;
- mParameters.associatedDisplayIsExternal = false;
- if (mParameters.orientationAware
- || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
- || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
- mParameters.hasAssociatedDisplay = true;
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
- mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
- String8 uniqueDisplayId;
- getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
- uniqueDisplayId);
- mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
- }
- }
- if (getDevice()->getAssociatedDisplayPort()) {
- mParameters.hasAssociatedDisplay = true;
- }
-
- // Initial downs on external touch devices should wake the device.
- // Normally we don't do this for internal touch screens to prevent them from waking
- // up in your pocket but you can enable it using the input device configuration.
- mParameters.wake = getDevice()->isExternal();
- getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"),
- mParameters.wake);
-}
-
-void TouchInputMapper::dumpParameters(std::string& dump) {
- dump += INDENT3 "Parameters:\n";
-
- switch (mParameters.gestureMode) {
- case Parameters::GESTURE_MODE_SINGLE_TOUCH:
- dump += INDENT4 "GestureMode: single-touch\n";
- break;
- case Parameters::GESTURE_MODE_MULTI_TOUCH:
- dump += INDENT4 "GestureMode: multi-touch\n";
- break;
- default:
- assert(false);
- }
-
- switch (mParameters.deviceType) {
- case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
- dump += INDENT4 "DeviceType: touchScreen\n";
- break;
- case Parameters::DEVICE_TYPE_TOUCH_PAD:
- dump += INDENT4 "DeviceType: touchPad\n";
- break;
- case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
- dump += INDENT4 "DeviceType: touchNavigation\n";
- break;
- case Parameters::DEVICE_TYPE_POINTER:
- dump += INDENT4 "DeviceType: pointer\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- dump += StringPrintf(
- INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n",
- toString(mParameters.hasAssociatedDisplay),
- toString(mParameters.associatedDisplayIsExternal),
- mParameters.uniqueDisplayId.c_str());
- dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
- toString(mParameters.orientationAware));
-}
-
-void TouchInputMapper::configureRawPointerAxes() {
- mRawPointerAxes.clear();
-}
-
-void TouchInputMapper::dumpRawPointerAxes(std::string& dump) {
- dump += INDENT3 "Raw Touch Axes:\n";
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
- dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
-}
-
-bool TouchInputMapper::hasExternalStylus() const {
- return mExternalStylusConnected;
-}
-
-/**
- * Determine which DisplayViewport to use.
- * 1. If display port is specified, return the matching viewport. If matching viewport not
- * found, then return.
- * 2. If a device has associated display, get the matching viewport by either unique id or by
- * the display type (internal or external).
- * 3. Otherwise, use a non-display viewport.
- */
-std::optional<DisplayViewport> TouchInputMapper::findViewport() {
- if (mParameters.hasAssociatedDisplay) {
- const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
- if (displayPort) {
- // Find the viewport that contains the same port
- return mDevice->getAssociatedViewport();
- }
-
- // Check if uniqueDisplayId is specified in idc file.
- if (!mParameters.uniqueDisplayId.empty()) {
- return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
- }
-
- ViewportType viewportTypeToUse;
- if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
- } else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
- }
-
- std::optional<DisplayViewport> viewport =
- mConfig.getDisplayViewportByType(viewportTypeToUse);
- if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
- ALOGW("Input device %s should be associated with external display, "
- "fallback to internal one for the external viewport is not found.",
- getDeviceName().c_str());
- viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- }
-
- return viewport;
- }
-
- // No associated display, return a non-display viewport.
- DisplayViewport newViewport;
- // Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.getRawWidth();
- int32_t rawHeight = mRawPointerAxes.getRawHeight();
- newViewport.setNonDisplayViewport(rawWidth, rawHeight);
- return std::make_optional(newViewport);
-}
-
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
- int32_t oldDeviceMode = mDeviceMode;
-
- resolveExternalStylusPresence();
-
- // Determine device mode.
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
- && mConfig.pointerGesturesEnabled) {
- mSource = AINPUT_SOURCE_MOUSE;
- mDeviceMode = DEVICE_MODE_POINTER;
- if (hasStylus()) {
- mSource |= AINPUT_SOURCE_STYLUS;
- }
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
- && mParameters.hasAssociatedDisplay) {
- mSource = AINPUT_SOURCE_TOUCHSCREEN;
- mDeviceMode = DEVICE_MODE_DIRECT;
- if (hasStylus()) {
- mSource |= AINPUT_SOURCE_STYLUS;
- }
- if (hasExternalStylus()) {
- mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
- }
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
- mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_NAVIGATION;
- } else {
- mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DEVICE_MODE_UNSCALED;
- }
-
- // Ensure we have valid X and Y axes.
- if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW("Touch device '%s' did not report support for X or Y axis! "
- "The device will be inoperable.", getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
-
- // Get associated display dimensions.
- std::optional<DisplayViewport> newViewport = findViewport();
- if (!newViewport) {
- ALOGI("Touch device '%s' could not query the properties of its associated "
- "display. The device will be inoperable until the display size "
- "becomes available.",
- getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
-
- // Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.getRawWidth();
- int32_t rawHeight = mRawPointerAxes.getRawHeight();
-
- bool viewportChanged = mViewport != *newViewport;
- if (viewportChanged) {
- mViewport = *newViewport;
-
- if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
- // Convert rotated viewport to natural surface coordinates.
- int32_t naturalLogicalWidth, naturalLogicalHeight;
- int32_t naturalPhysicalWidth, naturalPhysicalHeight;
- int32_t naturalPhysicalLeft, naturalPhysicalTop;
- int32_t naturalDeviceWidth, naturalDeviceHeight;
- switch (mViewport.orientation) {
- case DISPLAY_ORIENTATION_90:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalPhysicalTop = mViewport.physicalLeft;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_180:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
- naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- case DISPLAY_ORIENTATION_270:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.physicalTop;
- naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_0:
- default:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.physicalLeft;
- naturalPhysicalTop = mViewport.physicalTop;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- }
-
- if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
- ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
- naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
- naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
- }
-
- mPhysicalWidth = naturalPhysicalWidth;
- mPhysicalHeight = naturalPhysicalHeight;
- mPhysicalLeft = naturalPhysicalLeft;
- mPhysicalTop = naturalPhysicalTop;
-
- mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
-
- mSurfaceOrientation = mParameters.orientationAware ?
- mViewport.orientation : DISPLAY_ORIENTATION_0;
- } else {
- mPhysicalWidth = rawWidth;
- mPhysicalHeight = rawHeight;
- mPhysicalLeft = 0;
- mPhysicalTop = 0;
-
- mSurfaceWidth = rawWidth;
- mSurfaceHeight = rawHeight;
- mSurfaceLeft = 0;
- mSurfaceTop = 0;
- mSurfaceOrientation = DISPLAY_ORIENTATION_0;
- }
- }
-
- // If moving between pointer modes, need to reset some state.
- bool deviceModeChanged = mDeviceMode != oldDeviceMode;
- if (deviceModeChanged) {
- mOrientedRanges.clear();
- }
-
- // Create or update pointer controller if needed.
- if (mDeviceMode == DEVICE_MODE_POINTER ||
- (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
- if (mPointerController == nullptr || viewportChanged) {
- mPointerController = getPolicy()->obtainPointerController(getDeviceId());
- }
- } else {
- mPointerController.clear();
- }
-
- if (viewportChanged || deviceModeChanged) {
- ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
- "display id %d",
- getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
- mSurfaceOrientation, mDeviceMode, mViewport.displayId);
-
- // Configure X and Y factors.
- mXScale = float(mSurfaceWidth) / rawWidth;
- mYScale = float(mSurfaceHeight) / rawHeight;
- mXTranslate = -mSurfaceLeft;
- mYTranslate = -mSurfaceTop;
- mXPrecision = 1.0f / mXScale;
- mYPrecision = 1.0f / mYScale;
-
- mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
- mOrientedRanges.x.source = mSource;
- mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
- mOrientedRanges.y.source = mSource;
-
- configureVirtualKeys();
-
- // Scale factor for terms that are not oriented in a particular axis.
- // If the pixels are square then xScale == yScale otherwise we fake it
- // by choosing an average.
- mGeometricScale = avg(mXScale, mYScale);
-
- // Size of diagonal axis.
- float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
-
- // Size factors.
- if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
- if (mRawPointerAxes.touchMajor.valid
- && mRawPointerAxes.touchMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
- } else if (mRawPointerAxes.toolMajor.valid
- && mRawPointerAxes.toolMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
- } else {
- mSizeScale = 0.0f;
- }
-
- mOrientedRanges.haveTouchSize = true;
- mOrientedRanges.haveToolSize = true;
- mOrientedRanges.haveSize = true;
-
- mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
- mOrientedRanges.touchMajor.source = mSource;
- mOrientedRanges.touchMajor.min = 0;
- mOrientedRanges.touchMajor.max = diagonalSize;
- mOrientedRanges.touchMajor.flat = 0;
- mOrientedRanges.touchMajor.fuzz = 0;
- mOrientedRanges.touchMajor.resolution = 0;
-
- mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
- mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
-
- mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
- mOrientedRanges.toolMajor.source = mSource;
- mOrientedRanges.toolMajor.min = 0;
- mOrientedRanges.toolMajor.max = diagonalSize;
- mOrientedRanges.toolMajor.flat = 0;
- mOrientedRanges.toolMajor.fuzz = 0;
- mOrientedRanges.toolMajor.resolution = 0;
-
- mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
- mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
-
- mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
- mOrientedRanges.size.source = mSource;
- mOrientedRanges.size.min = 0;
- mOrientedRanges.size.max = 1.0;
- mOrientedRanges.size.flat = 0;
- mOrientedRanges.size.fuzz = 0;
- mOrientedRanges.size.resolution = 0;
- } else {
- mSizeScale = 0.0f;
- }
-
- // Pressure factors.
- mPressureScale = 0;
- float pressureMax = 1.0;
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
- || mCalibration.pressureCalibration
- == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
- if (mCalibration.havePressureScale) {
- mPressureScale = mCalibration.pressureScale;
- pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
- } else if (mRawPointerAxes.pressure.valid
- && mRawPointerAxes.pressure.maxValue != 0) {
- mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
- }
- }
-
- mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
- mOrientedRanges.pressure.source = mSource;
- mOrientedRanges.pressure.min = 0;
- mOrientedRanges.pressure.max = pressureMax;
- mOrientedRanges.pressure.flat = 0;
- mOrientedRanges.pressure.fuzz = 0;
- mOrientedRanges.pressure.resolution = 0;
-
- // Tilt
- mTiltXCenter = 0;
- mTiltXScale = 0;
- mTiltYCenter = 0;
- mTiltYScale = 0;
- mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
- if (mHaveTilt) {
- mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue,
- mRawPointerAxes.tiltX.maxValue);
- mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue,
- mRawPointerAxes.tiltY.maxValue);
- mTiltXScale = M_PI / 180;
- mTiltYScale = M_PI / 180;
-
- mOrientedRanges.haveTilt = true;
-
- mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
- mOrientedRanges.tilt.source = mSource;
- mOrientedRanges.tilt.min = 0;
- mOrientedRanges.tilt.max = M_PI_2;
- mOrientedRanges.tilt.flat = 0;
- mOrientedRanges.tilt.fuzz = 0;
- mOrientedRanges.tilt.resolution = 0;
- }
-
- // Orientation
- mOrientationScale = 0;
- if (mHaveTilt) {
- mOrientedRanges.haveOrientation = true;
-
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI;
- mOrientedRanges.orientation.max = M_PI;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
- } else if (mCalibration.orientationCalibration !=
- Calibration::ORIENTATION_CALIBRATION_NONE) {
- if (mCalibration.orientationCalibration
- == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
- if (mRawPointerAxes.orientation.valid) {
- if (mRawPointerAxes.orientation.maxValue > 0) {
- mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
- } else if (mRawPointerAxes.orientation.minValue < 0) {
- mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
- } else {
- mOrientationScale = 0;
- }
- }
- }
-
- mOrientedRanges.haveOrientation = true;
-
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI_2;
- mOrientedRanges.orientation.max = M_PI_2;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
- }
-
- // Distance
- mDistanceScale = 0;
- if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
- if (mCalibration.distanceCalibration
- == Calibration::DISTANCE_CALIBRATION_SCALED) {
- if (mCalibration.haveDistanceScale) {
- mDistanceScale = mCalibration.distanceScale;
- } else {
- mDistanceScale = 1.0f;
- }
- }
-
- mOrientedRanges.haveDistance = true;
-
- mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
- mOrientedRanges.distance.source = mSource;
- mOrientedRanges.distance.min =
- mRawPointerAxes.distance.minValue * mDistanceScale;
- mOrientedRanges.distance.max =
- mRawPointerAxes.distance.maxValue * mDistanceScale;
- mOrientedRanges.distance.flat = 0;
- mOrientedRanges.distance.fuzz =
- mRawPointerAxes.distance.fuzz * mDistanceScale;
- mOrientedRanges.distance.resolution = 0;
- }
-
- // Compute oriented precision, scales and ranges.
- // Note that the maximum value reported is an inclusive maximum value so it is one
- // unit less than the total width or height of surface.
- switch (mSurfaceOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- mOrientedXPrecision = mYPrecision;
- mOrientedYPrecision = mXPrecision;
-
- mOrientedRanges.x.min = mYTranslate;
- mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
-
- mOrientedRanges.y.min = mXTranslate;
- mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
- break;
-
- default:
- mOrientedXPrecision = mXPrecision;
- mOrientedYPrecision = mYPrecision;
-
- mOrientedRanges.x.min = mXTranslate;
- mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
- mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = 0;
- mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
-
- mOrientedRanges.y.min = mYTranslate;
- mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
- mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = 0;
- mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
- break;
- }
-
- // Location
- updateAffineTransformation();
-
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- // Compute pointer gesture detection parameters.
- float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
-
- // Scale movements such that one whole swipe of the touch pad covers a
- // given area relative to the diagonal size of the display when no acceleration
- // is applied.
- // Assume that the touch pad has a square aspect ratio such that movements in
- // X and Y of the same number of raw units cover the same physical distance.
- mPointerXMovementScale = mConfig.pointerGestureMovementSpeedRatio
- * displayDiagonal / rawDiagonal;
- mPointerYMovementScale = mPointerXMovementScale;
-
- // Scale zooms to cover a smaller range of the display than movements do.
- // This value determines the area around the pointer that is affected by freeform
- // pointer gestures.
- mPointerXZoomScale = mConfig.pointerGestureZoomSpeedRatio
- * displayDiagonal / rawDiagonal;
- mPointerYZoomScale = mPointerXZoomScale;
-
- // Max width between pointers to detect a swipe gesture is more than some fraction
- // of the diagonal axis of the touch pad. Touches that are wider than this are
- // translated into freeform gestures.
- mPointerGestureMaxSwipeWidth =
- mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
-
- // Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
- }
-
- // Inform the dispatcher about the changes.
- *outResetNeeded = true;
- bumpGeneration();
- }
-}
-
-void TouchInputMapper::dumpSurface(std::string& dump) {
- dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
- dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
- dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
- dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
- dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
- dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
- dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
- dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
- dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
-}
-
-void TouchInputMapper::configureVirtualKeys() {
- std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
- getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
-
- mVirtualKeys.clear();
-
- if (virtualKeyDefinitions.size() == 0) {
- return;
- }
-
- int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
- int32_t touchScreenTop = mRawPointerAxes.y.minValue;
- int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
- int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
-
- for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) {
- VirtualKey virtualKey;
-
- virtualKey.scanCode = virtualKeyDefinition.scanCode;
- int32_t keyCode;
- int32_t dummyKeyMetaState;
- uint32_t flags;
- if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0,
- &keyCode, &dummyKeyMetaState, &flags)) {
- ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
- virtualKey.scanCode);
- continue; // drop the key
- }
-
- virtualKey.keyCode = keyCode;
- virtualKey.flags = flags;
-
- // convert the key definition's display coordinates into touch coordinates for a hit box
- int32_t halfWidth = virtualKeyDefinition.width / 2;
- int32_t halfHeight = virtualKeyDefinition.height / 2;
-
- virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
- * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
- virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
- * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
- virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
- * touchScreenHeight / mSurfaceHeight + touchScreenTop;
- virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
- * touchScreenHeight / mSurfaceHeight + touchScreenTop;
- mVirtualKeys.push_back(virtualKey);
- }
-}
-
-void TouchInputMapper::dumpVirtualKeys(std::string& dump) {
- if (!mVirtualKeys.empty()) {
- dump += INDENT3 "Virtual Keys:\n";
-
- for (size_t i = 0; i < mVirtualKeys.size(); i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
- dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
- "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
- i, virtualKey.scanCode, virtualKey.keyCode,
- virtualKey.hitLeft, virtualKey.hitRight,
- virtualKey.hitTop, virtualKey.hitBottom);
- }
- }
-}
-
-void TouchInputMapper::parseCalibration() {
- const PropertyMap& in = getDevice()->getConfiguration();
- Calibration& out = mCalibration;
-
- // Size
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
- String8 sizeCalibrationString;
- if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
- if (sizeCalibrationString == "none") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
- } else if (sizeCalibrationString == "geometric") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
- } else if (sizeCalibrationString == "diameter") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
- } else if (sizeCalibrationString == "box") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
- } else if (sizeCalibrationString == "area") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
- } else if (sizeCalibrationString != "default") {
- ALOGW("Invalid value for touch.size.calibration: '%s'",
- sizeCalibrationString.string());
- }
- }
-
- out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"),
- out.sizeScale);
- out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"),
- out.sizeBias);
- out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"),
- out.sizeIsSummed);
-
- // Pressure
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
- String8 pressureCalibrationString;
- if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
- if (pressureCalibrationString == "none") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
- } else if (pressureCalibrationString == "physical") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
- } else if (pressureCalibrationString == "amplitude") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
- } else if (pressureCalibrationString != "default") {
- ALOGW("Invalid value for touch.pressure.calibration: '%s'",
- pressureCalibrationString.string());
- }
- }
-
- out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
- out.pressureScale);
-
- // Orientation
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
- String8 orientationCalibrationString;
- if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
- if (orientationCalibrationString == "none") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
- } else if (orientationCalibrationString == "interpolated") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
- } else if (orientationCalibrationString == "vector") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
- } else if (orientationCalibrationString != "default") {
- ALOGW("Invalid value for touch.orientation.calibration: '%s'",
- orientationCalibrationString.string());
- }
- }
-
- // Distance
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
- String8 distanceCalibrationString;
- if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
- if (distanceCalibrationString == "none") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
- } else if (distanceCalibrationString == "scaled") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
- } else if (distanceCalibrationString != "default") {
- ALOGW("Invalid value for touch.distance.calibration: '%s'",
- distanceCalibrationString.string());
- }
- }
-
- out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"),
- out.distanceScale);
-
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
- String8 coverageCalibrationString;
- if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
- if (coverageCalibrationString == "none") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
- } else if (coverageCalibrationString == "box") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
- } else if (coverageCalibrationString != "default") {
- ALOGW("Invalid value for touch.coverage.calibration: '%s'",
- coverageCalibrationString.string());
- }
- }
-}
-
-void TouchInputMapper::resolveCalibration() {
- // Size
- if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
- }
- } else {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
- }
-
- // Pressure
- if (mRawPointerAxes.pressure.valid) {
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
- }
- } else {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
- }
-
- // Orientation
- if (mRawPointerAxes.orientation.valid) {
- if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
- }
- } else {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
- }
-
- // Distance
- if (mRawPointerAxes.distance.valid) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
- }
- } else {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
- }
-
- // Coverage
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
- mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
- }
-}
-
-void TouchInputMapper::dumpCalibration(std::string& dump) {
- dump += INDENT3 "Calibration:\n";
-
- // Size
- switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_NONE:
- dump += INDENT4 "touch.size.calibration: none\n";
- break;
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- dump += INDENT4 "touch.size.calibration: geometric\n";
- break;
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- dump += INDENT4 "touch.size.calibration: diameter\n";
- break;
- case Calibration::SIZE_CALIBRATION_BOX:
- dump += INDENT4 "touch.size.calibration: box\n";
- break;
- case Calibration::SIZE_CALIBRATION_AREA:
- dump += INDENT4 "touch.size.calibration: area\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.haveSizeScale) {
- dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n",
- mCalibration.sizeScale);
- }
-
- if (mCalibration.haveSizeBias) {
- dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n",
- mCalibration.sizeBias);
- }
-
- if (mCalibration.haveSizeIsSummed) {
- dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n",
- toString(mCalibration.sizeIsSummed));
- }
-
- // Pressure
- switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_NONE:
- dump += INDENT4 "touch.pressure.calibration: none\n";
- break;
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- dump += INDENT4 "touch.pressure.calibration: physical\n";
- break;
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- dump += INDENT4 "touch.pressure.calibration: amplitude\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.havePressureScale) {
- dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n",
- mCalibration.pressureScale);
- }
-
- // Orientation
- switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_NONE:
- dump += INDENT4 "touch.orientation.calibration: none\n";
- break;
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- dump += INDENT4 "touch.orientation.calibration: interpolated\n";
- break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR:
- dump += INDENT4 "touch.orientation.calibration: vector\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- // Distance
- switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_NONE:
- dump += INDENT4 "touch.distance.calibration: none\n";
- break;
- case Calibration::DISTANCE_CALIBRATION_SCALED:
- dump += INDENT4 "touch.distance.calibration: scaled\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
- if (mCalibration.haveDistanceScale) {
- dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n",
- mCalibration.distanceScale);
- }
-
- switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_NONE:
- dump += INDENT4 "touch.coverage.calibration: none\n";
- break;
- case Calibration::COVERAGE_CALIBRATION_BOX:
- dump += INDENT4 "touch.coverage.calibration: box\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-}
-
-void TouchInputMapper::dumpAffineTransformation(std::string& dump) {
- dump += INDENT3 "Affine Transformation:\n";
-
- dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale);
- dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix);
- dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset);
- dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix);
- dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale);
- dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset);
-}
-
-void TouchInputMapper::updateAffineTransformation() {
- mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
- mSurfaceOrientation);
-}
-
-void TouchInputMapper::reset(nsecs_t when) {
- mCursorButtonAccumulator.reset(getDevice());
- mCursorScrollAccumulator.reset(getDevice());
- mTouchButtonAccumulator.reset(getDevice());
-
- mPointerVelocityControl.reset();
- mWheelXVelocityControl.reset();
- mWheelYVelocityControl.reset();
-
- mRawStatesPending.clear();
- mCurrentRawState.clear();
- mCurrentCookedState.clear();
- mLastRawState.clear();
- mLastCookedState.clear();
- mPointerUsage = POINTER_USAGE_NONE;
- mSentHoverEnter = false;
- mHavePointerIds = false;
- mCurrentMotionAborted = false;
- mDownTime = 0;
-
- mCurrentVirtualKey.down = false;
-
- mPointerGesture.reset();
- mPointerSimple.reset();
- resetExternalStylus();
-
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- mPointerController->clearSpots();
- }
-
- InputMapper::reset(when);
-}
-
-void TouchInputMapper::resetExternalStylus() {
- mExternalStylusState.clear();
- mExternalStylusId = -1;
- mExternalStylusFusionTimeout = LLONG_MAX;
- mExternalStylusDataPending = false;
-}
-
-void TouchInputMapper::clearStylusDataPendingFlags() {
- mExternalStylusDataPending = false;
- mExternalStylusFusionTimeout = LLONG_MAX;
-}
-
-void TouchInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
- mTouchButtonAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void TouchInputMapper::sync(nsecs_t when) {
- const RawState* last = mRawStatesPending.empty() ?
- &mCurrentRawState : &mRawStatesPending.back();
-
- // Push a new state.
- mRawStatesPending.emplace_back();
-
- RawState* next = &mRawStatesPending.back();
- next->clear();
- next->when = when;
-
- // Sync button state.
- next->buttonState = mTouchButtonAccumulator.getButtonState()
- | mCursorButtonAccumulator.getButtonState();
-
- // Sync scroll
- next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
- next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
- mCursorScrollAccumulator.finishSync();
-
- // Sync touch
- syncTouch(when, next);
-
- // Assign pointer ids.
- if (!mHavePointerIds) {
- assignPointerIds(last, next);
- }
-
-#if DEBUG_RAW_EVENTS
- ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x",
- last->rawPointerData.pointerCount,
- next->rawPointerData.pointerCount,
- last->rawPointerData.touchingIdBits.value,
- next->rawPointerData.touchingIdBits.value,
- last->rawPointerData.hoveringIdBits.value,
- next->rawPointerData.hoveringIdBits.value);
-#endif
-
- processRawTouches(false /*timeout*/);
-}
-
-void TouchInputMapper::processRawTouches(bool timeout) {
- if (mDeviceMode == DEVICE_MODE_DISABLED) {
- // Drop all input if the device is disabled.
- mCurrentRawState.clear();
- mRawStatesPending.clear();
- return;
- }
-
- // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
- // valid and must go through the full cook and dispatch cycle. This ensures that anything
- // touching the current state will only observe the events that have been dispatched to the
- // rest of the pipeline.
- const size_t N = mRawStatesPending.size();
- size_t count;
- for(count = 0; count < N; count++) {
- const RawState& next = mRawStatesPending[count];
-
- // A failure to assign the stylus id means that we're waiting on stylus data
- // and so should defer the rest of the pipeline.
- if (assignExternalStylusId(next, timeout)) {
- break;
- }
-
- // All ready to go.
- clearStylusDataPendingFlags();
- mCurrentRawState.copyFrom(next);
- if (mCurrentRawState.when < mLastRawState.when) {
- mCurrentRawState.when = mLastRawState.when;
- }
- cookAndDispatch(mCurrentRawState.when);
- }
- if (count != 0) {
- mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
- }
-
- if (mExternalStylusDataPending) {
- if (timeout) {
- nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
- clearStylusDataPendingFlags();
- mCurrentRawState.copyFrom(mLastRawState);
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, synthesizing event with new stylus data");
-#endif
- cookAndDispatch(when);
- } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
- mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
- getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
- }
- }
-}
-
-void TouchInputMapper::cookAndDispatch(nsecs_t when) {
- // Always start with a clean state.
- mCurrentCookedState.clear();
-
- // Apply stylus buttons to current raw state.
- applyExternalStylusButtonState(when);
-
- // Handle policy on initial down or hover events.
- bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
- && mCurrentRawState.rawPointerData.pointerCount != 0;
-
- uint32_t policyFlags = 0;
- bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
- if (initialDown || buttonsPressed) {
- // If this is a touch screen, hide the pointer on an initial down.
- if (mDeviceMode == DEVICE_MODE_DIRECT) {
- getContext()->fadePointer();
- }
-
- if (mParameters.wake) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
- }
-
- // Consume raw off-screen touches before cooking pointer data.
- // If touches are consumed, subsequent code will not receive any pointer data.
- if (consumeRawTouches(when, policyFlags)) {
- mCurrentRawState.rawPointerData.clear();
- }
-
- // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure
- // with cooked pointer data that has the same ids and indices as the raw data.
- // The following code can use either the raw or cooked data, as needed.
- cookPointerData();
-
- // Apply stylus pressure to current cooked state.
- applyExternalStylusTouchState(when);
-
- // Synthesize key down from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- mViewport.displayId, policyFlags,
- mLastCookedState.buttonState, mCurrentCookedState.buttonState);
-
- // Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
- !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
- mCurrentCookedState.stylusIdBits.markBit(id);
- } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- mCurrentCookedState.fingerIdBits.markBit(id);
- } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
- mCurrentCookedState.mouseIdBits.markBit(id);
- }
- }
- for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
- !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
- || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
- mCurrentCookedState.stylusIdBits.markBit(id);
- }
- }
-
- // Stylus takes precedence over all tools, then mouse, then finger.
- PointerUsage pointerUsage = mPointerUsage;
- if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
- mCurrentCookedState.mouseIdBits.clear();
- mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_STYLUS;
- } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
- mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_MOUSE;
- } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
- isPointerDown(mCurrentRawState.buttonState)) {
- pointerUsage = POINTER_USAGE_GESTURES;
- }
-
- dispatchPointerUsage(when, policyFlags, pointerUsage);
- } else {
- if (mDeviceMode == DEVICE_MODE_DIRECT
- && mConfig.showTouches && mPointerController != nullptr) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-
- mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits,
- mViewport.displayId);
- }
-
- if (!mCurrentMotionAborted) {
- dispatchButtonRelease(when, policyFlags);
- dispatchHoverExit(when, policyFlags);
- dispatchTouches(when, policyFlags);
- dispatchHoverEnterAndMove(when, policyFlags);
- dispatchButtonPress(when, policyFlags);
- }
-
- if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
- mCurrentMotionAborted = false;
- }
- }
-
- // Synthesize key up from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- mViewport.displayId, policyFlags,
- mLastCookedState.buttonState, mCurrentCookedState.buttonState);
-
- // Clear some transient state.
- mCurrentRawState.rawVScroll = 0;
- mCurrentRawState.rawHScroll = 0;
-
- // Copy current touch to last touch in preparation for the next cycle.
- mLastRawState.copyFrom(mCurrentRawState);
- mLastCookedState.copyFrom(mCurrentCookedState);
-}
-
-void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
- mCurrentRawState.buttonState |= mExternalStylusState.buttons;
- }
-}
-
-void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
- CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
- const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
-
- if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
- float pressure = mExternalStylusState.pressure;
- if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
- const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
- pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- }
- PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
- coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
-
- PointerProperties& properties =
- currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
- if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- properties.toolType = mExternalStylusState.toolType;
- }
- }
-}
-
-bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
- if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
- return false;
- }
-
- const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
- && state.rawPointerData.pointerCount != 0;
- if (initialDown) {
- if (mExternalStylusState.pressure != 0.0f) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Have both stylus and touch data, beginning fusion");
-#endif
- mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
- } else if (timeout) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, assuming touch is not a stylus.");
-#endif
- resetExternalStylus();
- } else {
- if (mExternalStylusFusionTimeout == LLONG_MAX) {
- mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
- }
-#if DEBUG_STYLUS_FUSION
- ALOGD("No stylus data but stylus is connected, requesting timeout "
- "(%" PRId64 "ms)", mExternalStylusFusionTimeout);
-#endif
- getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
- return true;
- }
- }
-
- // Check if the stylus pointer has gone up.
- if (mExternalStylusId != -1 &&
- !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Stylus pointer is going up");
-#endif
- mExternalStylusId = -1;
- }
-
- return false;
-}
-
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- if (mPointerUsage == POINTER_USAGE_GESTURES) {
- dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
- }
- } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
- if (mExternalStylusFusionTimeout < when) {
- processRawTouches(true /*timeout*/);
- } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
- getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
- }
- }
-}
-
-void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
- mExternalStylusState.copyFrom(state);
- if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
- // We're either in the middle of a fused stream of data or we're waiting on data before
- // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
- // data.
- mExternalStylusDataPending = true;
- processRawTouches(false /*timeout*/);
- }
-}
-
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
- // Check for release of a virtual key.
- if (mCurrentVirtualKey.down) {
- if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
- // Pointer went up while virtual key was down.
- mCurrentVirtualKey.down = false;
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- }
- return true;
- }
-
- if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
- uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(id);
- const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
- if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
- // Pointer is still within the space of the virtual key.
- return true;
- }
- }
-
- // Pointer left virtual key area or another pointer also went down.
- // Send key cancellation but do not consume the touch yet.
- // This is useful when the user swipes through from the virtual key area
- // into the main display surface.
- mCurrentVirtualKey.down = false;
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
- | AKEY_EVENT_FLAG_CANCELED);
- }
- }
-
- if (mLastRawState.rawPointerData.touchingIdBits.isEmpty()
- && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
- // Pointer just went down. Check for virtual key press or off-screen touches.
- uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
- const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
- if (!isPointInsideSurface(pointer.x, pointer.y)) {
- // If exactly one pointer went down, check for virtual key hit.
- // Otherwise we will drop the entire stroke.
- if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
- const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
- if (virtualKey) {
- mCurrentVirtualKey.down = true;
- mCurrentVirtualKey.downTime = when;
- mCurrentVirtualKey.keyCode = virtualKey->keyCode;
- mCurrentVirtualKey.scanCode = virtualKey->scanCode;
- mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey(
- when, getDevice(), virtualKey->keyCode, virtualKey->scanCode);
-
- if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode,
- mCurrentVirtualKey.scanCode);
-#endif
- dispatchVirtualKey(when, policyFlags,
- AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- }
- }
- }
- return true;
- }
- }
-
- // Disable all virtual key touches that happen within a short time interval of the
- // most recent touch within the screen area. The idea is to filter out stray
- // virtual key presses when interacting with the touch screen.
- //
- // Problems we're trying to solve:
- //
- // 1. While scrolling a list or dragging the window shade, the user swipes down into a
- // virtual key area that is implemented by a separate touch panel and accidentally
- // triggers a virtual key.
- //
- // 2. While typing in the on screen keyboard, the user taps slightly outside the screen
- // area and accidentally triggers a virtual key. This often happens when virtual keys
- // are layed out below the screen near to where the on screen keyboard's space bar
- // is displayed.
- if (mConfig.virtualKeyQuietTime > 0 &&
- !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
- mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
- }
- return false;
-}
-
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags) {
- int32_t keyCode = mCurrentVirtualKey.keyCode;
- int32_t scanCode = mCurrentVirtualKey.scanCode;
- nsecs_t downTime = mCurrentVirtualKey.downTime;
- int32_t metaState = mContext->getGlobalMetaState();
- policyFlags |= POLICY_FLAG_VIRTUAL;
-
- NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
- mViewport.displayId,
- policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
- getListener()->notifyKey(&args);
-}
-
-void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
- BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
- if (!currentIdBits.isEmpty()) {
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentCookedState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- mCurrentMotionAborted = true;
- }
-}
-
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
- BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
- BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentCookedState.buttonState;
-
- if (currentIdBits == lastIdBits) {
- if (!currentIdBits.isEmpty()) {
- // No pointer id changes so this is a move event.
- // The listener takes care of batching moves so we don't have to deal with that here.
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
- } else {
- // There may be pointers going up and pointers going down and pointers moving
- // all at the same time.
- BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
- BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
- BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
- BitSet32 dispatchedIdBits(lastIdBits.value);
-
- // Update last coordinates of pointers that have moved so that we observe the new
- // pointer positions at the same time as other pointers that have just gone up.
- bool moveNeeded = updateMovedPointers(
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mLastCookedState.cookedPointerData.pointerProperties,
- mLastCookedState.cookedPointerData.pointerCoords,
- mLastCookedState.cookedPointerData.idToIndex,
- moveIdBits);
- if (buttonState != mLastCookedState.buttonState) {
- moveNeeded = true;
- }
-
- // Dispatch pointer up events.
- while (!upIdBits.isEmpty()) {
- uint32_t upId = upIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
- mLastCookedState.cookedPointerData.pointerProperties,
- mLastCookedState.cookedPointerData.pointerCoords,
- mLastCookedState.cookedPointerData.idToIndex,
- dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- dispatchedIdBits.clearBit(upId);
- }
-
- // Dispatch move events if any of the remaining pointers moved from their old locations.
- // Although applications receive new locations as part of individual pointer up
- // events, they do not generally handle them except when presented in a move event.
- if (moveNeeded && !moveIdBits.isEmpty()) {
- ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-
- // Dispatch pointer down events using the new pointer locations.
- while (!downIdBits.isEmpty()) {
- uint32_t downId = downIdBits.clearFirstMarkedBit();
- dispatchedIdBits.markBit(downId);
-
- if (dispatchedIdBits.count() == 1) {
- // First pointer is going down. Set down time.
- mDownTime = when;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
- }
-}
-
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
- if (mSentHoverEnter &&
- (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()
- || !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
- int32_t metaState = getContext()->getGlobalMetaState();
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastCookedState.buttonState, 0,
- mLastCookedState.cookedPointerData.pointerProperties,
- mLastCookedState.cookedPointerData.pointerCoords,
- mLastCookedState.cookedPointerData.idToIndex,
- mLastCookedState.cookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- mSentHoverEnter = false;
- }
-}
-
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
- if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty()
- && !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
- int32_t metaState = getContext()->getGlobalMetaState();
- if (!mSentHoverEnter) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
- 0, 0, metaState, mCurrentRawState.buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- mSentHoverEnter = true;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-}
-
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
- BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
- const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
- const int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mLastCookedState.buttonState;
- while (!releasedButtons.isEmpty()) {
- int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
- buttonState &= ~actionButton;
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton,
- 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-}
-
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
- BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
- const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
- const int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mLastCookedState.buttonState;
- while (!pressedButtons.isEmpty()) {
- int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
- buttonState |= actionButton;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
- 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
-}
-
-const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
- if (!cookedPointerData.touchingIdBits.isEmpty()) {
- return cookedPointerData.touchingIdBits;
- }
- return cookedPointerData.hoveringIdBits;
-}
-
-void TouchInputMapper::cookPointerData() {
- uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
-
- mCurrentCookedState.cookedPointerData.clear();
- mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
- mCurrentCookedState.cookedPointerData.hoveringIdBits =
- mCurrentRawState.rawPointerData.hoveringIdBits;
- mCurrentCookedState.cookedPointerData.touchingIdBits =
- mCurrentRawState.rawPointerData.touchingIdBits;
-
- if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
- mCurrentCookedState.buttonState = 0;
- } else {
- mCurrentCookedState.buttonState = mCurrentRawState.buttonState;
- }
-
- // Walk through the the active pointers and map device coordinates onto
- // surface coordinates and adjust for display orientation.
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
-
- // Size
- float touchMajor, touchMinor, toolMajor, toolMinor, size;
- switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- case Calibration::SIZE_CALIBRATION_BOX:
- case Calibration::SIZE_CALIBRATION_AREA:
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
- touchMajor = in.touchMajor;
- touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
- toolMajor = in.toolMajor;
- toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.touchMinor.valid
- ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
- } else if (mRawPointerAxes.touchMajor.valid) {
- toolMajor = touchMajor = in.touchMajor;
- toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid
- ? in.touchMinor : in.touchMajor;
- size = mRawPointerAxes.touchMinor.valid
- ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
- } else if (mRawPointerAxes.toolMajor.valid) {
- touchMajor = toolMajor = in.toolMajor;
- touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid
- ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.toolMinor.valid
- ? avg(in.toolMajor, in.toolMinor) : in.toolMajor;
- } else {
- ALOG_ASSERT(false, "No touch or tool axes. "
- "Size calibration should have been resolved to NONE.");
- touchMajor = 0;
- touchMinor = 0;
- toolMajor = 0;
- toolMinor = 0;
- size = 0;
- }
-
- if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
- uint32_t touchingCount =
- mCurrentRawState.rawPointerData.touchingIdBits.count();
- if (touchingCount > 1) {
- touchMajor /= touchingCount;
- touchMinor /= touchingCount;
- toolMajor /= touchingCount;
- toolMinor /= touchingCount;
- size /= touchingCount;
- }
- }
-
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
- touchMajor *= mGeometricScale;
- touchMinor *= mGeometricScale;
- toolMajor *= mGeometricScale;
- toolMinor *= mGeometricScale;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
- touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
- touchMinor = touchMajor;
- toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
- toolMinor = toolMajor;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
- touchMinor = touchMajor;
- toolMinor = toolMajor;
- }
-
- mCalibration.applySizeScaleAndBias(&touchMajor);
- mCalibration.applySizeScaleAndBias(&touchMinor);
- mCalibration.applySizeScaleAndBias(&toolMajor);
- mCalibration.applySizeScaleAndBias(&toolMinor);
- size *= mSizeScale;
- break;
- default:
- touchMajor = 0;
- touchMinor = 0;
- toolMajor = 0;
- toolMinor = 0;
- size = 0;
- break;
- }
-
- // Pressure
- float pressure;
- switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- pressure = in.pressure * mPressureScale;
- break;
- default:
- pressure = in.isHovering ? 0 : 1;
- break;
- }
-
- // Tilt and Orientation
- float tilt;
- float orientation;
- if (mHaveTilt) {
- float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
- float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
- orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
- tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
- } else {
- tilt = 0;
-
- switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- orientation = in.orientation * mOrientationScale;
- break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
- int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
- int32_t c2 = signExtendNybble(in.orientation & 0x0f);
- if (c1 != 0 || c2 != 0) {
- orientation = atan2f(c1, c2) * 0.5f;
- float confidence = hypotf(c1, c2);
- float scale = 1.0f + confidence / 16.0f;
- touchMajor *= scale;
- touchMinor /= scale;
- toolMajor *= scale;
- toolMinor /= scale;
- } else {
- orientation = 0;
- }
- break;
- }
- default:
- orientation = 0;
- }
- }
-
- // Distance
- float distance;
- switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_SCALED:
- distance = in.distance * mDistanceScale;
- break;
- default:
- distance = 0;
- }
-
- // Coverage
- int32_t rawLeft, rawTop, rawRight, rawBottom;
- switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_BOX:
- rawLeft = (in.toolMinor & 0xffff0000) >> 16;
- rawRight = in.toolMinor & 0x0000ffff;
- rawBottom = in.toolMajor & 0x0000ffff;
- rawTop = (in.toolMajor & 0xffff0000) >> 16;
- break;
- default:
- rawLeft = rawTop = rawRight = rawBottom = 0;
- break;
- }
-
- // Adjust X,Y coords for device calibration
- // TODO: Adjust coverage coords?
- float xTransformed = in.x, yTransformed = in.y;
- mAffineTransform.applyTo(xTransformed, yTransformed);
-
- // Adjust X, Y, and coverage coords for surface orientation.
- float x, y;
- float left, top, right, bottom;
-
- switch (mSurfaceOrientation) {
- case DISPLAY_ORIENTATION_90:
- x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
- left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
- top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
- orientation -= M_PI_2;
- if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
- orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- case DISPLAY_ORIENTATION_180:
- x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
- y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
- left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
- right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
- bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
- top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
- orientation -= M_PI;
- if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
- orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- case DISPLAY_ORIENTATION_270:
- x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
- y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
- right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
- bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- orientation += M_PI_2;
- if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) {
- orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
- }
- break;
- default:
- x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- break;
- }
-
- // Write output coords.
- PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
- out.clear();
- out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
- out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
- out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
- out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
- out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
- out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
- } else {
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
- }
-
- // Write output properties.
- PointerProperties& properties =
- mCurrentCookedState.cookedPointerData.pointerProperties[i];
- uint32_t id = in.id;
- properties.clear();
- properties.id = id;
- properties.toolType = in.toolType;
-
- // Write id index.
- mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
- }
-}
-
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
- PointerUsage pointerUsage) {
- if (pointerUsage != mPointerUsage) {
- abortPointerUsage(when, policyFlags);
- mPointerUsage = pointerUsage;
- }
-
- switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
- dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
- break;
- case POINTER_USAGE_STYLUS:
- dispatchPointerStylus(when, policyFlags);
- break;
- case POINTER_USAGE_MOUSE:
- dispatchPointerMouse(when, policyFlags);
- break;
- default:
- break;
- }
-}
-
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
- switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
- abortPointerGestures(when, policyFlags);
- break;
- case POINTER_USAGE_STYLUS:
- abortPointerStylus(when, policyFlags);
- break;
- case POINTER_USAGE_MOUSE:
- abortPointerMouse(when, policyFlags);
- break;
- default:
- break;
- }
-
- mPointerUsage = POINTER_USAGE_NONE;
-}
-
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
- bool isTimeout) {
- // Update current gesture coordinates.
- bool cancelPreviousGesture, finishPreviousGesture;
- bool sendEvents = preparePointerGestures(when,
- &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
- if (!sendEvents) {
- return;
- }
- if (finishPreviousGesture) {
- cancelPreviousGesture = false;
- }
-
- // Update the pointer presentation and spots.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
- if (finishPreviousGesture || cancelPreviousGesture) {
- mPointerController->clearSpots();
- }
-
- if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
- mPointerController->setSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits,
- mPointerController->getDisplayId());
- }
- } else {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
- }
-
- // Show or hide the pointer if needed.
- switch (mPointerGesture.currentGestureMode) {
- case PointerGesture::NEUTRAL:
- case PointerGesture::QUIET:
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH
- && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
- // Remind the user of where the pointer is after finishing a gesture with spots.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
- break;
- case PointerGesture::TAP:
- case PointerGesture::TAP_DRAG:
- case PointerGesture::BUTTON_CLICK_OR_DRAG:
- case PointerGesture::HOVER:
- case PointerGesture::PRESS:
- case PointerGesture::SWIPE:
- // Unfade the pointer when the current gesture manipulates the
- // area directly under the pointer.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- break;
- case PointerGesture::FREEFORM:
- // Fade the pointer when the current gesture manipulates a different
- // area and there are spots to guide the user experience.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- } else {
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- }
- break;
- }
-
- // Send events!
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentCookedState.buttonState;
-
- // Update last coordinates of pointers that have moved so that we observe the new
- // pointer positions at the same time as other pointers that have just gone up.
- bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP
- || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG
- || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
- || mPointerGesture.currentGestureMode == PointerGesture::PRESS
- || mPointerGesture.currentGestureMode == PointerGesture::SWIPE
- || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
- bool moveNeeded = false;
- if (down && !cancelPreviousGesture && !finishPreviousGesture
- && !mPointerGesture.lastGestureIdBits.isEmpty()
- && !mPointerGesture.currentGestureIdBits.isEmpty()) {
- BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value
- & mPointerGesture.lastGestureIdBits.value);
- moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- movedGestureIdBits);
- if (buttonState != mLastCookedState.buttonState) {
- moveNeeded = true;
- }
- }
-
- // Send motion events for all pointers that went up or were canceled.
- BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
- if (!dispatchedGestureIdBits.isEmpty()) {
- if (cancelPreviousGesture) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
- mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
- mPointerGesture.downTime);
-
- dispatchedGestureIdBits.clear();
- } else {
- BitSet32 upGestureIdBits;
- if (finishPreviousGesture) {
- upGestureIdBits = dispatchedGestureIdBits;
- } else {
- upGestureIdBits.value = dispatchedGestureIdBits.value
- & ~mPointerGesture.currentGestureIdBits.value;
- }
- while (!upGestureIdBits.isEmpty()) {
- uint32_t id = upGestureIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
- dispatchedGestureIdBits, id,
- 0, 0, mPointerGesture.downTime);
-
- dispatchedGestureIdBits.clearBit(id);
- }
- }
- }
-
- // Send motion events for all pointers that moved.
- if (moveNeeded) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
- mPointerGesture.downTime);
- }
-
- // Send motion events for all pointers that went down.
- if (down) {
- BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value
- & ~dispatchedGestureIdBits.value);
- while (!downGestureIdBits.isEmpty()) {
- uint32_t id = downGestureIdBits.clearFirstMarkedBit();
- dispatchedGestureIdBits.markBit(id);
-
- if (dispatchedGestureIdBits.count() == 1) {
- mPointerGesture.downTime = when;
- }
-
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
- dispatchedGestureIdBits, id,
- 0, 0, mPointerGesture.downTime);
- }
- }
-
- // Send motion events for hover.
- if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
- } else if (dispatchedGestureIdBits.isEmpty()
- && !mPointerGesture.lastGestureIdBits.isEmpty()) {
- // Synthesize a hover move event after all pointers go up to indicate that
- // the pointer is hovering again even if the user is not currently touching
- // the touch pad. This ensures that a view will receive a fresh hover enter
- // event after a tap.
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-
- const int32_t displayId = mPointerController->getDisplayId();
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
- 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- // Update state.
- mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
- if (!down) {
- mPointerGesture.lastGestureIdBits.clear();
- } else {
- mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
- for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- mPointerGesture.lastGestureProperties[index].copyFrom(
- mPointerGesture.currentGestureProperties[index]);
- mPointerGesture.lastGestureCoords[index].copyFrom(
- mPointerGesture.currentGestureCoords[index]);
- mPointerGesture.lastGestureIdToIndex[id] = index;
- }
- }
-}
-
-void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
- // Cancel previously dispatches pointers.
- if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t buttonState = mCurrentRawState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
- mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime);
- }
-
- // Reset the current pointer gesture.
- mPointerGesture.reset();
- mPointerVelocityControl.reset();
-
- // Remove any current spots.
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- mPointerController->clearSpots();
- }
-}
-
-bool TouchInputMapper::preparePointerGestures(nsecs_t when,
- bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) {
- *outCancelPreviousGesture = false;
- *outFinishPreviousGesture = false;
-
- // Handle TAP timeout.
- if (isTimeout) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Processing timeout");
-#endif
-
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
- if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- // The tap/drag timeout has not yet expired.
- getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime
- + mConfig.pointerGestureTapDragInterval);
- } else {
- // The tap is finished.
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP finished");
-#endif
- *outFinishPreviousGesture = true;
-
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
- mPointerGesture.currentGestureIdBits.clear();
-
- mPointerVelocityControl.reset();
- return true;
- }
- }
-
- // We did not handle this timeout.
- return false;
- }
-
- const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
- const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
-
- // Update the velocity tracker.
- {
- VelocityTracker::Position positions[MAX_POINTERS];
- uint32_t count = 0;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(id);
- positions[count].x = pointer.x * mPointerXMovementScale;
- positions[count].y = pointer.y * mPointerYMovementScale;
- }
- mPointerGesture.velocityTracker.addMovement(when,
- mCurrentCookedState.fingerIdBits, positions);
- }
-
- // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
- // to NEUTRAL, then we should not generate tap event.
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER
- && mPointerGesture.lastGestureMode != PointerGesture::TAP
- && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
- mPointerGesture.resetTap();
- }
-
- // Pick a new active touch id if needed.
- // Choose an arbitrary pointer that just went down, if there is one.
- // Otherwise choose an arbitrary remaining pointer.
- // This guarantees we always have an active touch id when there is at least one pointer.
- // We keep the same active touch id for as long as possible.
- int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
- int32_t activeTouchId = lastActiveTouchId;
- if (activeTouchId < 0) {
- if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
- mPointerGesture.firstTouchTime = when;
- }
- } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
- if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
- } else {
- activeTouchId = mPointerGesture.activeTouchId = -1;
- }
- }
-
- // Determine whether we are in quiet time.
- bool isQuietTime = false;
- if (activeTouchId < 0) {
- mPointerGesture.resetQuietTime();
- } else {
- isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
- if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
- || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
- || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)
- && currentFingerCount < 2) {
- // Enter quiet time when exiting swipe or freeform state.
- // This is to prevent accidentally entering the hover state and flinging the
- // pointer when finishing a swipe and there is still one pointer left onscreen.
- isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
- && currentFingerCount >= 2
- && !isPointerDown(mCurrentRawState.buttonState)) {
- // Enter quiet time when releasing the button and there are still two or more
- // fingers down. This may indicate that one finger was used to press the button
- // but it has not gone up yet.
- isQuietTime = true;
- }
- if (isQuietTime) {
- mPointerGesture.quietTime = when;
- }
- }
- }
-
- // Switch states based on button and pointer state.
- if (isQuietTime) {
- // Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
- ALOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
- + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
- *outFinishPreviousGesture = true;
- }
-
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::QUIET;
- mPointerGesture.currentGestureIdBits.clear();
-
- mPointerVelocityControl.reset();
- } else if (isPointerDown(mCurrentRawState.buttonState)) {
- // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
- // The pointer follows the active touch point.
- // Emit DOWN, MOVE, UP events at the pointer location.
- //
- // Only the active touch matters; other fingers are ignored. This policy helps
- // to handle the case where the user places a second finger on the touch pad
- // to apply the necessary force to depress an integrated button below the surface.
- // We don't want the second finger to be delivered to applications.
- //
- // For this to work well, we need to make sure to track the pointer that is really
- // active. If the user first puts one finger down to click then adds another
- // finger to drag then the active pointer should switch to the finger that is
- // being dragged.
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
- "currentFingerCount=%d", activeTouchId, currentFingerCount);
-#endif
- // Reset state when just starting.
- if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
- *outFinishPreviousGesture = true;
- mPointerGesture.activeGestureId = 0;
- }
-
- // Switch pointers if needed.
- // Find the fastest pointer and follow it.
- if (activeTouchId >= 0 && currentFingerCount > 1) {
- int32_t bestId = -1;
- float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- float vx, vy;
- if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
- float speed = hypotf(vx, vy);
- if (speed > bestSpeed) {
- bestId = id;
- bestSpeed = speed;
- }
- }
- }
- if (bestId >= 0 && bestId != activeTouchId) {
- mPointerGesture.activeTouchId = activeTouchId = bestId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
- "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
-#endif
- }
- }
-
- float deltaX = 0, deltaY = 0;
- if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
- const RawPointerData::Pointer& currentPointer =
- mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
- const RawPointerData::Pointer& lastPointer =
- mLastRawState.rawPointerData.pointerForId(activeTouchId);
- deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
- deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- // Move the pointer using a relative motion.
- // When using spots, the click will occur at the position of the anchor
- // spot and all other spots will move there.
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (currentFingerCount == 0) {
- // Case 3. No fingers down and button is not pressed. (NEUTRAL)
- if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
- *outFinishPreviousGesture = true;
- }
-
- // Watch for taps coming out of HOVER or TAP_DRAG mode.
- // Checking for taps after TAP_DRAG allows us to detect double-taps.
- bool tapped = false;
- if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
- || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
- && lastFingerCount == 1) {
- if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
- && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP");
-#endif
-
- mPointerGesture.tapUpTime = when;
- getContext()->requestTimeoutAtTime(when
- + mConfig.pointerGestureTapDragInterval);
-
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.currentGestureMode = PointerGesture::TAP;
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(
- mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[
- mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id =
- mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(
- AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
- tapped = true;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
- }
- } else {
-#if DEBUG_GESTURES
- if (mPointerGesture.tapDownTime != LLONG_MIN) {
- ALOGD("Gestures: Not a TAP, %0.3fms since down",
- (when - mPointerGesture.tapDownTime) * 0.000001f);
- } else {
- ALOGD("Gestures: Not a TAP, incompatible mode transitions");
- }
-#endif
- }
- }
-
- mPointerVelocityControl.reset();
-
- if (!tapped) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: NEUTRAL");
-#endif
- mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
- mPointerGesture.currentGestureIdBits.clear();
- }
- } else if (currentFingerCount == 1) {
- // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
- // The pointer follows the active touch point.
- // When in HOVER, emit HOVER_MOVE events at the pointer location.
- // When in TAP_DRAG, emit MOVE events at the pointer location.
- ALOG_ASSERT(activeTouchId >= 0);
-
- mPointerGesture.currentGestureMode = PointerGesture::HOVER;
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
- if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- float x, y;
- mPointerController->getPosition(&x, &y);
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
- && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
- }
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
- (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
- }
- } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
- }
-
- float deltaX = 0, deltaY = 0;
- if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
- const RawPointerData::Pointer& currentPointer =
- mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
- const RawPointerData::Pointer& lastPointer =
- mLastRawState.rawPointerData.pointerForId(activeTouchId);
- deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
- deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- // Move the pointer using a relative motion.
- // When using spots, the hover or drag will occur at the position of the anchor spot.
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- bool down;
- if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP_DRAG");
-#endif
- down = true;
- } else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: HOVER");
-#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
- *outFinishPreviousGesture = true;
- }
- mPointerGesture.activeGestureId = 0;
- down = false;
- }
-
- float x, y;
- mPointerController->getPosition(&x, &y);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- down ? 1.0f : 0.0f);
-
- if (lastFingerCount == 0 && currentFingerCount != 0) {
- mPointerGesture.resetTap();
- mPointerGesture.tapDownTime = when;
- mPointerGesture.tapX = x;
- mPointerGesture.tapY = y;
- }
- } else {
- // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
- // We need to provide feedback for each finger that goes down so we cannot wait
- // for the fingers to move before deciding what to do.
- //
- // The ambiguous case is deciding what to do when there are two fingers down but they
- // have not moved enough to determine whether they are part of a drag or part of a
- // freeform gesture, or just a press or long-press at the pointer location.
- //
- // When there are two fingers we start with the PRESS hypothesis and we generate a
- // down at the pointer location.
- //
- // When the two fingers move enough or when additional fingers are added, we make
- // a decision to transition into SWIPE or FREEFORM mode accordingly.
- ALOG_ASSERT(activeTouchId >= 0);
-
- bool settled = when >= mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
- && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
- && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
- *outFinishPreviousGesture = true;
- } else if (!settled && currentFingerCount > lastFingerCount) {
- // Additional pointers have gone down but not yet settled.
- // Reset the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval - when)
- * 0.000001f);
-#endif
- *outCancelPreviousGesture = true;
- } else {
- // Continue previous gesture.
- mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
- }
-
- if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::PRESS;
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.referenceIdBits.clear();
- mPointerVelocityControl.reset();
-
- // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
- + mConfig.pointerGestureMultitouchSettleInterval - when)
- * 0.000001f);
-#endif
- mCurrentRawState.rawPointerData.getCentroidOfTouchingPointers(
- &mPointerGesture.referenceTouchX,
- &mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
-
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value
- & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
-
- // Add delta for all fingers and calculate a common movement delta.
- float commonDeltaX = 0, commonDeltaY = 0;
- BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value
- & mCurrentCookedState.fingerIdBits.value);
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- bool first = (idBits == commonIdBits);
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
- const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
- }
-
- // Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
- float dist[MAX_POINTER_ID + 1];
- int32_t distOverThreshold = 0;
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- dist[id] = hypotf(delta.dx * mPointerXZoomScale,
- delta.dy * mPointerYZoomScale);
- if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
- distOverThreshold += 1;
- }
- }
-
- // Only transition when at least two pointers have moved further than
- // the minimum distance threshold.
- if (distOverThreshold >= 2) {
- if (currentFingerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else {
- // There are exactly two pointers.
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- uint32_t id1 = idBits.clearFirstMarkedBit();
- uint32_t id2 = idBits.firstMarkedBit();
- const RawPointerData::Pointer& p1 =
- mCurrentRawState.rawPointerData.pointerForId(id1);
- const RawPointerData::Pointer& p2 =
- mCurrentRawState.rawPointerData.pointerForId(id2);
- float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
- if (mutualDistance > mPointerGestureMaxSwipeWidth) {
- // There are two pointers but they are too far apart for a SWIPE,
- // switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- float dist1 = dist[id1];
- float dist2 = dist[id2];
- if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
- && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
- // Calculate the dot product of the displacement vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
- PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
- PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
- float dx1 = delta1.dx * mPointerXZoomScale;
- float dy1 = delta1.dy * mPointerYZoomScale;
- float dx2 = delta2.dx * mPointerXZoomScale;
- float dy2 = delta2.dy * mPointerYZoomScale;
- float dot = dx1 * dx2 + dy1 * dy2;
- float cosine = dot / (dist1 * dist2); // denominator always > 0
- if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance,
- dist2, mConfig.pointerGestureMultitouchMinDistance,
- cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance,
- dist2, mConfig.pointerGestureMultitouchMinDistance,
- cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- }
- }
- }
- }
- }
- } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
- // Switch from SWIPE to FREEFORM if additional pointers go down.
- // Cancel previous gesture.
- if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- }
- }
-
- // Move the reference points based on the overall group motion of the fingers
- // except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
- && (commonDeltaX || commonDeltaY)) {
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
-
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mPointerXMovementScale;
- commonDeltaY *= mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
- mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
- }
-
- // Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS
- || mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
- // PRESS or SWIPE mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
- // FREEFORM mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
-
- BitSet32 mappedTouchIdBits;
- BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
- // Initially, assign the active gesture id to the active touch point
- // if there is one. No other touch id bits are mapped yet.
- if (!*outCancelPreviousGesture) {
- mappedTouchIdBits.markBit(activeTouchId);
- usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
- mPointerGesture.activeGestureId;
- } else {
- mPointerGesture.activeGestureId = -1;
- }
- } else {
- // Otherwise, assume we mapped all touches from the previous frame.
- // Reuse all mappings that are still applicable.
- mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value
- & mCurrentCookedState.fingerIdBits.value;
- usedGestureIdBits = mPointerGesture.lastGestureIdBits;
-
- // Check whether we need to choose a new active gesture id because the
- // current went went up.
- for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value
- & ~mCurrentCookedState.fingerIdBits.value);
- !upTouchIdBits.isEmpty(); ) {
- uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
- uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
- if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
- mPointerGesture.activeGestureId = -1;
- break;
- }
- }
- }
-
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM follow up "
- "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
- "activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-#endif
-
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- for (uint32_t i = 0; i < currentFingerCount; i++) {
- uint32_t touchId = idBits.clearFirstMarkedBit();
- uint32_t gestureId;
- if (!mappedTouchIdBits.hasBit(touchId)) {
- gestureId = usedGestureIdBits.markFirstUnmarkedBit();
- mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
- } else {
- gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
- }
- mPointerGesture.currentGestureIdBits.markBit(gestureId);
- mPointerGesture.currentGestureIdToIndex[gestureId] = i;
-
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(touchId);
- float deltaX = (pointer.x - mPointerGesture.referenceTouchX)
- * mPointerXZoomScale;
- float deltaY = (pointer.y - mPointerGesture.referenceTouchY)
- * mPointerYZoomScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-
- mPointerGesture.currentGestureProperties[i].clear();
- mPointerGesture.currentGestureProperties[i].id = gestureId;
- mPointerGesture.currentGestureProperties[i].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[i].clear();
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX);
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY);
- mPointerGesture.currentGestureCoords[i].setAxisValue(
- AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- }
-
- if (mPointerGesture.activeGestureId < 0) {
- mPointerGesture.activeGestureId =
- mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM new "
- "activeGestureId=%d", mPointerGesture.activeGestureId);
-#endif
- }
- }
- }
-
- mPointerController->setButtonState(mCurrentRawState.buttonState);
-
-#if DEBUG_GESTURES
- ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
- "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
- "lastGestureMode=%d, lastGestureIdBits=0x%08x",
- toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
- mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
- mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
- for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
- ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType,
- coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
- for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
- ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType,
- coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
-#endif
- return true;
-}
-
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- bool down, hovering;
- if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
- uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
- uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
- float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
- float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
- mPointerController->setPosition(x, y);
-
- hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
- down = !hovering;
-
- mPointerController->getPosition(&x, &y);
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[index]);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerSimple.currentProperties.id = 0;
- mPointerSimple.currentProperties.toolType =
- mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType;
- } else {
- down = false;
- hovering = false;
- }
-
- dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
-}
-
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- bool down, hovering;
- if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
- uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
- uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
- float deltaX = 0, deltaY = 0;
- if (mLastCookedState.mouseIdBits.hasBit(id)) {
- uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
- deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x
- - mLastRawState.rawPointerData.pointers[lastIndex].x)
- * mPointerXMovementScale;
- deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y
- - mLastRawState.rawPointerData.pointers[lastIndex].y)
- * mPointerYMovementScale;
-
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
- mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- mPointerController->move(deltaX, deltaY);
- } else {
- mPointerVelocityControl.reset();
- }
-
- down = isPointerDown(mCurrentRawState.buttonState);
- hovering = !down;
-
- float x, y;
- mPointerController->getPosition(&x, &y);
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- hovering ? 0.0f : 1.0f);
- mPointerSimple.currentProperties.id = 0;
- mPointerSimple.currentProperties.toolType =
- mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
- } else {
- mPointerVelocityControl.reset();
-
- down = false;
- hovering = false;
- }
-
- dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
-
- mPointerVelocityControl.reset();
-}
-
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
- bool down, bool hovering) {
- int32_t metaState = getContext()->getGlobalMetaState();
- int32_t displayId = mViewport.displayId;
-
- if (down || hovering) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
- mPointerController->clearSpots();
- mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
- } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
- displayId = mPointerController->getDisplayId();
-
- float xCursorPosition;
- float yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
-
- if (mPointerSimple.down && !down) {
- mPointerSimple.down = false;
-
- // Send up.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- if (mPointerSimple.hovering && !hovering) {
- mPointerSimple.hovering = false;
-
- // Send hover exit.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
- metaState, mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- if (down) {
- if (!mPointerSimple.down) {
- mPointerSimple.down = true;
- mPointerSimple.downTime = when;
-
- // Send down.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- // Send move.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- if (hovering) {
- if (!mPointerSimple.hovering) {
- mPointerSimple.hovering = true;
-
- // Send hover enter.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- // Send hover move.
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
- float vscroll = mCurrentRawState.rawVScroll;
- float hscroll = mCurrentRawState.rawHScroll;
- mWheelYVelocityControl.move(when, nullptr, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, nullptr);
-
- // Send scroll.
- PointerCoords pointerCoords;
- pointerCoords.copyFrom(mPointerSimple.currentCoords);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
- }
-
- // Save state.
- if (down || hovering) {
- mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
- mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
- } else {
- mPointerSimple.reset();
- }
-}
-
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- dispatchPointerSimple(when, policyFlags, false, false);
-}
-
-void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties,
- const PointerCoords* coords, const uint32_t* idToIndex,
- BitSet32 idBits, int32_t changedId, float xPrecision,
- float yPrecision, nsecs_t downTime) {
- PointerCoords pointerCoords[MAX_POINTERS];
- PointerProperties pointerProperties[MAX_POINTERS];
- uint32_t pointerCount = 0;
- while (!idBits.isEmpty()) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = idToIndex[id];
- pointerProperties[pointerCount].copyFrom(properties[index]);
- pointerCoords[pointerCount].copyFrom(coords[index]);
-
- if (changedId >= 0 && id == uint32_t(changedId)) {
- action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- }
-
- pointerCount += 1;
- }
-
- ALOG_ASSERT(pointerCount != 0);
-
- if (changedId >= 0 && pointerCount == 1) {
- // Replace initial down and final up action.
- // We can compare the action without masking off the changed pointer index
- // because we know the index is 0.
- if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
- action = AMOTION_EVENT_ACTION_DOWN;
- } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
- action = AMOTION_EVENT_ACTION_UP;
- } else {
- // Can't happen.
- ALOG_ASSERT(false);
- }
- }
- float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
- }
- const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
- const int32_t deviceId = getDeviceId();
- std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
- std::for_each(frames.begin(), frames.end(),
- [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId,
- policyFlags, action, actionButton, flags, metaState, buttonState,
- MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
- pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
- downTime, std::move(frames));
- getListener()->notifyMotion(&args);
-}
-
-bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
- const PointerCoords* inCoords, const uint32_t* inIdToIndex,
- PointerProperties* outProperties, PointerCoords* outCoords, const uint32_t* outIdToIndex,
- BitSet32 idBits) const {
- bool changed = false;
- while (!idBits.isEmpty()) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t inIndex = inIdToIndex[id];
- uint32_t outIndex = outIdToIndex[id];
-
- const PointerProperties& curInProperties = inProperties[inIndex];
- const PointerCoords& curInCoords = inCoords[inIndex];
- PointerProperties& curOutProperties = outProperties[outIndex];
- PointerCoords& curOutCoords = outCoords[outIndex];
-
- if (curInProperties != curOutProperties) {
- curOutProperties.copyFrom(curInProperties);
- changed = true;
- }
-
- if (curInCoords != curOutCoords) {
- curOutCoords.copyFrom(curInCoords);
- changed = true;
- }
- }
- return changed;
-}
-
-void TouchInputMapper::fadePointer() {
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
- }
-}
-
-void TouchInputMapper::cancelTouch(nsecs_t when) {
- abortPointerUsage(when, 0 /*policyFlags*/);
- abortTouches(when, 0 /* policyFlags*/);
-}
-
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
- const float scaledX = x * mXScale;
- const float scaledY = y * mYScale;
- return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue
- && scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth
- && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue
- && scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight;
-}
-
-const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
-
- for (const VirtualKey& virtualKey: mVirtualKeys) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y,
- virtualKey.keyCode, virtualKey.scanCode,
- virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
-#endif
-
- if (virtualKey.isHit(x, y)) {
- return & virtualKey;
- }
- }
-
- return nullptr;
-}
-
-void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
- uint32_t currentPointerCount = current->rawPointerData.pointerCount;
- uint32_t lastPointerCount = last->rawPointerData.pointerCount;
-
- current->rawPointerData.clearIdBits();
-
- if (currentPointerCount == 0) {
- // No pointers to assign.
- return;
- }
-
- if (lastPointerCount == 0) {
- // All pointers are new.
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- uint32_t id = i;
- current->rawPointerData.pointers[i].id = id;
- current->rawPointerData.idToIndex[id] = i;
- current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
- }
- return;
- }
-
- if (currentPointerCount == 1 && lastPointerCount == 1
- && current->rawPointerData.pointers[0].toolType
- == last->rawPointerData.pointers[0].toolType) {
- // Only one pointer and no change in count so it must have the same id as before.
- uint32_t id = last->rawPointerData.pointers[0].id;
- current->rawPointerData.pointers[0].id = id;
- current->rawPointerData.idToIndex[id] = 0;
- current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
- return;
- }
-
- // General case.
- // We build a heap of squared euclidean distances between current and last pointers
- // associated with the current and last pointer indices. Then, we find the best
- // match (by distance) for each current pointer.
- // The pointers must have the same tool type but it is possible for them to
- // transition from hovering to touching or vice-versa while retaining the same id.
- PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
- uint32_t heapSize = 0;
- for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
- currentPointerIndex++) {
- for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
- lastPointerIndex++) {
- const RawPointerData::Pointer& currentPointer =
- current->rawPointerData.pointers[currentPointerIndex];
- const RawPointerData::Pointer& lastPointer =
- last->rawPointerData.pointers[lastPointerIndex];
- if (currentPointer.toolType == lastPointer.toolType) {
- int64_t deltaX = currentPointer.x - lastPointer.x;
- int64_t deltaY = currentPointer.y - lastPointer.y;
-
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
- // Insert new element into the heap (sift up).
- heap[heapSize].currentPointerIndex = currentPointerIndex;
- heap[heapSize].lastPointerIndex = lastPointerIndex;
- heap[heapSize].distance = distance;
- heapSize += 1;
- }
- }
- }
-
- // Heapify
- for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
- startIndex -= 1;
- for (uint32_t parentIndex = startIndex; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
-
- // Pull matches out by increasing order of distance.
- // To avoid reassigning pointers that have already been matched, the loop keeps track
- // of which last and current pointers have been matched using the matchedXXXBits variables.
- // It also tracks the used pointer id bits.
- BitSet32 matchedLastBits(0);
- BitSet32 matchedCurrentBits(0);
- BitSet32 usedIdBits(0);
- bool first = true;
- for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
- while (heapSize > 0) {
- if (first) {
- // The first time through the loop, we just consume the root element of
- // the heap (the one with smallest distance).
- first = false;
- } else {
- // Previous iterations consumed the root element of the heap.
- // Pop root element off of the heap (sift down).
- heap[0] = heap[heapSize];
- for (uint32_t parentIndex = 0; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
- }
-
- heapSize -= 1;
-
- uint32_t currentPointerIndex = heap[0].currentPointerIndex;
- if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
- uint32_t lastPointerIndex = heap[0].lastPointerIndex;
- if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
- matchedCurrentBits.markBit(currentPointerIndex);
- matchedLastBits.markBit(lastPointerIndex);
-
- uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
- current->rawPointerData.pointers[currentPointerIndex].id = id;
- current->rawPointerData.idToIndex[id] = currentPointerIndex;
- current->rawPointerData.markIdBit(id,
- current->rawPointerData.isHovering(currentPointerIndex));
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32
- ", id=%" PRIu32 ", distance=%" PRIu64,
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
- break;
- }
- }
-
- // Assign fresh ids to pointers that were not matched in the process.
- for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
- uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
- uint32_t id = usedIdBits.markFirstUnmarkedBit();
-
- current->rawPointerData.pointers[currentPointerIndex].id = id;
- current->rawPointerData.idToIndex[id] = currentPointerIndex;
- current->rawPointerData.markIdBit(id,
- current->rawPointerData.isHovering(currentPointerIndex));
-
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
-#endif
- }
-}
-
-int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
- if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
- return AKEY_STATE_VIRTUAL;
- }
-
- for (const VirtualKey& virtualKey : mVirtualKeys) {
- if (virtualKey.keyCode == keyCode) {
- return AKEY_STATE_UP;
- }
- }
-
- return AKEY_STATE_UNKNOWN;
-}
-
-int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
- if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
- return AKEY_STATE_VIRTUAL;
- }
-
- for (const VirtualKey& virtualKey : mVirtualKeys) {
- if (virtualKey.scanCode == scanCode) {
- return AKEY_STATE_UP;
- }
- }
-
- return AKEY_STATE_UNKNOWN;
-}
-
-bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- for (const VirtualKey& virtualKey : mVirtualKeys) {
- for (size_t i = 0; i < numCodes; i++) {
- if (virtualKey.keyCode == keyCodes[i]) {
- outFlags[i] = 1;
- }
- }
- }
-
- return true;
-}
-
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- return std::make_optional(mPointerController->getDisplayId());
- } else {
- return std::make_optional(mViewport.displayId);
- }
- }
- return std::nullopt;
-}
-
-// --- SingleTouchInputMapper ---
-
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
- TouchInputMapper(device) {
-}
-
-SingleTouchInputMapper::~SingleTouchInputMapper() {
-}
-
-void SingleTouchInputMapper::reset(nsecs_t when) {
- mSingleTouchMotionAccumulator.reset(getDevice());
-
- TouchInputMapper::reset(when);
-}
-
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
-
- mSingleTouchMotionAccumulator.process(rawEvent);
-}
-
-void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
- if (mTouchButtonAccumulator.isToolActive()) {
- outState->rawPointerData.pointerCount = 1;
- outState->rawPointerData.idToIndex[0] = 0;
-
- bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
- && (mTouchButtonAccumulator.isHovering()
- || (mRawPointerAxes.pressure.valid
- && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
- outState->rawPointerData.markIdBit(0, isHovering);
-
- RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0];
- outPointer.id = 0;
- outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
- outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
- outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
- outPointer.touchMajor = 0;
- outPointer.touchMinor = 0;
- outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
- outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
- outPointer.orientation = 0;
- outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();
- outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();
- outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();
- outPointer.toolType = mTouchButtonAccumulator.getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- outPointer.isHovering = isHovering;
- }
-}
-
-void SingleTouchInputMapper::configureRawPointerAxes() {
- TouchInputMapper::configureRawPointerAxes();
-
- getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
- getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
-}
-
-bool SingleTouchInputMapper::hasStylus() const {
- return mTouchButtonAccumulator.hasStylus();
-}
-
-
-// --- MultiTouchInputMapper ---
-
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
- TouchInputMapper(device) {
-}
-
-MultiTouchInputMapper::~MultiTouchInputMapper() {
-}
-
-void MultiTouchInputMapper::reset(nsecs_t when) {
- mMultiTouchMotionAccumulator.reset(getDevice());
-
- mPointerIdBits.clear();
-
- TouchInputMapper::reset(when);
-}
-
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
-
- mMultiTouchMotionAccumulator.process(rawEvent);
-}
-
-void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
- size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
- size_t outCount = 0;
- BitSet32 newPointerIdBits;
- mHavePointerIds = true;
-
- for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
- const MultiTouchMotionAccumulator::Slot* inSlot =
- mMultiTouchMotionAccumulator.getSlot(inIndex);
- if (!inSlot->isInUse()) {
- continue;
- }
-
- if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
- ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
- "ignoring the rest.",
- getDeviceName().c_str(), MAX_POINTERS);
-#endif
- break; // too many fingers!
- }
-
- RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
- outPointer.x = inSlot->getX();
- outPointer.y = inSlot->getY();
- outPointer.pressure = inSlot->getPressure();
- outPointer.touchMajor = inSlot->getTouchMajor();
- outPointer.touchMinor = inSlot->getTouchMinor();
- outPointer.toolMajor = inSlot->getToolMajor();
- outPointer.toolMinor = inSlot->getToolMinor();
- outPointer.orientation = inSlot->getOrientation();
- outPointer.distance = inSlot->getDistance();
- outPointer.tiltX = 0;
- outPointer.tiltY = 0;
-
- outPointer.toolType = inSlot->getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = mTouchButtonAccumulator.getToolType();
- if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- }
- }
-
- bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
- && (mTouchButtonAccumulator.isHovering()
- || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
- outPointer.isHovering = isHovering;
-
- // Assign pointer id using tracking id if available.
- if (mHavePointerIds) {
- int32_t trackingId = inSlot->getTrackingId();
- int32_t id = -1;
- if (trackingId >= 0) {
- for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
- uint32_t n = idBits.clearFirstMarkedBit();
- if (mPointerTrackingIdMap[n] == trackingId) {
- id = n;
- }
- }
-
- if (id < 0 && !mPointerIdBits.isFull()) {
- id = mPointerIdBits.markFirstUnmarkedBit();
- mPointerTrackingIdMap[id] = trackingId;
- }
- }
- if (id < 0) {
- mHavePointerIds = false;
- outState->rawPointerData.clearIdBits();
- newPointerIdBits.clear();
- } else {
- outPointer.id = id;
- outState->rawPointerData.idToIndex[id] = outCount;
- outState->rawPointerData.markIdBit(id, isHovering);
- newPointerIdBits.markBit(id);
- }
- }
- outCount += 1;
- }
-
- outState->rawPointerData.pointerCount = outCount;
- mPointerIdBits = newPointerIdBits;
-
- mMultiTouchMotionAccumulator.finishSync();
-}
-
-void MultiTouchInputMapper::configureRawPointerAxes() {
- TouchInputMapper::configureRawPointerAxes();
-
- getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
- getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
- getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
- getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
-
- if (mRawPointerAxes.trackingId.valid
- && mRawPointerAxes.slot.valid
- && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
- size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
- if (slotCount > MAX_SLOTS) {
- ALOGW("MultiTouch Device %s reported %zu slots but the framework "
- "only supports a maximum of %zu slots at this time.",
- getDeviceName().c_str(), slotCount, MAX_SLOTS);
- slotCount = MAX_SLOTS;
- }
- mMultiTouchMotionAccumulator.configure(getDevice(),
- slotCount, true /*usingSlotsProtocol*/);
- } else {
- mMultiTouchMotionAccumulator.configure(getDevice(),
- MAX_POINTERS, false /*usingSlotsProtocol*/);
- }
-}
-
-bool MultiTouchInputMapper::hasStylus() const {
- return mMultiTouchMotionAccumulator.hasStylus()
- || mTouchButtonAccumulator.hasStylus();
-}
-
-// --- ExternalStylusInputMapper
-
-ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) :
- InputMapper(device) {
-
-}
-
-uint32_t ExternalStylusInputMapper::getSources() {
- return AINPUT_SOURCE_STYLUS;
-}
-
-void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS,
- 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-}
-
-void ExternalStylusInputMapper::dump(std::string& dump) {
- dump += INDENT2 "External Stylus Input Mapper:\n";
- dump += INDENT3 "Raw Stylus Axes:\n";
- dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure");
- dump += INDENT3 "Stylus State:\n";
- dumpStylusState(dump, mStylusState);
-}
-
-void ExternalStylusInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
- mTouchButtonAccumulator.configure(getDevice());
-}
-
-void ExternalStylusInputMapper::reset(nsecs_t when) {
- InputDevice* device = getDevice();
- mSingleTouchMotionAccumulator.reset(device);
- mTouchButtonAccumulator.reset(device);
- InputMapper::reset(when);
-}
-
-void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
- mSingleTouchMotionAccumulator.process(rawEvent);
- mTouchButtonAccumulator.process(rawEvent);
-
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
-}
-
-void ExternalStylusInputMapper::sync(nsecs_t when) {
- mStylusState.clear();
-
- mStylusState.when = when;
-
- mStylusState.toolType = mTouchButtonAccumulator.getToolType();
- if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
- }
-
- int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
- if (mRawPressureAxis.valid) {
- mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
- } else if (mTouchButtonAccumulator.isToolActive()) {
- mStylusState.pressure = 1.0f;
- } else {
- mStylusState.pressure = 0.0f;
- }
-
- mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
-
- mContext->dispatchExternalStylusState(mStylusState);
-}
-
-
-// --- JoystickInputMapper ---
-
-JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
- InputMapper(device) {
-}
-
-JoystickInputMapper::~JoystickInputMapper() {
-}
-
-uint32_t JoystickInputMapper::getSources() {
- return AINPUT_SOURCE_JOYSTICK;
-}
-
-void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- for (size_t i = 0; i < mAxes.size(); i++) {
- const Axis& axis = mAxes.valueAt(i);
- addMotionRange(axis.axisInfo.axis, axis, info);
-
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- addMotionRange(axis.axisInfo.highAxis, axis, info);
-
- }
- }
-}
-
-void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis,
- InputDeviceInfo* info) {
- info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- /* In order to ease the transition for developers from using the old axes
- * to the newer, more semantically correct axes, we'll continue to register
- * the old axes as duplicates of their corresponding new ones. */
- int32_t compatAxis = getCompatAxis(axisId);
- if (compatAxis >= 0) {
- info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- }
-}
-
-/* A mapping from axes the joystick actually has to the axes that should be
- * artificially created for compatibility purposes.
- * Returns -1 if no compatibility axis is needed. */
-int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
- switch(axis) {
- case AMOTION_EVENT_AXIS_LTRIGGER:
- return AMOTION_EVENT_AXIS_BRAKE;
- case AMOTION_EVENT_AXIS_RTRIGGER:
- return AMOTION_EVENT_AXIS_GAS;
- }
- return -1;
-}
-
-void JoystickInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Joystick Input Mapper:\n";
-
- dump += INDENT3 "Axes:\n";
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- const char* label = getAxisLabel(axis.axisInfo.axis);
- if (label) {
- dump += StringPrintf(INDENT4 "%s", label);
- } else {
- dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
- }
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- label = getAxisLabel(axis.axisInfo.highAxis);
- if (label) {
- dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
- } else {
- dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis,
- axis.axisInfo.splitValue);
- }
- } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
- dump += " (invert)";
- }
-
- dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
- axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
- dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, "
- "highScale=%0.5f, highOffset=%0.5f\n",
- axis.scale, axis.offset, axis.highScale, axis.highOffset);
- dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
- "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
- mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
- axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution);
- }
-}
-
-void JoystickInputMapper::configure(nsecs_t when,
- const InputReaderConfiguration* config, uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- // Collect all axes.
- for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
- if (!(getAbsAxisUsage(abs, getDevice()->getClasses())
- & INPUT_DEVICE_CLASS_JOYSTICK)) {
- continue; // axis must be claimed by a different device
- }
-
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
- // Map axis.
- AxisInfo axisInfo;
- bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
- if (!explicitlyMapped) {
- // Axis is not explicitly mapped, will choose a generic axis later.
- axisInfo.mode = AxisInfo::MODE_NORMAL;
- axisInfo.axis = -1;
- }
-
- // Apply flat override.
- int32_t rawFlat = axisInfo.flatOverride < 0
- ? rawAxisInfo.flat : axisInfo.flatOverride;
-
- // Calculate scaling factors and limits.
- Axis axis;
- if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
- float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
- float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, 0.0f, highScale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else if (isCenteredAxis(axisInfo.axis)) {
- float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, offset, scale, offset,
- -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else {
- float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
- scale, 0.0f, scale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- }
-
- // To eliminate noise while the joystick is at rest, filter out small variations
- // in axis values up front.
- axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
- mAxes.add(abs, axis);
- }
- }
-
- // If there are too many axes, start dropping them.
- // Prefer to keep explicitly mapped axes.
- if (mAxes.size() > PointerCoords::MAX_AXES) {
- ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
- getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
- pruneAxes(true);
- pruneAxes(false);
- }
-
- // Assign generic axis ids to remaining axes.
- int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- if (axis.axisInfo.axis < 0) {
- while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
- && haveAxis(nextGenericAxisId)) {
- nextGenericAxisId += 1;
- }
-
- if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
- axis.axisInfo.axis = nextGenericAxisId;
- nextGenericAxisId += 1;
- } else {
- ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
- "have already been assigned to other axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i--);
- numAxes -= 1;
- }
- }
- }
- }
-}
-
-bool JoystickInputMapper::haveAxis(int32_t axisId) {
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- if (axis.axisInfo.axis == axisId
- || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT
- && axis.axisInfo.highAxis == axisId)) {
- return true;
- }
- }
- return false;
-}
-
-void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
- size_t i = mAxes.size();
- while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
- if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
- continue;
- }
- ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i);
- }
-}
-
-bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- case AMOTION_EVENT_AXIS_Y:
- case AMOTION_EVENT_AXIS_Z:
- case AMOTION_EVENT_AXIS_RX:
- case AMOTION_EVENT_AXIS_RY:
- case AMOTION_EVENT_AXIS_RZ:
- case AMOTION_EVENT_AXIS_HAT_X:
- case AMOTION_EVENT_AXIS_HAT_Y:
- case AMOTION_EVENT_AXIS_ORIENTATION:
- case AMOTION_EVENT_AXIS_RUDDER:
- case AMOTION_EVENT_AXIS_WHEEL:
- return true;
- default:
- return false;
- }
-}
-
-void JoystickInputMapper::reset(nsecs_t when) {
- // Recenter all axes.
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- axis.resetValue();
- }
-
- InputMapper::reset(when);
-}
-
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_ABS: {
- ssize_t index = mAxes.indexOfKey(rawEvent->code);
- if (index >= 0) {
- Axis& axis = mAxes.editValueAt(index);
- float newValue, highNewValue;
- switch (axis.axisInfo.mode) {
- case AxisInfo::MODE_INVERT:
- newValue = (axis.rawAxisInfo.maxValue - rawEvent->value)
- * axis.scale + axis.offset;
- highNewValue = 0.0f;
- break;
- case AxisInfo::MODE_SPLIT:
- if (rawEvent->value < axis.axisInfo.splitValue) {
- newValue = (axis.axisInfo.splitValue - rawEvent->value)
- * axis.scale + axis.offset;
- highNewValue = 0.0f;
- } else if (rawEvent->value > axis.axisInfo.splitValue) {
- newValue = 0.0f;
- highNewValue = (rawEvent->value - axis.axisInfo.splitValue)
- * axis.highScale + axis.highOffset;
- } else {
- newValue = 0.0f;
- highNewValue = 0.0f;
- }
- break;
- default:
- newValue = rawEvent->value * axis.scale + axis.offset;
- highNewValue = 0.0f;
- break;
- }
- axis.newValue = newValue;
- axis.highNewValue = highNewValue;
- }
- break;
- }
-
- case EV_SYN:
- switch (rawEvent->code) {
- case SYN_REPORT:
- sync(rawEvent->when, false /*force*/);
- break;
- }
- break;
- }
-}
-
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
- if (!filterAxes(force)) {
- return;
- }
-
- int32_t metaState = mContext->getGlobalMetaState();
- int32_t buttonState = 0;
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
- axis.highCurrentValue);
- }
- }
-
- // Moving a joystick axis should not wake the device because joysticks can
- // be fairly noisy even when not in use. On the other hand, pushing a gamepad
- // button will likely wake the device.
- // TODO: Use the input device configuration to control this behavior more finely.
- uint32_t policyFlags = 0;
-
- NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
- AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
- AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &pointerProperties, &pointerCoords, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
-}
-
-void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords,
- int32_t axis, float value) {
- pointerCoords->setAxisValue(axis, value);
- /* In order to ease the transition for developers from using the old axes
- * to the newer, more semantically correct axes, we'll continue to produce
- * values for the old axes as mirrors of the value of their corresponding
- * new axes. */
- int32_t compatAxis = getCompatAxis(axis);
- if (compatAxis >= 0) {
- pointerCoords->setAxisValue(compatAxis, value);
- }
-}
-
-bool JoystickInputMapper::filterAxes(bool force) {
- bool atLeastOneSignificantChange = force;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- if (force || hasValueChangedSignificantly(axis.filter,
- axis.newValue, axis.currentValue, axis.min, axis.max)) {
- axis.currentValue = axis.newValue;
- atLeastOneSignificantChange = true;
- }
- if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- if (force || hasValueChangedSignificantly(axis.filter,
- axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) {
- axis.highCurrentValue = axis.highNewValue;
- atLeastOneSignificantChange = true;
- }
- }
- }
- return atLeastOneSignificantChange;
-}
-
-bool JoystickInputMapper::hasValueChangedSignificantly(
- float filter, float newValue, float currentValue, float min, float max) {
- if (newValue != currentValue) {
- // Filter out small changes in value unless the value is converging on the axis
- // bounds or center point. This is intended to reduce the amount of information
- // sent to applications by particularly noisy joysticks (such as PS3).
- if (fabs(newValue - currentValue) > filter
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min)
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max)
- || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
- return true;
- }
- }
- return false;
-}
-
-bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(
- float filter, float newValue, float currentValue, float thresholdValue) {
- float newDistance = fabs(newValue - thresholdValue);
- if (newDistance < filter) {
- float oldDistance = fabs(currentValue - thresholdValue);
- if (newDistance < oldDistance) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace android
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
deleted file mode 100644
index 0666ca5..0000000
--- a/services/inputflinger/InputReader.h
+++ /dev/null
@@ -1,1743 +0,0 @@
-/*
- * Copyright (C) 2010 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 _UI_INPUT_READER_H
-#define _UI_INPUT_READER_H
-
-#include "EventHub.h"
-#include "PointerControllerInterface.h"
-#include "InputListener.h"
-#include "InputReaderBase.h"
-
-#include <input/DisplayViewport.h>
-#include <input/Input.h>
-#include <input/VelocityControl.h>
-#include <input/VelocityTracker.h>
-#include <ui/DisplayInfo.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-#include <optional>
-#include <stddef.h>
-#include <unistd.h>
-#include <vector>
-
-namespace android {
-
-class InputDevice;
-class InputMapper;
-
-
-struct StylusState {
- /* Time the stylus event was received. */
- nsecs_t when;
- /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
- float pressure;
- /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
- uint32_t buttons;
- /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
- int32_t toolType;
-
- void copyFrom(const StylusState& other) {
- when = other.when;
- pressure = other.pressure;
- buttons = other.buttons;
- toolType = other.toolType;
- }
-
- void clear() {
- when = LLONG_MAX;
- pressure = 0.f;
- buttons = 0;
- toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
- }
-};
-
-
-/* Internal interface used by individual input devices to access global input device state
- * and parameters maintained by the input reader.
- */
-class InputReaderContext {
-public:
- InputReaderContext() { }
- virtual ~InputReaderContext() { }
-
- virtual void updateGlobalMetaState() = 0;
- virtual int32_t getGlobalMetaState() = 0;
-
- virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
- virtual bool shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
-
- virtual void fadePointer() = 0;
-
- virtual void requestTimeoutAtTime(nsecs_t when) = 0;
- virtual int32_t bumpGeneration() = 0;
-
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
- virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
-
- virtual InputReaderPolicyInterface* getPolicy() = 0;
- virtual InputListenerInterface* getListener() = 0;
- virtual EventHubInterface* getEventHub() = 0;
-
- virtual uint32_t getNextSequenceNum() = 0;
-};
-
-
-/* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input listener. Some functions of the input reader, such as early
- * event filtering in low power states, are controlled by a separate policy object.
- *
- * The InputReader owns a collection of InputMappers. Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
- * components running on arbitrary threads. To keep things manageable, the InputReader
- * uses a single Mutex to guard its state. The Mutex may be held while calling into the
- * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
- */
-class InputReader : public InputReaderInterface {
-public:
- InputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener);
- virtual ~InputReader();
-
- virtual void dump(std::string& dump);
- virtual void monitor();
-
- virtual void loopOnce();
-
- virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices);
-
- virtual bool isInputDeviceEnabled(int32_t deviceId);
-
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode);
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode);
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw);
-
- virtual void toggleCapsLockState(int32_t deviceId);
-
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual void requestRefreshConfiguration(uint32_t changes);
-
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token);
- virtual void cancelVibrate(int32_t deviceId, int32_t token);
-
- virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
-protected:
- // These members are protected so they can be instrumented by test cases.
- virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, uint32_t classes);
-
- class ContextImpl : public InputReaderContext {
- InputReader* mReader;
-
- public:
- explicit ContextImpl(InputReader* reader);
-
- virtual void updateGlobalMetaState();
- virtual int32_t getGlobalMetaState();
- virtual void disableVirtualKeysUntil(nsecs_t time);
- virtual bool shouldDropVirtualKey(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode);
- virtual void fadePointer();
- virtual void requestTimeoutAtTime(nsecs_t when);
- virtual int32_t bumpGeneration();
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices);
- virtual void dispatchExternalStylusState(const StylusState& outState);
- virtual InputReaderPolicyInterface* getPolicy();
- virtual InputListenerInterface* getListener();
- virtual EventHubInterface* getEventHub();
- virtual uint32_t getNextSequenceNum();
- } mContext;
-
- friend class ContextImpl;
-
-private:
- Mutex mLock;
-
- Condition mReaderIsAliveCondition;
-
- // This could be unique_ptr, but due to the way InputReader tests are written,
- // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
- // in parallel to passing it to the InputReader.
- std::shared_ptr<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<QueuedInputListener> mQueuedListener;
-
- InputReaderConfiguration mConfig;
-
- // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
- uint32_t mNextSequenceNum;
-
- // The event queue.
- static const int EVENT_BUFFER_SIZE = 256;
- RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
-
- KeyedVector<int32_t, InputDevice*> mDevices;
-
- // low-level input event decoding and device management
- void processEventsLocked(const RawEvent* rawEvents, size_t count);
-
- void addDeviceLocked(nsecs_t when, int32_t deviceId);
- void removeDeviceLocked(nsecs_t when, int32_t deviceId);
- void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
- void timeoutExpiredLocked(nsecs_t when);
-
- void handleConfigurationChangedLocked(nsecs_t when);
-
- int32_t mGlobalMetaState;
- void updateGlobalMetaStateLocked();
- int32_t getGlobalMetaStateLocked();
-
- void notifyExternalStylusPresenceChanged();
- void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
- void dispatchExternalStylusState(const StylusState& state);
-
- void fadePointerLocked();
-
- int32_t mGeneration;
- int32_t bumpGenerationLocked();
-
- void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
-
- nsecs_t mDisableVirtualKeysTimeout;
- void disableVirtualKeysUntilLocked(nsecs_t time);
- bool shouldDropVirtualKeyLocked(nsecs_t now,
- InputDevice* device, int32_t keyCode, int32_t scanCode);
-
- nsecs_t mNextTimeout;
- void requestTimeoutAtTimeLocked(nsecs_t when);
-
- uint32_t mConfigurationChangesToRefresh;
- void refreshConfigurationLocked(uint32_t changes);
-
- // state queries
- typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
- int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
- GetStateFunc getStateFunc);
- bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-};
-
-
-/* Represents the state of a single input device. */
-class InputDevice {
-public:
- InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t
- controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes);
- ~InputDevice();
-
- inline InputReaderContext* getContext() { return mContext; }
- inline int32_t getId() const { return mId; }
- inline int32_t getControllerNumber() const { return mControllerNumber; }
- inline int32_t getGeneration() const { return mGeneration; }
- inline const std::string getName() const { return mIdentifier.name; }
- inline const std::string getDescriptor() { return mIdentifier.descriptor; }
- inline uint32_t getClasses() const { return mClasses; }
- inline uint32_t getSources() const { return mSources; }
-
- inline bool isExternal() { return mIsExternal; }
- inline void setExternal(bool external) { mIsExternal = external; }
- inline std::optional<uint8_t> getAssociatedDisplayPort() const {
- return mAssociatedDisplayPort;
- }
- inline std::optional<DisplayViewport> getAssociatedViewport() const {
- return mAssociatedViewport;
- }
- inline void setMic(bool hasMic) { mHasMic = hasMic; }
- inline bool hasMic() const { return mHasMic; }
-
- inline bool isIgnored() { return mMappers.empty(); }
-
- bool isEnabled();
- void setEnabled(bool enabled, nsecs_t when);
-
- void dump(std::string& dump);
- void addMapper(InputMapper* mapper);
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- void reset(nsecs_t when);
- void process(const RawEvent* rawEvents, size_t count);
- void timeoutExpired(nsecs_t when);
- void updateExternalStylusState(const StylusState& state);
-
- void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
- int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
- bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
- void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
- void cancelVibrate(int32_t token);
- void cancelTouch(nsecs_t when);
-
- int32_t getMetaState();
- void updateMetaState(int32_t keyCode);
-
- void fadePointer();
-
- void bumpGeneration();
-
- void notifyReset(nsecs_t when);
-
- inline const PropertyMap& getConfiguration() { return mConfiguration; }
- inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
- bool hasKey(int32_t code) {
- return getEventHub()->hasScanCode(mId, code);
- }
-
- bool hasAbsoluteAxis(int32_t code) {
- RawAbsoluteAxisInfo info;
- getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
- return info.valid;
- }
-
- bool isKeyPressed(int32_t code) {
- return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
- }
-
- int32_t getAbsoluteAxisValue(int32_t code) {
- int32_t value;
- getEventHub()->getAbsoluteAxisValue(mId, code, &value);
- return value;
- }
-
- std::optional<int32_t> getAssociatedDisplayId();
-
-private:
- InputReaderContext* mContext;
- int32_t mId;
- int32_t mGeneration;
- int32_t mControllerNumber;
- InputDeviceIdentifier mIdentifier;
- std::string mAlias;
- uint32_t mClasses;
-
- std::vector<InputMapper*> mMappers;
-
- uint32_t mSources;
- bool mIsExternal;
- std::optional<uint8_t> mAssociatedDisplayPort;
- std::optional<DisplayViewport> mAssociatedViewport;
- bool mHasMic;
- bool mDropUntilNextSync;
-
- typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
- int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
-
- PropertyMap mConfiguration;
-};
-
-
-/* Keeps track of the state of mouse or touch pad buttons. */
-class CursorButtonAccumulator {
-public:
- CursorButtonAccumulator();
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
-
- uint32_t getButtonState() const;
-
-private:
- bool mBtnLeft;
- bool mBtnRight;
- bool mBtnMiddle;
- bool mBtnBack;
- bool mBtnSide;
- bool mBtnForward;
- bool mBtnExtra;
- bool mBtnTask;
-
- void clearButtons();
-};
-
-
-/* Keeps track of cursor movements. */
-
-class CursorMotionAccumulator {
-public:
- CursorMotionAccumulator();
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
- void finishSync();
-
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
-
-private:
- int32_t mRelX;
- int32_t mRelY;
-
- void clearRelativeAxes();
-};
-
-
-/* Keeps track of cursor scrolling motions. */
-
-class CursorScrollAccumulator {
-public:
- CursorScrollAccumulator();
- void configure(InputDevice* device);
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
- void finishSync();
-
- inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
- inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
-
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
- inline int32_t getRelativeVWheel() const { return mRelWheel; }
- inline int32_t getRelativeHWheel() const { return mRelHWheel; }
-
-private:
- bool mHaveRelWheel;
- bool mHaveRelHWheel;
-
- int32_t mRelX;
- int32_t mRelY;
- int32_t mRelWheel;
- int32_t mRelHWheel;
-
- void clearRelativeAxes();
-};
-
-
-/* Keeps track of the state of touch, stylus and tool buttons. */
-class TouchButtonAccumulator {
-public:
- TouchButtonAccumulator();
- void configure(InputDevice* device);
- void reset(InputDevice* device);
-
- void process(const RawEvent* rawEvent);
-
- uint32_t getButtonState() const;
- int32_t getToolType() const;
- bool isToolActive() const;
- bool isHovering() const;
- bool hasStylus() const;
-
-private:
- bool mHaveBtnTouch;
- bool mHaveStylus;
-
- bool mBtnTouch;
- bool mBtnStylus;
- bool mBtnStylus2;
- bool mBtnToolFinger;
- bool mBtnToolPen;
- bool mBtnToolRubber;
- bool mBtnToolBrush;
- bool mBtnToolPencil;
- bool mBtnToolAirbrush;
- bool mBtnToolMouse;
- bool mBtnToolLens;
- bool mBtnToolDoubleTap;
- bool mBtnToolTripleTap;
- bool mBtnToolQuadTap;
-
- void clearButtons();
-};
-
-
-/* Raw axis information from the driver. */
-struct RawPointerAxes {
- RawAbsoluteAxisInfo x;
- RawAbsoluteAxisInfo y;
- RawAbsoluteAxisInfo pressure;
- RawAbsoluteAxisInfo touchMajor;
- RawAbsoluteAxisInfo touchMinor;
- RawAbsoluteAxisInfo toolMajor;
- RawAbsoluteAxisInfo toolMinor;
- RawAbsoluteAxisInfo orientation;
- RawAbsoluteAxisInfo distance;
- RawAbsoluteAxisInfo tiltX;
- RawAbsoluteAxisInfo tiltY;
- RawAbsoluteAxisInfo trackingId;
- RawAbsoluteAxisInfo slot;
-
- RawPointerAxes();
- inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
- inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
- void clear();
-};
-
-
-/* Raw data for a collection of pointers including a pointer id mapping table. */
-struct RawPointerData {
- struct Pointer {
- uint32_t id;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t touchMajor;
- int32_t touchMinor;
- int32_t toolMajor;
- int32_t toolMinor;
- int32_t orientation;
- int32_t distance;
- int32_t tiltX;
- int32_t tiltY;
- int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
- bool isHovering;
- };
-
- uint32_t pointerCount;
- Pointer pointers[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
-
- RawPointerData();
- void clear();
- void copyFrom(const RawPointerData& other);
- void getCentroidOfTouchingPointers(float* outX, float* outY) const;
-
- inline void markIdBit(uint32_t id, bool isHovering) {
- if (isHovering) {
- hoveringIdBits.markBit(id);
- } else {
- touchingIdBits.markBit(id);
- }
- }
-
- inline void clearIdBits() {
- hoveringIdBits.clear();
- touchingIdBits.clear();
- }
-
- inline const Pointer& pointerForId(uint32_t id) const {
- return pointers[idToIndex[id]];
- }
-
- inline bool isHovering(uint32_t pointerIndex) {
- return pointers[pointerIndex].isHovering;
- }
-};
-
-
-/* Cooked data for a collection of pointers including a pointer id mapping table. */
-struct CookedPointerData {
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
-
- CookedPointerData();
- void clear();
- void copyFrom(const CookedPointerData& other);
-
- inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
- return pointerCoords[idToIndex[id]];
- }
-
- inline PointerCoords& editPointerCoordsWithId(uint32_t id) {
- return pointerCoords[idToIndex[id]];
- }
-
- inline PointerProperties& editPointerPropertiesWithId(uint32_t id) {
- return pointerProperties[idToIndex[id]];
- }
-
- inline bool isHovering(uint32_t pointerIndex) const {
- return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
- }
-
- inline bool isTouching(uint32_t pointerIndex) const {
- return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
- }
-};
-
-
-/* Keeps track of the state of single-touch protocol. */
-class SingleTouchMotionAccumulator {
-public:
- SingleTouchMotionAccumulator();
-
- void process(const RawEvent* rawEvent);
- void reset(InputDevice* device);
-
- inline int32_t getAbsoluteX() const { return mAbsX; }
- inline int32_t getAbsoluteY() const { return mAbsY; }
- inline int32_t getAbsolutePressure() const { return mAbsPressure; }
- inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; }
- inline int32_t getAbsoluteDistance() const { return mAbsDistance; }
- inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; }
- inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; }
-
-private:
- int32_t mAbsX;
- int32_t mAbsY;
- int32_t mAbsPressure;
- int32_t mAbsToolWidth;
- int32_t mAbsDistance;
- int32_t mAbsTiltX;
- int32_t mAbsTiltY;
-
- void clearAbsoluteAxes();
-};
-
-
-/* Keeps track of the state of multi-touch protocol. */
-class MultiTouchMotionAccumulator {
-public:
- class Slot {
- public:
- inline bool isInUse() const { return mInUse; }
- inline int32_t getX() const { return mAbsMTPositionX; }
- inline int32_t getY() const { return mAbsMTPositionY; }
- inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
- inline int32_t getTouchMinor() const {
- return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; }
- inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
- inline int32_t getToolMinor() const {
- return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; }
- inline int32_t getOrientation() const { return mAbsMTOrientation; }
- inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
- inline int32_t getPressure() const { return mAbsMTPressure; }
- inline int32_t getDistance() const { return mAbsMTDistance; }
- inline int32_t getToolType() const;
-
- private:
- friend class MultiTouchMotionAccumulator;
-
- bool mInUse;
- bool mHaveAbsMTTouchMinor;
- bool mHaveAbsMTWidthMinor;
- bool mHaveAbsMTToolType;
-
- int32_t mAbsMTPositionX;
- int32_t mAbsMTPositionY;
- int32_t mAbsMTTouchMajor;
- int32_t mAbsMTTouchMinor;
- int32_t mAbsMTWidthMajor;
- int32_t mAbsMTWidthMinor;
- int32_t mAbsMTOrientation;
- int32_t mAbsMTTrackingId;
- int32_t mAbsMTPressure;
- int32_t mAbsMTDistance;
- int32_t mAbsMTToolType;
-
- Slot();
- void clear();
- };
-
- MultiTouchMotionAccumulator();
- ~MultiTouchMotionAccumulator();
-
- void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
- void reset(InputDevice* device);
- void process(const RawEvent* rawEvent);
- void finishSync();
- bool hasStylus() const;
-
- inline size_t getSlotCount() const { return mSlotCount; }
- inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
-
-private:
- int32_t mCurrentSlot;
- Slot* mSlots;
- size_t mSlotCount;
- bool mUsingSlotsProtocol;
- bool mHaveStylus;
-
- void clearSlots(int32_t initialSlot);
-};
-
-
-/* An input mapper transforms raw input events into cooked event data.
- * A single input device can have multiple associated input mappers in order to interpret
- * different classes of events.
- *
- * InputMapper lifecycle:
- * - create
- * - configure with 0 changes
- * - reset
- * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
- * - reset
- * - destroy
- */
-class InputMapper {
-public:
- explicit InputMapper(InputDevice* device);
- virtual ~InputMapper();
-
- inline InputDevice* getDevice() { return mDevice; }
- inline int32_t getDeviceId() { return mDevice->getId(); }
- inline const std::string getDeviceName() { return mDevice->getName(); }
- inline InputReaderContext* getContext() { return mContext; }
- inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
- inline InputListenerInterface* getListener() { return mContext->getListener(); }
- inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
- virtual uint32_t getSources() = 0;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent) = 0;
- virtual void timeoutExpired(nsecs_t when);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token);
- virtual void cancelVibrate(int32_t token);
- virtual void cancelTouch(nsecs_t when);
-
- virtual int32_t getMetaState();
- virtual void updateMetaState(int32_t keyCode);
-
- virtual void updateExternalStylusState(const StylusState& state);
-
- virtual void fadePointer();
- virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
-
-protected:
- InputDevice* mDevice;
- InputReaderContext* mContext;
-
- status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
- void bumpGeneration();
-
- static void dumpRawAbsoluteAxisInfo(std::string& dump,
- const RawAbsoluteAxisInfo& axis, const char* name);
- static void dumpStylusState(std::string& dump, const StylusState& state);
-};
-
-
-class SwitchInputMapper : public InputMapper {
-public:
- explicit SwitchInputMapper(InputDevice* device);
- virtual ~SwitchInputMapper();
-
- virtual uint32_t getSources();
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
- virtual void dump(std::string& dump);
-
-private:
- uint32_t mSwitchValues;
- uint32_t mUpdatedSwitchMask;
-
- void processSwitch(int32_t switchCode, int32_t switchValue);
- void sync(nsecs_t when);
-};
-
-
-class VibratorInputMapper : public InputMapper {
-public:
- explicit VibratorInputMapper(InputDevice* device);
- virtual ~VibratorInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void process(const RawEvent* rawEvent);
-
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
- int32_t token);
- virtual void cancelVibrate(int32_t token);
- virtual void timeoutExpired(nsecs_t when);
- virtual void dump(std::string& dump);
-
-private:
- bool mVibrating;
- nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
- size_t mPatternSize;
- ssize_t mRepeat;
- int32_t mToken;
- ssize_t mIndex;
- nsecs_t mNextStepTime;
-
- void nextStep();
- void stopVibrating();
-};
-
-
-class KeyboardInputMapper : public InputMapper {
-public:
- KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
- virtual ~KeyboardInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual int32_t getMetaState();
- virtual void updateMetaState(int32_t keyCode);
- virtual std::optional<int32_t> getAssociatedDisplayId();
-
-private:
- // The current viewport.
- std::optional<DisplayViewport> mViewport;
-
- struct KeyDown {
- int32_t keyCode;
- int32_t scanCode;
- };
-
- uint32_t mSource;
- int32_t mKeyboardType;
-
- std::vector<KeyDown> mKeyDowns; // keys that are down
- int32_t mMetaState;
- nsecs_t mDownTime; // time of most recent key down
-
- int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
-
- struct LedState {
- bool avail; // led is available
- bool on; // we think the led is currently on
- };
- LedState mCapsLockLedState;
- LedState mNumLockLedState;
- LedState mScrollLockLedState;
-
- // Immutable configuration parameters.
- struct Parameters {
- bool orientationAware;
- bool handlesKeyRepeat;
- } mParameters;
-
- void configureParameters();
- void dumpParameters(std::string& dump);
-
- int32_t getOrientation();
- int32_t getDisplayId();
-
- bool isKeyboardOrGamepadKey(int32_t scanCode);
- bool isMediaKey(int32_t keyCode);
-
- void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
-
- bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
-
- ssize_t findKeyDown(int32_t scanCode);
-
- void resetLedState();
- void initializeLedState(LedState& ledState, int32_t led);
- void updateLedState(bool reset);
- void updateLedStateForModifier(LedState& ledState, int32_t led,
- int32_t modifier, bool reset);
- std::optional<DisplayViewport> findViewport(nsecs_t when,
- const InputReaderConfiguration* config);
-};
-
-
-class CursorInputMapper : public InputMapper {
-public:
- explicit CursorInputMapper(InputDevice* device);
- virtual ~CursorInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-
- virtual void fadePointer();
-
- virtual std::optional<int32_t> getAssociatedDisplayId();
-
-private:
- // Amount that trackball needs to move in order to generate a key event.
- static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
- // Immutable configuration parameters.
- struct Parameters {
- enum Mode {
- MODE_POINTER,
- MODE_POINTER_RELATIVE,
- MODE_NAVIGATION,
- };
-
- Mode mode;
- bool hasAssociatedDisplay;
- bool orientationAware;
- } mParameters;
-
- CursorButtonAccumulator mCursorButtonAccumulator;
- CursorMotionAccumulator mCursorMotionAccumulator;
- CursorScrollAccumulator mCursorScrollAccumulator;
-
- int32_t mSource;
- float mXScale;
- float mYScale;
- float mXPrecision;
- float mYPrecision;
-
- float mVWheelScale;
- float mHWheelScale;
-
- // Velocity controls for mouse pointer and wheel movements.
- // The controls for X and Y wheel movements are separate to keep them decoupled.
- VelocityControl mPointerVelocityControl;
- VelocityControl mWheelXVelocityControl;
- VelocityControl mWheelYVelocityControl;
-
- int32_t mOrientation;
-
- sp<PointerControllerInterface> mPointerController;
-
- int32_t mButtonState;
- nsecs_t mDownTime;
-
- void configureParameters();
- void dumpParameters(std::string& dump);
-
- void sync(nsecs_t when);
-};
-
-
-class RotaryEncoderInputMapper : public InputMapper {
-public:
- explicit RotaryEncoderInputMapper(InputDevice* device);
- virtual ~RotaryEncoderInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-private:
- CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
-
- int32_t mSource;
- float mScalingFactor;
- int32_t mOrientation;
-
- void sync(nsecs_t when);
-};
-
-class TouchInputMapper : public InputMapper {
-public:
- explicit TouchInputMapper(InputDevice* device);
- virtual ~TouchInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags);
-
- virtual void fadePointer();
- virtual void cancelTouch(nsecs_t when);
- virtual void timeoutExpired(nsecs_t when);
- virtual void updateExternalStylusState(const StylusState& state);
- virtual std::optional<int32_t> getAssociatedDisplayId();
-
-protected:
- CursorButtonAccumulator mCursorButtonAccumulator;
- CursorScrollAccumulator mCursorScrollAccumulator;
- TouchButtonAccumulator mTouchButtonAccumulator;
-
- struct VirtualKey {
- int32_t keyCode;
- int32_t scanCode;
- uint32_t flags;
-
- // computed hit box, specified in touch screen coords based on known display size
- int32_t hitLeft;
- int32_t hitTop;
- int32_t hitRight;
- int32_t hitBottom;
-
- inline bool isHit(int32_t x, int32_t y) const {
- return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
- }
- };
-
- // Input sources and device mode.
- uint32_t mSource;
-
- enum DeviceMode {
- DEVICE_MODE_DISABLED, // input is disabled
- DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
- DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
- DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- DEVICE_MODE_POINTER, // pointer mapping (pointer)
- };
- DeviceMode mDeviceMode;
-
- // The reader's configuration.
- InputReaderConfiguration mConfig;
-
- // Immutable configuration parameters.
- struct Parameters {
- enum DeviceType {
- DEVICE_TYPE_TOUCH_SCREEN,
- DEVICE_TYPE_TOUCH_PAD,
- DEVICE_TYPE_TOUCH_NAVIGATION,
- DEVICE_TYPE_POINTER,
- };
-
- DeviceType deviceType;
- bool hasAssociatedDisplay;
- bool associatedDisplayIsExternal;
- bool orientationAware;
- bool hasButtonUnderPad;
- std::string uniqueDisplayId;
-
- enum GestureMode {
- GESTURE_MODE_SINGLE_TOUCH,
- GESTURE_MODE_MULTI_TOUCH,
- };
- GestureMode gestureMode;
-
- bool wake;
- } mParameters;
-
- // Immutable calibration parameters in parsed form.
- struct Calibration {
- // Size
- enum SizeCalibration {
- SIZE_CALIBRATION_DEFAULT,
- SIZE_CALIBRATION_NONE,
- SIZE_CALIBRATION_GEOMETRIC,
- SIZE_CALIBRATION_DIAMETER,
- SIZE_CALIBRATION_BOX,
- SIZE_CALIBRATION_AREA,
- };
-
- SizeCalibration sizeCalibration;
-
- bool haveSizeScale;
- float sizeScale;
- bool haveSizeBias;
- float sizeBias;
- bool haveSizeIsSummed;
- bool sizeIsSummed;
-
- // Pressure
- enum PressureCalibration {
- PRESSURE_CALIBRATION_DEFAULT,
- PRESSURE_CALIBRATION_NONE,
- PRESSURE_CALIBRATION_PHYSICAL,
- PRESSURE_CALIBRATION_AMPLITUDE,
- };
-
- PressureCalibration pressureCalibration;
- bool havePressureScale;
- float pressureScale;
-
- // Orientation
- enum OrientationCalibration {
- ORIENTATION_CALIBRATION_DEFAULT,
- ORIENTATION_CALIBRATION_NONE,
- ORIENTATION_CALIBRATION_INTERPOLATED,
- ORIENTATION_CALIBRATION_VECTOR,
- };
-
- OrientationCalibration orientationCalibration;
-
- // Distance
- enum DistanceCalibration {
- DISTANCE_CALIBRATION_DEFAULT,
- DISTANCE_CALIBRATION_NONE,
- DISTANCE_CALIBRATION_SCALED,
- };
-
- DistanceCalibration distanceCalibration;
- bool haveDistanceScale;
- float distanceScale;
-
- enum CoverageCalibration {
- COVERAGE_CALIBRATION_DEFAULT,
- COVERAGE_CALIBRATION_NONE,
- COVERAGE_CALIBRATION_BOX,
- };
-
- CoverageCalibration coverageCalibration;
-
- inline void applySizeScaleAndBias(float* outSize) const {
- if (haveSizeScale) {
- *outSize *= sizeScale;
- }
- if (haveSizeBias) {
- *outSize += sizeBias;
- }
- if (*outSize < 0) {
- *outSize = 0;
- }
- }
- } mCalibration;
-
- // Affine location transformation/calibration
- struct TouchAffineTransformation mAffineTransform;
-
- RawPointerAxes mRawPointerAxes;
-
- struct RawState {
- nsecs_t when;
-
- // Raw pointer sample data.
- RawPointerData rawPointerData;
-
- int32_t buttonState;
-
- // Scroll state.
- int32_t rawVScroll;
- int32_t rawHScroll;
-
- void copyFrom(const RawState& other) {
- when = other.when;
- rawPointerData.copyFrom(other.rawPointerData);
- buttonState = other.buttonState;
- rawVScroll = other.rawVScroll;
- rawHScroll = other.rawHScroll;
- }
-
- void clear() {
- when = 0;
- rawPointerData.clear();
- buttonState = 0;
- rawVScroll = 0;
- rawHScroll = 0;
- }
- };
-
- struct CookedState {
- // Cooked pointer sample data.
- CookedPointerData cookedPointerData;
-
- // Id bits used to differentiate fingers, stylus and mouse tools.
- BitSet32 fingerIdBits;
- BitSet32 stylusIdBits;
- BitSet32 mouseIdBits;
-
- int32_t buttonState;
-
- void copyFrom(const CookedState& other) {
- cookedPointerData.copyFrom(other.cookedPointerData);
- fingerIdBits = other.fingerIdBits;
- stylusIdBits = other.stylusIdBits;
- mouseIdBits = other.mouseIdBits;
- buttonState = other.buttonState;
- }
-
- void clear() {
- cookedPointerData.clear();
- fingerIdBits.clear();
- stylusIdBits.clear();
- mouseIdBits.clear();
- buttonState = 0;
- }
- };
-
- std::vector<RawState> mRawStatesPending;
- RawState mCurrentRawState;
- CookedState mCurrentCookedState;
- RawState mLastRawState;
- CookedState mLastCookedState;
-
- // State provided by an external stylus
- StylusState mExternalStylusState;
- int64_t mExternalStylusId;
- nsecs_t mExternalStylusFusionTimeout;
- bool mExternalStylusDataPending;
-
- // True if we sent a HOVER_ENTER event.
- bool mSentHoverEnter;
-
- // Have we assigned pointer IDs for this stream
- bool mHavePointerIds;
-
- // Is the current stream of direct touch events aborted
- bool mCurrentMotionAborted;
-
- // The time the primary pointer last went down.
- nsecs_t mDownTime;
-
- // The pointer controller, or null if the device is not a pointer.
- sp<PointerControllerInterface> mPointerController;
-
- std::vector<VirtualKey> mVirtualKeys;
-
- virtual void configureParameters();
- virtual void dumpParameters(std::string& dump);
- virtual void configureRawPointerAxes();
- virtual void dumpRawPointerAxes(std::string& dump);
- virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
- virtual void dumpSurface(std::string& dump);
- virtual void configureVirtualKeys();
- virtual void dumpVirtualKeys(std::string& dump);
- virtual void parseCalibration();
- virtual void resolveCalibration();
- virtual void dumpCalibration(std::string& dump);
- virtual void updateAffineTransformation();
- virtual void dumpAffineTransformation(std::string& dump);
- virtual void resolveExternalStylusPresence();
- virtual bool hasStylus() const = 0;
- virtual bool hasExternalStylus() const;
-
- virtual void syncTouch(nsecs_t when, RawState* outState) = 0;
-
-private:
- // The current viewport.
- // The components of the viewport are specified in the display's rotated orientation.
- DisplayViewport mViewport;
-
- // The surface orientation, width and height set by configureSurface().
- // The width and height are derived from the viewport but are specified
- // in the natural orientation.
- // The surface origin specifies how the surface coordinates should be translated
- // to align with the logical display coordinate space.
- int32_t mSurfaceWidth;
- int32_t mSurfaceHeight;
- int32_t mSurfaceLeft;
- int32_t mSurfaceTop;
-
- // Similar to the surface coordinates, but in the raw display coordinate space rather than in
- // the logical coordinate space.
- int32_t mPhysicalWidth;
- int32_t mPhysicalHeight;
- int32_t mPhysicalLeft;
- int32_t mPhysicalTop;
-
- // The orientation may be different from the viewport orientation as it specifies
- // the rotation of the surface coordinates required to produce the viewport's
- // requested orientation, so it will depend on whether the device is orientation aware.
- int32_t mSurfaceOrientation;
-
- // Translation and scaling factors, orientation-independent.
- float mXTranslate;
- float mXScale;
- float mXPrecision;
-
- float mYTranslate;
- float mYScale;
- float mYPrecision;
-
- float mGeometricScale;
-
- float mPressureScale;
-
- float mSizeScale;
-
- float mOrientationScale;
-
- float mDistanceScale;
-
- bool mHaveTilt;
- float mTiltXCenter;
- float mTiltXScale;
- float mTiltYCenter;
- float mTiltYScale;
-
- bool mExternalStylusConnected;
-
- // Oriented motion ranges for input device info.
- struct OrientedRanges {
- InputDeviceInfo::MotionRange x;
- InputDeviceInfo::MotionRange y;
- InputDeviceInfo::MotionRange pressure;
-
- bool haveSize;
- InputDeviceInfo::MotionRange size;
-
- bool haveTouchSize;
- InputDeviceInfo::MotionRange touchMajor;
- InputDeviceInfo::MotionRange touchMinor;
-
- bool haveToolSize;
- InputDeviceInfo::MotionRange toolMajor;
- InputDeviceInfo::MotionRange toolMinor;
-
- bool haveOrientation;
- InputDeviceInfo::MotionRange orientation;
-
- bool haveDistance;
- InputDeviceInfo::MotionRange distance;
-
- bool haveTilt;
- InputDeviceInfo::MotionRange tilt;
-
- OrientedRanges() {
- clear();
- }
-
- void clear() {
- haveSize = false;
- haveTouchSize = false;
- haveToolSize = false;
- haveOrientation = false;
- haveDistance = false;
- haveTilt = false;
- }
- } mOrientedRanges;
-
- // Oriented dimensions and precision.
- float mOrientedXPrecision;
- float mOrientedYPrecision;
-
- struct CurrentVirtualKeyState {
- bool down;
- bool ignored;
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
- } mCurrentVirtualKey;
-
- // Scale factor for gesture or mouse based pointer movements.
- float mPointerXMovementScale;
- float mPointerYMovementScale;
-
- // Scale factor for gesture based zooming and other freeform motions.
- float mPointerXZoomScale;
- float mPointerYZoomScale;
-
- // The maximum swipe width.
- float mPointerGestureMaxSwipeWidth;
-
- struct PointerDistanceHeapElement {
- uint32_t currentPointerIndex : 8;
- uint32_t lastPointerIndex : 8;
- uint64_t distance : 48; // squared distance
- };
-
- enum PointerUsage {
- POINTER_USAGE_NONE,
- POINTER_USAGE_GESTURES,
- POINTER_USAGE_STYLUS,
- POINTER_USAGE_MOUSE,
- };
- PointerUsage mPointerUsage;
-
- struct PointerGesture {
- enum Mode {
- // No fingers, button is not pressed.
- // Nothing happening.
- NEUTRAL,
-
- // No fingers, button is not pressed.
- // Tap detected.
- // Emits DOWN and UP events at the pointer location.
- TAP,
-
- // Exactly one finger dragging following a tap.
- // Pointer follows the active finger.
- // Emits DOWN, MOVE and UP events at the pointer location.
- //
- // Detect double-taps when the finger goes up while in TAP_DRAG mode.
- TAP_DRAG,
-
- // Button is pressed.
- // Pointer follows the active finger if there is one. Other fingers are ignored.
- // Emits DOWN, MOVE and UP events at the pointer location.
- BUTTON_CLICK_OR_DRAG,
-
- // Exactly one finger, button is not pressed.
- // Pointer follows the active finger.
- // Emits HOVER_MOVE events at the pointer location.
- //
- // Detect taps when the finger goes up while in HOVER mode.
- HOVER,
-
- // Exactly two fingers but neither have moved enough to clearly indicate
- // whether a swipe or freeform gesture was intended. We consider the
- // pointer to be pressed so this enables clicking or long-pressing on buttons.
- // Pointer does not move.
- // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
- PRESS,
-
- // Exactly two fingers moving in the same direction, button is not pressed.
- // Pointer does not move.
- // Emits DOWN, MOVE and UP events with a single pointer coordinate that
- // follows the midpoint between both fingers.
- SWIPE,
-
- // Two or more fingers moving in arbitrary directions, button is not pressed.
- // Pointer does not move.
- // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
- // each finger individually relative to the initial centroid of the finger.
- FREEFORM,
-
- // Waiting for quiet time to end before starting the next gesture.
- QUIET,
- };
-
- // Time the first finger went down.
- nsecs_t firstTouchTime;
-
- // The active pointer id from the raw touch data.
- int32_t activeTouchId; // -1 if none
-
- // The active pointer id from the gesture last delivered to the application.
- int32_t activeGestureId; // -1 if none
-
- // Pointer coords and ids for the current and previous pointer gesture.
- Mode currentGestureMode;
- BitSet32 currentGestureIdBits;
- uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties currentGestureProperties[MAX_POINTERS];
- PointerCoords currentGestureCoords[MAX_POINTERS];
-
- Mode lastGestureMode;
- BitSet32 lastGestureIdBits;
- uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties lastGestureProperties[MAX_POINTERS];
- PointerCoords lastGestureCoords[MAX_POINTERS];
-
- // Time the pointer gesture last went down.
- nsecs_t downTime;
-
- // Time when the pointer went down for a TAP.
- nsecs_t tapDownTime;
-
- // Time when the pointer went up for a TAP.
- nsecs_t tapUpTime;
-
- // Location of initial tap.
- float tapX, tapY;
-
- // Time we started waiting for quiescence.
- nsecs_t quietTime;
-
- // Reference points for multitouch gestures.
- float referenceTouchX; // reference touch X/Y coordinates in surface units
- float referenceTouchY;
- float referenceGestureX; // reference gesture X/Y coordinates in pixels
- float referenceGestureY;
-
- // Distance that each pointer has traveled which has not yet been
- // subsumed into the reference gesture position.
- BitSet32 referenceIdBits;
- struct Delta {
- float dx, dy;
- };
- Delta referenceDeltas[MAX_POINTER_ID + 1];
-
- // Describes how touch ids are mapped to gesture ids for freeform gestures.
- uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
-
- // A velocity tracker for determining whether to switch active pointers during drags.
- VelocityTracker velocityTracker;
-
- void reset() {
- firstTouchTime = LLONG_MIN;
- activeTouchId = -1;
- activeGestureId = -1;
- currentGestureMode = NEUTRAL;
- currentGestureIdBits.clear();
- lastGestureMode = NEUTRAL;
- lastGestureIdBits.clear();
- downTime = 0;
- velocityTracker.clear();
- resetTap();
- resetQuietTime();
- }
-
- void resetTap() {
- tapDownTime = LLONG_MIN;
- tapUpTime = LLONG_MIN;
- }
-
- void resetQuietTime() {
- quietTime = LLONG_MIN;
- }
- } mPointerGesture;
-
- struct PointerSimple {
- PointerCoords currentCoords;
- PointerProperties currentProperties;
- PointerCoords lastCoords;
- PointerProperties lastProperties;
-
- // True if the pointer is down.
- bool down;
-
- // True if the pointer is hovering.
- bool hovering;
-
- // Time the pointer last went down.
- nsecs_t downTime;
-
- void reset() {
- currentCoords.clear();
- currentProperties.clear();
- lastCoords.clear();
- lastProperties.clear();
- down = false;
- hovering = false;
- downTime = 0;
- }
- } mPointerSimple;
-
- // The pointer and scroll velocity controls.
- VelocityControl mPointerVelocityControl;
- VelocityControl mWheelXVelocityControl;
- VelocityControl mWheelYVelocityControl;
-
- std::optional<DisplayViewport> findViewport();
-
- void resetExternalStylus();
- void clearStylusDataPendingFlags();
-
- void sync(nsecs_t when);
-
- bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
- void processRawTouches(bool timeout);
- void cookAndDispatch(nsecs_t when);
- void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags);
-
- void dispatchTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonPress(nsecs_t when, uint32_t policyFlags);
- const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
- void cookPointerData();
- void abortTouches(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
- void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
- void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
- bool preparePointerGestures(nsecs_t when,
- bool* outCancelPreviousGesture, bool* outFinishPreviousGesture,
- bool isTimeout);
-
- void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
- void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
- void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
-
- void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
- bool down, bool hovering);
- void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
-
- bool assignExternalStylusId(const RawState& state, bool timeout);
- void applyExternalStylusButtonState(nsecs_t when);
- void applyExternalStylusTouchState(nsecs_t when);
-
- // Dispatches a motion event.
- // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
- // method will take care of setting the index and transmuting the action to DOWN or UP
- // it is the first / last pointer to go down / up.
- void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties, const PointerCoords* coords,
- const uint32_t* idToIndex, BitSet32 idBits,
- int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
-
- // Updates pointer coords and properties for pointers with specified ids that have moved.
- // Returns true if any of them changed.
- bool updateMovedPointers(const PointerProperties* inProperties,
- const PointerCoords* inCoords, const uint32_t* inIdToIndex,
- PointerProperties* outProperties, PointerCoords* outCoords,
- const uint32_t* outIdToIndex, BitSet32 idBits) const;
-
- bool isPointInsideSurface(int32_t x, int32_t y);
- const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
-
- static void assignPointerIds(const RawState* last, RawState* current);
-
- const char* modeToString(DeviceMode deviceMode);
-};
-
-
-class SingleTouchInputMapper : public TouchInputMapper {
-public:
- explicit SingleTouchInputMapper(InputDevice* device);
- virtual ~SingleTouchInputMapper();
-
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
-
-private:
- SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-};
-
-
-class MultiTouchInputMapper : public TouchInputMapper {
-public:
- explicit MultiTouchInputMapper(InputDevice* device);
- virtual ~MultiTouchInputMapper();
-
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
-
-private:
- MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
-
- // Specifies the pointer id bits that are in use, and their associated tracking id.
- BitSet32 mPointerIdBits;
- int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
-};
-
-class ExternalStylusInputMapper : public InputMapper {
-public:
- explicit ExternalStylusInputMapper(InputDevice* device);
- virtual ~ExternalStylusInputMapper() = default;
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
- virtual void sync(nsecs_t when);
-
-private:
- SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
- RawAbsoluteAxisInfo mRawPressureAxis;
- TouchButtonAccumulator mTouchButtonAccumulator;
-
- StylusState mStylusState;
-};
-
-
-class JoystickInputMapper : public InputMapper {
-public:
- explicit JoystickInputMapper(InputDevice* device);
- virtual ~JoystickInputMapper();
-
- virtual uint32_t getSources();
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
- virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent);
-
-private:
- struct Axis {
- RawAbsoluteAxisInfo rawAxisInfo;
- AxisInfo axisInfo;
-
- bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
-
- float scale; // scale factor from raw to normalized values
- float offset; // offset to add after scaling for normalization
- float highScale; // scale factor from raw to normalized values of high split
- float highOffset; // offset to add after scaling for normalization of high split
-
- float min; // normalized inclusive minimum
- float max; // normalized inclusive maximum
- float flat; // normalized flat region size
- float fuzz; // normalized error tolerance
- float resolution; // normalized resolution in units/mm
-
- float filter; // filter out small variations of this size
- float currentValue; // current value
- float newValue; // most recent value
- float highCurrentValue; // current value of high split
- float highNewValue; // most recent value of high split
-
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
- bool explicitlyMapped, float scale, float offset,
- float highScale, float highOffset,
- float min, float max, float flat, float fuzz, float resolution) {
- this->rawAxisInfo = rawAxisInfo;
- this->axisInfo = axisInfo;
- this->explicitlyMapped = explicitlyMapped;
- this->scale = scale;
- this->offset = offset;
- this->highScale = highScale;
- this->highOffset = highOffset;
- this->min = min;
- this->max = max;
- this->flat = flat;
- this->fuzz = fuzz;
- this->resolution = resolution;
- this->filter = 0;
- resetValue();
- }
-
- void resetValue() {
- this->currentValue = 0;
- this->newValue = 0;
- this->highCurrentValue = 0;
- this->highNewValue = 0;
- }
- };
-
- // Axes indexed by raw ABS_* axis index.
- KeyedVector<int32_t, Axis> mAxes;
-
- void sync(nsecs_t when, bool force);
-
- bool haveAxis(int32_t axisId);
- void pruneAxes(bool ignoreExplicitlyMappedAxes);
- bool filterAxes(bool force);
-
- static bool hasValueChangedSignificantly(float filter,
- float newValue, float currentValue, float min, float max);
- static bool hasMovedNearerToValueWithinFilteredRange(float filter,
- float newValue, float currentValue, float thresholdValue);
-
- static bool isCenteredAxis(int32_t axis);
- static int32_t getCompatAxis(int32_t axis);
-
- static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
- static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
- float value);
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_READER_H
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index bc53cf5..0422d83 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -114,8 +114,10 @@
std::optional<DisplayViewport> result = std::nullopt;
for (const DisplayViewport& currentViewport : mDisplays) {
// Return the first match
- if (currentViewport.type == type && !result) {
- result = std::make_optional(currentViewport);
+ if (currentViewport.type == type) {
+ if (!result) {
+ result = std::make_optional(currentViewport);
+ }
count++;
}
}
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
new file mode 100644
index 0000000..9185e00
--- /dev/null
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2019 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.
+
+cc_library_static {
+ name: "libinputdispatcher",
+ defaults: ["inputflinger_defaults"],
+ srcs: [
+ "Connection.cpp",
+ "Entry.cpp",
+ "InjectionState.cpp",
+ "InputDispatcher.cpp",
+ "InputDispatcherFactory.cpp",
+ "InputDispatcherThread.cpp",
+ "InputState.cpp",
+ "InputTarget.cpp",
+ "Monitor.cpp",
+ "TouchState.cpp"
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libinput",
+ "libinputreporter",
+ "libinputflinger_base",
+ "liblog",
+ "libstatslog",
+ "libui",
+ "libutils",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
new file mode 100644
index 0000000..99e2108
--- /dev/null
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+
+#include <optional>
+
+namespace android::inputdispatcher {
+
+/* Specifies which events are to be canceled and why. */
+struct CancelationOptions {
+ enum Mode {
+ CANCEL_ALL_EVENTS = 0,
+ CANCEL_POINTER_EVENTS = 1,
+ CANCEL_NON_POINTER_EVENTS = 2,
+ CANCEL_FALLBACK_EVENTS = 3,
+ };
+
+ // The criterion to use to determine which events should be canceled.
+ Mode mode;
+
+ // Descriptive reason for the cancelation.
+ const char* reason;
+
+ // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
+ std::optional<int32_t> keyCode = std::nullopt;
+
+ // The specific device id of events to cancel, or nullopt to cancel events from any device.
+ std::optional<int32_t> deviceId = std::nullopt;
+
+ // The specific display id of events to cancel, or nullopt to cancel events on any display.
+ std::optional<int32_t> displayId = std::nullopt;
+
+ CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {}
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
new file mode 100644
index 0000000..6f82f4f
--- /dev/null
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Connection.h"
+
+#include "Entry.h"
+
+namespace android::inputdispatcher {
+
+Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor)
+ : status(STATUS_NORMAL),
+ inputChannel(inputChannel),
+ monitor(monitor),
+ inputPublisher(inputChannel),
+ inputPublisherBlocked(false) {}
+
+Connection::~Connection() {}
+
+const std::string Connection::getWindowName() const {
+ if (inputChannel != nullptr) {
+ return inputChannel->getName();
+ }
+ if (monitor) {
+ return "monitor";
+ }
+ return "?";
+}
+
+const char* Connection::getStatusLabel() const {
+ switch (status) {
+ case STATUS_NORMAL:
+ return "NORMAL";
+ case STATUS_BROKEN:
+ return "BROKEN";
+ case STATUS_ZOMBIE:
+ return "ZOMBIE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
+ for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
+ if ((*it)->seq == seq) {
+ return it;
+ }
+ }
+ return waitQueue.end();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
new file mode 100644
index 0000000..8423010
--- /dev/null
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+
+#include "InputState.h"
+
+#include <input/InputTransport.h>
+#include <deque>
+
+namespace android::inputdispatcher {
+
+struct DispatchEntry;
+
+/* Manages the dispatch state associated with a single input channel. */
+class Connection : public RefBase {
+protected:
+ virtual ~Connection();
+
+public:
+ enum Status {
+ // Everything is peachy.
+ STATUS_NORMAL,
+ // An unrecoverable communication error has occurred.
+ STATUS_BROKEN,
+ // The input channel has been unregistered.
+ STATUS_ZOMBIE
+ };
+
+ Status status;
+ sp<InputChannel> inputChannel; // never null
+ bool monitor;
+ InputPublisher inputPublisher;
+ InputState inputState;
+
+ // True if the socket is full and no further events can be published until
+ // the application consumes some of the input.
+ bool inputPublisherBlocked;
+
+ // Queue of events that need to be published to the connection.
+ std::deque<DispatchEntry*> outboundQueue;
+
+ // Queue of events that have been published to the connection but that have not
+ // yet received a "finished" response from the application.
+ std::deque<DispatchEntry*> waitQueue;
+
+ explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
+
+ inline const std::string getInputChannelName() const { return inputChannel->getName(); }
+
+ const std::string getWindowName() const;
+ const char* getStatusLabel() const;
+
+ std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
new file mode 100644
index 0000000..640a69a
--- /dev/null
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Entry.h"
+
+#include "Connection.h"
+
+#include <android-base/stringprintf.h>
+#include <cutils/atomic.h>
+#include <inttypes.h>
+
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+static std::string motionActionToString(int32_t action) {
+ // Convert MotionEvent action to string
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
+ case AMOTION_EVENT_ACTION_UP:
+ return "UP";
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ return "POINTER_DOWN";
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ return "POINTER_UP";
+ }
+ return StringPrintf("%" PRId32, action);
+}
+
+static std::string keyActionToString(int32_t action) {
+ // Convert KeyEvent action to string
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AKEY_EVENT_ACTION_UP:
+ return "UP";
+ case AKEY_EVENT_ACTION_MULTIPLE:
+ return "MULTIPLE";
+ }
+ return StringPrintf("%" PRId32, action);
+}
+
+// --- EventEntry ---
+
+EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
+ : sequenceNum(sequenceNum),
+ refCount(1),
+ type(type),
+ eventTime(eventTime),
+ policyFlags(policyFlags),
+ injectionState(nullptr),
+ dispatchInProgress(false) {}
+
+EventEntry::~EventEntry() {
+ releaseInjectionState();
+}
+
+void EventEntry::release() {
+ refCount -= 1;
+ if (refCount == 0) {
+ delete this;
+ } else {
+ ALOG_ASSERT(refCount > 0);
+ }
+}
+
+void EventEntry::releaseInjectionState() {
+ if (injectionState) {
+ injectionState->release();
+ injectionState = nullptr;
+ }
+}
+
+// --- ConfigurationChangedEntry ---
+
+ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
+ : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+
+ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
+
+void ConfigurationChangedEntry::appendDescription(std::string& msg) const {
+ msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+}
+
+// --- DeviceResetEntry ---
+
+DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
+ : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+
+DeviceResetEntry::~DeviceResetEntry() {}
+
+void DeviceResetEntry::appendDescription(std::string& msg) const {
+ msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
+}
+
+// --- KeyEntry ---
+
+KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime)
+ : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+ deviceId(deviceId),
+ source(source),
+ displayId(displayId),
+ action(action),
+ flags(flags),
+ keyCode(keyCode),
+ scanCode(scanCode),
+ metaState(metaState),
+ repeatCount(repeatCount),
+ downTime(downTime),
+ syntheticRepeat(false),
+ interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+ interceptKeyWakeupTime(0) {}
+
+KeyEntry::~KeyEntry() {}
+
+void KeyEntry::appendDescription(std::string& msg) const {
+ msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+ "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+ "repeatCount=%d), policyFlags=0x%08x",
+ deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+ keyCode, scanCode, metaState, repeatCount, policyFlags);
+}
+
+void KeyEntry::recycle() {
+ releaseInjectionState();
+
+ dispatchInProgress = false;
+ syntheticRepeat = false;
+ interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ interceptKeyWakeupTime = 0;
+}
+
+// --- MotionEntry ---
+
+MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xOffset, float yOffset)
+ : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+ eventTime(eventTime),
+ deviceId(deviceId),
+ source(source),
+ displayId(displayId),
+ action(action),
+ actionButton(actionButton),
+ flags(flags),
+ metaState(metaState),
+ buttonState(buttonState),
+ classification(classification),
+ edgeFlags(edgeFlags),
+ xPrecision(xPrecision),
+ yPrecision(yPrecision),
+ xCursorPosition(xCursorPosition),
+ yCursorPosition(yCursorPosition),
+ downTime(downTime),
+ pointerCount(pointerCount) {
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ this->pointerProperties[i].copyFrom(pointerProperties[i]);
+ this->pointerCoords[i].copyFrom(pointerCoords[i]);
+ if (xOffset || yOffset) {
+ this->pointerCoords[i].applyOffset(xOffset, yOffset);
+ }
+ }
+}
+
+MotionEntry::~MotionEntry() {}
+
+void MotionEntry::appendDescription(std::string& msg) const {
+ msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
+ ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
+ "buttonState=0x%08x, "
+ "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
+ "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
+ deviceId, source, displayId, motionActionToString(action).c_str(),
+ actionButton, flags, metaState, buttonState,
+ motionClassificationToString(classification), edgeFlags, xPrecision,
+ yPrecision, xCursorPosition, yCursorPosition);
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ if (i) {
+ msg += ", ";
+ }
+ msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(),
+ pointerCoords[i].getY());
+ }
+ msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
+}
+
+// --- DispatchEntry ---
+
+volatile int32_t DispatchEntry::sNextSeqAtomic;
+
+DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
+ float yOffset, float globalScaleFactor, float windowXScale,
+ float windowYScale)
+ : seq(nextSeq()),
+ eventEntry(eventEntry),
+ targetFlags(targetFlags),
+ xOffset(xOffset),
+ yOffset(yOffset),
+ globalScaleFactor(globalScaleFactor),
+ windowXScale(windowXScale),
+ windowYScale(windowYScale),
+ deliveryTime(0),
+ resolvedAction(0),
+ resolvedFlags(0) {
+ eventEntry->refCount += 1;
+}
+
+DispatchEntry::~DispatchEntry() {
+ eventEntry->release();
+}
+
+uint32_t DispatchEntry::nextSeq() {
+ // Sequence number 0 is reserved and will never be returned.
+ uint32_t seq;
+ do {
+ seq = android_atomic_inc(&sNextSeqAtomic);
+ } while (!seq);
+ return seq;
+}
+
+// --- CommandEntry ---
+
+CommandEntry::CommandEntry(Command command)
+ : command(command),
+ eventTime(0),
+ keyEntry(nullptr),
+ userActivityEventType(0),
+ seq(0),
+ handled(false) {}
+
+CommandEntry::~CommandEntry() {}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
new file mode 100644
index 0000000..28c2799
--- /dev/null
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+
+#include "InjectionState.h"
+#include "InputTarget.h"
+
+#include <input/Input.h>
+#include <input/InputApplication.h>
+#include <stdint.h>
+#include <utils/Timers.h>
+#include <functional>
+#include <string>
+
+namespace android::inputdispatcher {
+
+// Sequence number for synthesized or injected events.
+constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+
+struct EventEntry {
+ enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
+
+ uint32_t sequenceNum;
+ mutable int32_t refCount;
+ int32_t type;
+ nsecs_t eventTime;
+ uint32_t policyFlags;
+ InjectionState* injectionState;
+
+ bool dispatchInProgress; // initially false, set to true while dispatching
+
+ /**
+ * Injected keys are events from an external (probably untrusted) application
+ * and are not related to real hardware state. They come in via
+ * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED.
+ */
+ inline bool isInjected() const { return injectionState != nullptr; }
+
+ /**
+ * Synthesized events are either injected events, or events that come
+ * from real hardware, but aren't directly attributable to a specific hardware event.
+ * Key repeat is a synthesized event, because it is related to an actual hardware state
+ * (a key is currently pressed), but the repeat itself is generated by the framework.
+ */
+ inline bool isSynthesized() const {
+ return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM;
+ }
+
+ void release();
+
+ virtual void appendDescription(std::string& msg) const = 0;
+
+protected:
+ EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+ virtual ~EventEntry();
+ void releaseInjectionState();
+};
+
+struct ConfigurationChangedEntry : EventEntry {
+ explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
+ virtual void appendDescription(std::string& msg) const;
+
+protected:
+ virtual ~ConfigurationChangedEntry();
+};
+
+struct DeviceResetEntry : EventEntry {
+ int32_t deviceId;
+
+ DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
+ virtual void appendDescription(std::string& msg) const;
+
+protected:
+ virtual ~DeviceResetEntry();
+};
+
+struct KeyEntry : EventEntry {
+ int32_t deviceId;
+ 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
+
+ enum InterceptKeyResult {
+ INTERCEPT_KEY_RESULT_UNKNOWN,
+ INTERCEPT_KEY_RESULT_SKIP,
+ INTERCEPT_KEY_RESULT_CONTINUE,
+ INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
+ };
+ InterceptKeyResult interceptKeyResult; // set based on the interception result
+ nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+
+ KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime);
+ virtual void appendDescription(std::string& msg) const;
+ void recycle();
+
+protected:
+ virtual ~KeyEntry();
+};
+
+struct MotionEntry : EventEntry {
+ nsecs_t eventTime;
+ int32_t deviceId;
+ uint32_t source;
+ int32_t displayId;
+ int32_t action;
+ int32_t actionButton;
+ int32_t flags;
+ int32_t metaState;
+ int32_t buttonState;
+ MotionClassification classification;
+ int32_t edgeFlags;
+ float xPrecision;
+ float yPrecision;
+ float xCursorPosition;
+ float yCursorPosition;
+ nsecs_t downTime;
+ uint32_t pointerCount;
+ PointerProperties pointerProperties[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+
+ MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, int32_t edgeFlags, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xOffset, float yOffset);
+ virtual void appendDescription(std::string& msg) const;
+
+protected:
+ virtual ~MotionEntry();
+};
+
+// Tracks the progress of dispatching a particular event to a particular connection.
+struct DispatchEntry {
+ const uint32_t seq; // unique sequence number, never 0
+
+ EventEntry* eventEntry; // the event to dispatch
+ int32_t targetFlags;
+ float xOffset;
+ float yOffset;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+ nsecs_t deliveryTime; // time when the event was actually delivered
+
+ // Set to the resolved action and flags when the event is enqueued.
+ int32_t resolvedAction;
+ int32_t resolvedFlags;
+
+ DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
+ float globalScaleFactor, float windowXScale, float windowYScale);
+ ~DispatchEntry();
+
+ inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
+
+ inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; }
+
+private:
+ static volatile int32_t sNextSeqAtomic;
+
+ static uint32_t nextSeq();
+};
+
+class InputDispatcher;
+// 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
+// a kind of continuation used to postpone sensitive policy interactions to a point
+// in the dispatch loop where it is safe to release the lock (generally after finishing
+// the critical parts of the dispatch cycle).
+//
+// The special thing about commands is that they can voluntarily release and reacquire
+// the dispatcher lock at will. Initially when the command starts running, the
+// dispatcher lock is held. However, if the command needs to call into the policy to
+// do some work, it can release the lock, do the work, then reacquire the lock again
+// before returning.
+//
+// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+// never calls into the policy while holding its lock.
+//
+// Commands are implicitly 'LockedInterruptible'.
+struct CommandEntry;
+typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
+
+class Connection;
+struct CommandEntry {
+ explicit CommandEntry(Command command);
+ ~CommandEntry();
+
+ Command command;
+
+ // parameters for the command (usage varies by command)
+ sp<Connection> connection;
+ nsecs_t eventTime;
+ KeyEntry* keyEntry;
+ sp<InputApplicationHandle> inputApplicationHandle;
+ std::string reason;
+ int32_t userActivityEventType;
+ uint32_t seq;
+ bool handled;
+ sp<InputChannel> inputChannel;
+ sp<IBinder> oldToken;
+ sp<IBinder> newToken;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
new file mode 100644
index 0000000..b2d0a26
--- /dev/null
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InjectionState.h"
+
+#include <log/log.h>
+
+namespace android::inputdispatcher {
+
+InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid)
+ : refCount(1),
+ injectorPid(injectorPid),
+ injectorUid(injectorUid),
+ injectionResult(INPUT_EVENT_INJECTION_PENDING),
+ injectionIsAsync(false),
+ pendingForegroundDispatches(0) {}
+
+InjectionState::~InjectionState() {}
+
+void InjectionState::release() {
+ refCount -= 1;
+ if (refCount == 0) {
+ delete this;
+ } else {
+ ALOG_ASSERT(refCount > 0);
+ }
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
new file mode 100644
index 0000000..311a0f1
--- /dev/null
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+
+#include "InputDispatcherInterface.h"
+
+#include <stdint.h>
+
+namespace android::inputdispatcher {
+
+/*
+ * Constants used to determine the input event injection synchronization mode.
+ */
+enum {
+ /* Injection is asynchronous and is assumed always to be successful. */
+ INPUT_EVENT_INJECTION_SYNC_NONE = 0,
+
+ /* Waits for previous events to be dispatched so that the input dispatcher can determine
+ * whether input event injection willbe permitted based on the current input focus.
+ * Does not wait for the input event to finish processing. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
+
+ /* Waits for the input event to be completely processed. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
+};
+
+struct InjectionState {
+ mutable int32_t refCount;
+
+ int32_t injectorPid;
+ int32_t injectorUid;
+ int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ bool injectionIsAsync; // set to true if injection is not waiting for the result
+ int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+
+ InjectionState(int32_t injectorPid, int32_t injectorUid);
+ void release();
+
+private:
+ ~InjectionState();
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
similarity index 61%
rename from services/inputflinger/InputDispatcher.cpp
rename to services/inputflinger/dispatcher/InputDispatcher.cpp
index c516ab7..4db9ae2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -35,7 +35,7 @@
#define DEBUG_INJECTION 0
// Log debug messages about input focus tracking.
-#define DEBUG_FOCUS 0
+static constexpr bool DEBUG_FOCUS = false;
// Log debug messages about the app switch latency optimization.
#define DEBUG_APP_SWITCH 0
@@ -45,9 +45,12 @@
#include "InputDispatcher.h"
+#include "Connection.h"
+
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <statslog.h>
#include <stddef.h>
#include <time.h>
#include <unistd.h>
@@ -56,10 +59,10 @@
#include <android-base/chrono_utils.h>
#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-#include <powermanager/PowerManager.h>
#include <binder/Binder.h>
+#include <log/log.h>
+#include <powermanager/PowerManager.h>
+#include <utils/Trace.h>
#define INDENT " "
#define INDENT2 " "
@@ -68,7 +71,7 @@
using android::base::StringPrintf;
-namespace android {
+namespace android::inputdispatcher {
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
@@ -98,10 +101,6 @@
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
-// Sequence number for synthesized or injected events.
-constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
-
-
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -110,71 +109,23 @@
return value ? "true" : "false";
}
-static std::string motionActionToString(int32_t action) {
- // Convert MotionEvent action to string
- switch(action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
- case AMOTION_EVENT_ACTION_UP:
- return "UP";
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
- case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
- }
- return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
- // Convert KeyEvent action to string
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- return "DOWN";
- case AKEY_EVENT_ACTION_UP:
- return "UP";
- case AKEY_EVENT_ACTION_MULTIPLE:
- return "MULTIPLE";
- }
- return StringPrintf("%" PRId32, action);
-}
-
-static std::string dispatchModeToString(int32_t dispatchMode) {
- switch (dispatchMode) {
- case InputTarget::FLAG_DISPATCH_AS_IS:
- return "DISPATCH_AS_IS";
- case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
- return "DISPATCH_AS_OUTSIDE";
- case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
- return "DISPATCH_AS_HOVER_ENTER";
- case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
- return "DISPATCH_AS_HOVER_EXIT";
- case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
- return "DISPATCH_AS_SLIPPERY_EXIT";
- case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
- return "DISPATCH_AS_SLIPPERY_ENTER";
- }
- return StringPrintf("%" PRId32, dispatchMode);
-}
-
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
- return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
static bool isValidKeyAction(int32_t action) {
switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- case AKEY_EVENT_ACTION_UP:
- return true;
- default:
- return false;
+ case AKEY_EVENT_ACTION_DOWN:
+ case AKEY_EVENT_ACTION_UP:
+ return true;
+ default:
+ return false;
}
}
static bool validateKeyEvent(int32_t action) {
- if (! isValidKeyAction(action)) {
+ if (!isValidKeyAction(action)) {
ALOGE("Key event has invalid action code 0x%x", action);
return false;
}
@@ -183,46 +134,46 @@
static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
- case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_EXIT:
- case AMOTION_EVENT_ACTION_SCROLL:
- return true;
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && index < pointerCount;
- }
- case AMOTION_EVENT_ACTION_BUTTON_PRESS:
- case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
- return actionButton != 0;
- default:
- return false;
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return true;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ int32_t index = getMotionEventActionPointerIndex(action);
+ return index >= 0 && index < pointerCount;
+ }
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
+ return actionButton != 0;
+ default:
+ return false;
}
}
static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
- const PointerProperties* pointerProperties) {
- if (! isValidMotionAction(action, actionButton, pointerCount)) {
+ const PointerProperties* pointerProperties) {
+ if (!isValidMotionAction(action, actionButton, pointerCount)) {
ALOGE("Motion event has invalid action code 0x%x", action);
return false;
}
if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.",
- pointerCount, MAX_POINTERS);
+ pointerCount, MAX_POINTERS);
return false;
}
BitSet32 pointerIdBits;
for (size_t i = 0; i < pointerCount; i++) {
int32_t id = pointerProperties[i].id;
if (id < 0 || id > MAX_POINTER_ID) {
- ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
- id, MAX_POINTER_ID);
+ ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id,
+ MAX_POINTER_ID);
return false;
}
if (pointerIdBits.hasBit(id)) {
@@ -363,9 +314,9 @@
// If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
-#if DEBUG_FOCUS
- ALOGD("Dispatch frozen. Waiting some more.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Dispatch frozen. Waiting some more.");
+ }
return;
}
@@ -379,7 +330,7 @@
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
- if (! mPendingEvent) {
+ if (!mPendingEvent) {
if (mInboundQueue.empty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
@@ -435,63 +386,59 @@
}
switch (mPendingEvent->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(mPendingEvent);
- done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
- dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
- break;
- }
+ case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ ConfigurationChangedEntry* typedEntry =
+ static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+ dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
+ break;
+ }
- case EventEntry::TYPE_DEVICE_RESET: {
- DeviceResetEntry* typedEntry =
- static_cast<DeviceResetEntry*>(mPendingEvent);
- done = dispatchDeviceResetLocked(currentTime, typedEntry);
- dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
- break;
- }
+ case EventEntry::TYPE_DEVICE_RESET: {
+ DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
+ done = dispatchDeviceResetLocked(currentTime, typedEntry);
+ dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
+ break;
+ }
- case EventEntry::TYPE_KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
- if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(typedEntry)) {
- resetPendingAppSwitchLocked(true);
- isAppSwitchDue = false;
- } else if (dropReason == DROP_REASON_NOT_DROPPED) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ if (isAppSwitchDue) {
+ if (isAppSwitchKeyEvent(typedEntry)) {
+ resetPendingAppSwitchLocked(true);
+ isAppSwitchDue = false;
+ } else if (dropReason == DROP_REASON_NOT_DROPPED) {
+ dropReason = DROP_REASON_APP_SWITCH;
+ }
+ }
+ if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+ dropReason = DROP_REASON_STALE;
+ }
+ if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+ dropReason = DROP_REASON_BLOCKED;
+ }
+ done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
+ if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+ dropReason = DROP_REASON_STALE;
+ }
+ if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+ dropReason = DROP_REASON_BLOCKED;
+ }
+ done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ break;
}
- if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEvent(currentTime, typedEntry)) {
- dropReason = DROP_REASON_STALE;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DROP_REASON_BLOCKED;
- }
- done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
- break;
- }
- case EventEntry::TYPE_MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
- if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
- dropReason = DROP_REASON_APP_SWITCH;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEvent(currentTime, typedEntry)) {
- dropReason = DROP_REASON_STALE;
- }
- if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DROP_REASON_BLOCKED;
- }
- done = dispatchMotionLocked(currentTime, typedEntry,
- &dropReason, nextWakeupTime);
- break;
- }
-
- default:
- ALOG_ASSERT(false);
- break;
+ default:
+ ALOG_ASSERT(false);
+ break;
}
if (done) {
@@ -501,7 +448,7 @@
mLastDropReason = dropReason;
releasePendingEventLocked();
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
@@ -511,55 +458,56 @@
traceInboundQueueLengthLocked();
switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- // Optimize app switch latency.
- // If the application takes too long to catch up then we drop all events preceding
- // the app switch key.
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEvent(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
+ case EventEntry::TYPE_KEY: {
+ // Optimize app switch latency.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the app switch key.
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+ if (isAppSwitchKeyEvent(keyEntry)) {
+ if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+ mAppSwitchSawKeyDown = true;
+ } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
- ALOGD("App switch is pending!");
+ ALOGD("App switch is pending!");
#endif
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
+ mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+ mAppSwitchSawKeyDown = false;
+ needWake = true;
+ }
+ }
+ }
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ // Optimize case where the current application is unresponsive and the user
+ // decides to touch a window in a different application.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the touch into the other window.
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
+ (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+ mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
+ mInputTargetWaitApplicationToken != nullptr) {
+ int32_t displayId = motionEntry->displayId;
+ int32_t x =
+ int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y =
+ int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ sp<InputWindowHandle> touchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y);
+ if (touchedWindowHandle != nullptr &&
+ touchedWindowHandle->getApplicationToken() !=
+ mInputTargetWaitApplicationToken) {
+ // User touched a different application than the one we are waiting on.
+ // Flag the event, and start pruning the input queue.
+ mNextUnblockedEvent = motionEntry;
needWake = true;
}
}
+ break;
}
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- // Optimize case where the current application is unresponsive and the user
- // decides to touch a window in a different application.
- // If the application takes too long to catch up then we drop all events preceding
- // the touch into the other window.
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
- && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
- && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
- && mInputTargetWaitApplicationToken != nullptr) {
- int32_t displayId = motionEntry->displayId;
- int32_t x = int32_t(motionEntry->pointerCoords[0].
- getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(motionEntry->pointerCoords[0].
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
- if (touchedWindowHandle != nullptr
- && touchedWindowHandle->getApplicationToken()
- != mInputTargetWaitApplicationToken) {
- // User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- mNextUnblockedEvent = motionEntry;
- needWake = true;
- }
- }
- break;
- }
}
return needWake;
@@ -574,8 +522,9 @@
}
}
-sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
- int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
+sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
+ int32_t y, bool addOutsideTargets,
+ bool addPortalWindows) {
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -585,18 +534,19 @@
if (windowInfo->visible) {
if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
- | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+ bool isTouchModal = (flags &
+ (InputWindowInfo::FLAG_NOT_FOCUSABLE |
+ InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
int32_t portalToDisplayId = windowInfo->portalToDisplayId;
- if (portalToDisplayId != ADISPLAY_ID_NONE
- && portalToDisplayId != displayId) {
+ if (portalToDisplayId != ADISPLAY_ID_NONE &&
+ portalToDisplayId != displayId) {
if (addPortalWindows) {
// For the monitoring channels of the display.
mTempTouchState.addPortalWindow(windowHandle);
}
- return findTouchedWindowAtLocked(
- portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+ return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+ addOutsideTargets, addPortalWindows);
}
// Found window.
return windowHandle;
@@ -604,8 +554,9 @@
}
if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(
- windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+ mTempTouchState.addOrUpdateWindow(windowHandle,
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ BitSet32(0));
}
}
}
@@ -613,7 +564,7 @@
return nullptr;
}
-std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
+std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
std::vector<TouchedMonitor> touchedMonitors;
@@ -622,14 +573,15 @@
for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
const InputWindowInfo* windowInfo = portalWindow->getInfo();
monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
- addGestureMonitors(monitors, touchedMonitors,
- -windowInfo->frameLeft, -windowInfo->frameTop);
+ addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
+ -windowInfo->frameTop);
}
return touchedMonitors;
}
void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset, float yOffset) {
+ std::vector<TouchedMonitor>& outTouchedMonitors,
+ float xOffset, float yOffset) {
if (monitors.empty()) {
return;
}
@@ -642,68 +594,66 @@
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
- case DROP_REASON_POLICY:
+ case DROP_REASON_POLICY:
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("Dropped event because policy consumed it.");
+ ALOGD("Dropped event because policy consumed it.");
#endif
- reason = "inbound event was dropped because the policy consumed it";
- break;
- case DROP_REASON_DISABLED:
- if (mLastDropReason != DROP_REASON_DISABLED) {
- ALOGI("Dropped event because input dispatch is disabled.");
- }
- reason = "inbound event was dropped because input dispatch is disabled";
- break;
- case DROP_REASON_APP_SWITCH:
- ALOGI("Dropped event because of pending overdue app switch.");
- reason = "inbound event was dropped because of pending overdue app switch";
- break;
- case DROP_REASON_BLOCKED:
- ALOGI("Dropped event because the current application is not responding and the user "
- "has started interacting with a different application.");
- reason = "inbound event was dropped because the current application is not responding "
- "and the user has started interacting with a different application";
- break;
- case DROP_REASON_STALE:
- ALOGI("Dropped event because it is stale.");
- reason = "inbound event was dropped because it is stale";
- break;
- default:
- ALOG_ASSERT(false);
- return;
+ reason = "inbound event was dropped because the policy consumed it";
+ break;
+ case DROP_REASON_DISABLED:
+ if (mLastDropReason != DROP_REASON_DISABLED) {
+ ALOGI("Dropped event because input dispatch is disabled.");
+ }
+ reason = "inbound event was dropped because input dispatch is disabled";
+ break;
+ case DROP_REASON_APP_SWITCH:
+ ALOGI("Dropped event because of pending overdue app switch.");
+ reason = "inbound event was dropped because of pending overdue app switch";
+ break;
+ case DROP_REASON_BLOCKED:
+ ALOGI("Dropped event because the current application is not responding and the user "
+ "has started interacting with a different application.");
+ reason = "inbound event was dropped because the current application is not responding "
+ "and the user has started interacting with a different application";
+ break;
+ case DROP_REASON_STALE:
+ ALOGI("Dropped event because it is stale.");
+ reason = "inbound event was dropped because it is stale";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ return;
}
switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- break;
- }
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- } else {
+ case EventEntry::TYPE_KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
+ break;
}
- break;
- }
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
+ } else {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
+ }
+ break;
+ }
}
}
static bool isAppSwitchKeyCode(int32_t keyCode) {
- return keyCode == AKEYCODE_HOME
- || keyCode == AKEYCODE_ENDCALL
- || keyCode == AKEYCODE_APP_SWITCH;
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL ||
+ keyCode == AKEYCODE_APP_SWITCH;
}
bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
- return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
- && isAppSwitchKeyCode(keyEntry->keyCode)
- && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
- && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+ return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) &&
+ (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) &&
+ (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
}
bool InputDispatcher::isAppSwitchPendingLocked() {
@@ -789,7 +739,7 @@
}
}
-InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
+KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
// Reuse the repeated key entry if it is otherwise unreferenced.
@@ -801,10 +751,11 @@
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
} else {
- KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
- entry->deviceId, entry->source, entry->displayId, policyFlags,
- entry->action, entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount + 1, entry->downTime);
+ KeyEntry* newEntry =
+ new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId,
+ entry->source, entry->displayId, policyFlags, entry->action,
+ entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->repeatCount + 1, entry->downTime);
mKeyRepeatState.lastKeyEntry = newEntry;
entry->release();
@@ -821,8 +772,8 @@
return entry;
}
-bool InputDispatcher::dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
+ ConfigurationChangedEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
#endif
@@ -838,30 +789,27 @@
return true;
}
-bool InputDispatcher::dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry) {
+bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
- entry->deviceId);
+ entry->deviceId);
#endif
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "device was reset");
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry->deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
}
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
- if (! entry->dispatchInProgress) {
- if (entry->repeatCount == 0
- && entry->action == AKEY_EVENT_ACTION_DOWN
- && (entry->policyFlags & POLICY_FLAG_TRUSTED)
- && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
- if (mKeyRepeatState.lastKeyEntry
- && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ if (!entry->dispatchInProgress) {
+ if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
+ (entry->policyFlags & POLICY_FLAG_TRUSTED) &&
+ (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
+ if (mKeyRepeatState.lastKeyEntry &&
+ mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
// driver is automatically generating key repeats itself. We take note of the
// repeat here, but we disable our own next key repeat timer since it is clear that
@@ -876,7 +824,7 @@
}
mKeyRepeatState.lastKeyEntry = entry;
entry->refCount += 1;
- } else if (! entry->syntheticRepeat) {
+ } else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -911,8 +859,7 @@
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
if (focusedWindowHandle != nullptr) {
- commandEntry->inputChannel =
- getInputChannelLocked(focusedWindowHandle->getToken());
+ commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
}
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
@@ -929,16 +876,17 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
- ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(entry,
+ *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+ : INPUT_EVENT_INJECTION_FAILED);
mReporter->reportDroppedKey(entry->sequenceNum);
return true;
}
// Identify targets.
std::vector<InputTarget> inputTargets;
- int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime);
+ int32_t injectionResult =
+ findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
@@ -959,20 +907,19 @@
void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
- "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
- "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
- prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
- entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
- entry->repeatCount, entry->downTime);
+ "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+ "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+ prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
+ entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode,
+ entry->metaState, entry->repeatCount, entry->downTime);
#endif
}
-bool InputDispatcher::dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
// Preprocessing.
- if (! entry->dispatchInProgress) {
+ if (!entry->dispatchInProgress) {
entry->dispatchInProgress = true;
logOutboundMotionDetails("dispatchMotion - ", entry);
@@ -980,8 +927,9 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
- ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(entry,
+ *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+ : INPUT_EVENT_INJECTION_FAILED);
return true;
}
@@ -994,12 +942,13 @@
int32_t injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
- injectionResult = findTouchedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
+ injectionResult =
+ findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime,
+ &conflictingPointerActions);
} else {
// Non touch event. (eg. trackball)
- injectionResult = findFocusedWindowTargetsLocked(currentTime,
- entry, inputTargets, nextWakeupTime);
+ injectionResult =
+ findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
@@ -1008,9 +957,9 @@
setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
- CancelationOptions::Mode mode(isPointerEvent ?
- CancelationOptions::CANCEL_POINTER_EVENTS :
- CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+ CancelationOptions::Mode mode(isPointerEvent
+ ? CancelationOptions::CANCEL_POINTER_EVENTS
+ : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
CancelationOptions options(mode, "input event injection failed");
synthesizeCancelationEventsForMonitorsLocked(options);
}
@@ -1030,7 +979,7 @@
for (size_t i = 0; i < state.portalWindows.size(); i++) {
const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo();
addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
- -windowInfo->frameLeft, -windowInfo->frameTop);
+ -windowInfo->frameLeft, -windowInfo->frameTop);
}
}
}
@@ -1039,50 +988,46 @@
// Dispatch the motion.
if (conflictingPointerActions) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "conflicting pointer actions");
+ "conflicting pointer actions");
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
-
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
- ", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, "
- "metaState=0x%x, buttonState=0x%x,"
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
- entry->action, entry->actionButton, entry->flags,
- entry->metaState, entry->buttonState,
- entry->edgeFlags, entry->xPrecision, entry->yPrecision,
- entry->downTime);
+ ", policyFlags=0x%x, "
+ "action=0x%x, actionButton=0x%x, flags=0x%x, "
+ "metaState=0x%x, buttonState=0x%x,"
+ "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+ prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
+ entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState,
+ entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
+ entry->downTime);
for (uint32_t i = 0; i < entry->pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, entry->pointerProperties[i].id,
- entry->pointerProperties[i].toolType,
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ "x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType,
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
#endif
}
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
+ const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
@@ -1097,25 +1042,24 @@
if (connection != nullptr) {
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
} else {
-#if DEBUG_FOCUS
- ALOGD("Dropping event delivery to target with channel '%s' because it "
- "is no longer registered with the input dispatcher.",
- inputTarget.inputChannel->getName().c_str());
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Dropping event delivery to target with channel '%s' because it "
+ "is no longer registered with the input dispatcher.",
+ inputTarget.inputChannel->getName().c_str());
+ }
}
}
}
-int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
- const EventEntry* entry,
+int32_t InputDispatcher::handleTargetsNotReadyLocked(
+ nsecs_t currentTime, const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason) {
+ const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
if (applicationHandle == nullptr && windowHandle == nullptr) {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
-#if DEBUG_FOCUS
- ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
+ }
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
@@ -1124,17 +1068,16 @@
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-#if DEBUG_FOCUS
- ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
- reason);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
+ }
nsecs_t timeout;
if (windowHandle != nullptr) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != nullptr) {
- timeout = applicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+ timeout =
+ applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
@@ -1159,8 +1102,8 @@
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
- onANRLocked(currentTime, applicationHandle, windowHandle,
- entry->eventTime, mInputTargetWaitStartTime, reason);
+ onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime,
+ mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
@@ -1182,8 +1125,8 @@
}
}
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel) {
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
+ nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {
if (newTimeout > 0) {
// Extend the timeout.
mInputTargetWaitTimeoutTime = now() + newTimeout;
@@ -1209,8 +1152,7 @@
}
}
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
- nsecs_t currentTime) {
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
return currentTime - mInputTargetWaitStartTime;
}
@@ -1218,9 +1160,9 @@
}
void InputDispatcher::resetANRTimeoutsLocked() {
-#if DEBUG_FOCUS
+ if (DEBUG_FOCUS) {
ALOGD("Resetting ANR timeouts.");
-#endif
+ }
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
@@ -1235,26 +1177,28 @@
int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
int32_t displayId;
switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
- displayId = typedEntry->displayId;
- break;
- }
- case EventEntry::TYPE_MOTION: {
- const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
- displayId = typedEntry->displayId;
- break;
- }
- default: {
- ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
- return ADISPLAY_ID_NONE;
- }
+ case EventEntry::TYPE_KEY: {
+ const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
+ displayId = typedEntry->displayId;
+ break;
+ }
+ case EventEntry::TYPE_MOTION: {
+ const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
+ displayId = typedEntry->displayId;
+ break;
+ }
+ default: {
+ ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+ return ADISPLAY_ID_NONE;
+ }
}
return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
+ const EventEntry* entry,
+ std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime) {
int32_t injectionResult;
std::string reason;
@@ -1268,16 +1212,18 @@
// then drop the event.
if (focusedWindowHandle == nullptr) {
if (focusedApplicationHandle != nullptr) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- focusedApplicationHandle, nullptr, nextWakeupTime,
- "Waiting because no window has focus but there is a "
- "focused application that may eventually add a window "
- "when it finishes starting up.");
+ injectionResult =
+ handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+ nullptr, nextWakeupTime,
+ "Waiting because no window has focus but there is "
+ "a focused application that may eventually add a "
+ "window when it finishes starting up.");
goto Unresponsive;
}
ALOGI("Dropping event because there is no focused window or focused application in display "
- "%" PRId32 ".", displayId);
+ "%" PRId32 ".",
+ displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1289,36 +1235,38 @@
}
// Check whether the window is ready for more input.
- reason = checkWindowReadyForMoreInputLocked(currentTime,
- focusedWindowHandle, entry, "focused");
+ reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
if (!reason.empty()) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str());
+ injectionResult =
+ handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+ focusedWindowHandle, nextWakeupTime, reason.c_str());
goto Unresponsive;
}
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
addWindowTargetLocked(focusedWindowHandle,
- InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
- inputTargets);
+ InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+ BitSet32(0), inputTargets);
// Done.
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
- ALOGD("findFocusedWindow finished: injectionResult=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, timeSpentWaitingForApplication / 1000000.0);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("findFocusedWindow finished: injectionResult=%d, "
+ "timeSpentWaitingForApplication=%0.1fms",
+ injectionResult, timeSpentWaitingForApplication / 1000000.0);
+ }
return injectionResult;
}
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) {
+ const MotionEntry* entry,
+ std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime,
+ bool* outConflictingPointerActions) {
ATRACE_CALL();
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
@@ -1348,25 +1296,24 @@
}
bool isSplit = mTempTouchState.split;
- bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0
- && (mTempTouchState.deviceId != entry->deviceId
- || mTempTouchState.source != entry->source
- || mTempTouchState.displayId != displayId);
- bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
- bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_SCROLL
- || isHoverAction);
+ bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
+ (mTempTouchState.deviceId != entry->deviceId ||
+ mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId);
+ bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
+ bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE;
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because a pointer for a different device is already down "
- "in display %" PRId32, displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Dropping event because a pointer for a different device is already down "
+ "in display %" PRId32,
+ displayId);
+ }
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_FAILED;
switchedDevice = false;
@@ -1380,10 +1327,11 @@
mTempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
-#if DEBUG_FOCUS
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32, displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGI("Dropping move event because a pointer for a different device is already active "
+ "in display %" PRId32,
+ displayId);
+ }
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
switchedDevice = false;
@@ -1406,16 +1354,17 @@
y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
- displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+ sp<InputWindowHandle> newTouchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
+ true /*addPortalWindows*/);
std::vector<TouchedMonitor> newGestureMonitors = isDown
? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
: std::vector<TouchedMonitor>{};
// Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != nullptr
- && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
+ if (newTouchedWindowHandle != nullptr &&
+ newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
// New window supports splitting, but we should never split mouse events.
isSplit = !isFromMouse;
} else if (isSplit) {
@@ -1432,7 +1381,8 @@
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
- "(%d, %d) in display %" PRId32 ".", x, y, displayId);
+ "(%d, %d) in display %" PRId32 ".",
+ x, y, displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1470,19 +1420,19 @@
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (! mTempTouchState.down) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because the pointer is not down or we previously "
- "dropped the pointer down event in display %" PRId32, displayId);
-#endif
+ if (!mTempTouchState.down) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Dropping event because the pointer is not down or we previously "
+ "dropped the pointer down event in display %" PRId32,
+ displayId);
+ }
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
// Check whether touches should slip outside of the current foreground window.
- if (maskedAction == AMOTION_EVENT_ACTION_MOVE
- && entry->pointerCount == 1
- && mTempTouchState.isSlippery()) {
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 &&
+ mTempTouchState.isSlippery()) {
int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
@@ -1490,26 +1440,25 @@
mTempTouchState.getFirstForegroundWindowHandle();
sp<InputWindowHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y);
- if (oldTouchedWindowHandle != newTouchedWindowHandle
- && oldTouchedWindowHandle != nullptr
- && newTouchedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
- ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
- oldTouchedWindowHandle->getName().c_str(),
- newTouchedWindowHandle->getName().c_str(),
- displayId);
-#endif
+ if (oldTouchedWindowHandle != newTouchedWindowHandle &&
+ oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+ oldTouchedWindowHandle->getName().c_str(),
+ newTouchedWindowHandle->getName().c_str(), displayId);
+ }
// Make a slippery exit from the old window.
mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ BitSet32(0));
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
isSplit = true;
}
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND
- | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+ int32_t targetFlags =
+ InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
if (isSplit) {
targetFlags |= InputTarget::FLAG_SPLIT;
}
@@ -1531,20 +1480,22 @@
if (mLastHoverWindowHandle != nullptr) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
- mLastHoverWindowHandle->getName().c_str());
+ mLastHoverWindowHandle->getName().c_str());
#endif
mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
+ BitSet32(0));
}
// Let the new window know that the hover sequence is starting.
if (newHoverWindowHandle != nullptr) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
- newHoverWindowHandle->getName().c_str());
+ newHoverWindowHandle->getName().c_str());
#endif
mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+ BitSet32(0));
}
}
@@ -1555,8 +1506,7 @@
for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
- if (! checkInjectionPermission(touchedWindow.windowHandle,
- entry->injectionState)) {
+ if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
@@ -1565,10 +1515,11 @@
}
bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
if (!haveForegroundWindow && !hasGestureMonitor) {
-#if DEBUG_FOCUS
- ALOGD("Dropping event because there is no touched foreground window in display %"
- PRId32 " or gesture monitor to receive it.", displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Dropping event because there is no touched foreground window in display "
+ "%" PRId32 " or gesture monitor to receive it.",
+ displayId);
+ }
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1589,7 +1540,8 @@
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ InputTarget::FLAG_ZERO_COORDS,
+ BitSet32(0));
}
}
}
@@ -1600,11 +1552,13 @@
for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
// Check whether the window is ready for more input.
- std::string reason = checkWindowReadyForMoreInputLocked(currentTime,
- touchedWindow.windowHandle, entry, "touched");
+ std::string reason =
+ checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
+ entry, "touched");
if (!reason.empty()) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
+ touchedWindow.windowHandle,
+ nextWakeupTime, reason.c_str());
goto Unresponsive;
}
}
@@ -1624,14 +1578,15 @@
getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
- if (info->displayId == displayId
- && windowHandle->getInfo()->layoutParamsType
- == InputWindowInfo::TYPE_WALLPAPER) {
- mTempTouchState.addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_WINDOW_IS_OBSCURED
- | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED
- | InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0));
+ if (info->displayId == displayId &&
+ windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
+ mTempTouchState
+ .addOrUpdateWindow(windowHandle,
+ InputTarget::FLAG_WINDOW_IS_OBSCURED |
+ InputTarget::
+ FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
+ InputTarget::FLAG_DISPATCH_AS_IS,
+ BitSet32(0));
}
}
}
@@ -1642,12 +1597,12 @@
for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, inputTargets);
+ touchedWindow.pointerIds, inputTargets);
}
for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
- touchedMonitor.yOffset, inputTargets);
+ touchedMonitor.yOffset, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
@@ -1668,37 +1623,38 @@
if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
if (!wrongDevice) {
if (switchedDevice) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Switched to a different device.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Switched to a different device.");
+ }
*outConflictingPointerActions = true;
}
if (isHoverAction) {
// Started hovering, therefore no longer down.
if (oldState && oldState->down) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Hover received while pointer was down.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Hover received while pointer was "
+ "down.");
+ }
*outConflictingPointerActions = true;
}
mTempTouchState.reset();
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
mTempTouchState.deviceId = entry->deviceId;
mTempTouchState.source = entry->source;
mTempTouchState.displayId = displayId;
}
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP
- || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
mTempTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (oldState && oldState->down) {
-#if DEBUG_FOCUS
- ALOGD("Conflicting pointer actions: Down received while already down.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Down received while already down.");
+ }
*outConflictingPointerActions = true;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -1707,7 +1663,7 @@
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
- for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+ for (size_t i = 0; i < mTempTouchState.windows.size();) {
TouchedWindow& touchedWindow = mTempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
touchedWindow.pointerIds.clearBit(pointerId);
@@ -1739,9 +1695,9 @@
mLastHoverWindowHandle = newHoverWindowHandle;
}
} else {
-#if DEBUG_FOCUS
- ALOGD("Not updating touch focus because injection was denied.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Not updating touch focus because injection was denied.");
+ }
}
Unresponsive:
@@ -1750,16 +1706,17 @@
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
- ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+ "timeSpentWaitingForApplication=%0.1fms",
+ injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
+ }
return injectionResult;
}
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) {
+ int32_t targetFlags, BitSet32 pointerIds,
+ std::vector<InputTarget>& inputTargets) {
sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
@@ -1770,8 +1727,8 @@
InputTarget target;
target.inputChannel = inputChannel;
target.flags = targetFlags;
- target.xOffset = - windowInfo->frameLeft;
- target.yOffset = - windowInfo->frameTop;
+ target.xOffset = -windowInfo->frameLeft;
+ target.yOffset = -windowInfo->frameTop;
target.globalScaleFactor = windowInfo->globalScaleFactor;
target.windowXScale = windowInfo->windowXScale;
target.windowYScale = windowInfo->windowYScale;
@@ -1780,8 +1737,8 @@
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId, float xOffset, float yOffset) {
-
+ int32_t displayId, float xOffset,
+ float yOffset) {
std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
mGlobalMonitorsByDisplay.find(displayId);
@@ -1793,8 +1750,9 @@
}
}
-void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor,
- float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) {
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
+ float yOffset,
+ std::vector<InputTarget>& inputTargets) {
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -1806,28 +1764,27 @@
}
bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
- const InjectionState* injectionState) {
- if (injectionState
- && (windowHandle == nullptr
- || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
- && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
+ const InjectionState* injectionState) {
+ if (injectionState &&
+ (windowHandle == nullptr ||
+ windowHandle->getInfo()->ownerUid != injectionState->injectorUid) &&
+ !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
if (windowHandle != nullptr) {
ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
- "owned by uid %d",
- injectionState->injectorPid, injectionState->injectorUid,
- windowHandle->getName().c_str(),
- windowHandle->getInfo()->ownerUid);
+ "owned by uid %d",
+ injectionState->injectorPid, injectionState->injectorUid,
+ windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid);
} else {
ALOGW("Permission denied: injecting event from pid %d uid %d",
- injectionState->injectorPid, injectionState->injectorUid);
+ injectionState->injectorPid, injectionState->injectorUid);
}
return false;
}
return true;
}
-bool InputDispatcher::isWindowObscuredAtPointLocked(
- const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
+ int32_t x, int32_t y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
@@ -1836,16 +1793,14 @@
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (otherInfo->displayId == displayId
- && otherInfo->visible && !otherInfo->isTrustedOverlay()
- && otherInfo->frameContainsPoint(x, y)) {
+ if (otherInfo->displayId == displayId && otherInfo->visible &&
+ !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) {
return true;
}
}
return false;
}
-
bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
int32_t displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
@@ -1856,18 +1811,17 @@
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (otherInfo->displayId == displayId
- && otherInfo->visible && !otherInfo->isTrustedOverlay()
- && otherInfo->overlaps(windowInfo)) {
+ if (otherInfo->displayId == displayId && otherInfo->visible &&
+ !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) {
return true;
}
}
return false;
}
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
- const char* targetType) {
+std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
+ nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
+ const EventEntry* eventEntry, const char* targetType) {
// If the window is paused then keep waiting.
if (windowHandle->getInfo()->paused) {
return StringPrintf("Waiting because the %s window is paused.", targetType);
@@ -1878,15 +1832,16 @@
getConnectionLocked(getInputChannelLocked(windowHandle->getToken()));
if (connection == nullptr) {
return StringPrintf("Waiting because the %s window's input channel is not "
- "registered with the input dispatcher. The window may be in the process "
- "of being removed.", targetType);
+ "registered with the input dispatcher. The window may be in the "
+ "process of being removed.",
+ targetType);
}
// If the connection is dead then keep waiting.
if (connection->status != Connection::STATUS_NORMAL) {
return StringPrintf("Waiting because the %s window's input connection is %s."
- "The window may be in the process of being removed.", targetType,
- connection->getStatusLabel());
+ "The window may be in the process of being removed.",
+ targetType, connection->getStatusLabel());
}
// If the connection is backed up then keep waiting.
@@ -1986,25 +1941,25 @@
int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
switch (eventEntry->type) {
- case EventEntry::TYPE_MOTION: {
- const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
- return;
- }
+ case EventEntry::TYPE_MOTION: {
+ const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
+ if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+ return;
+ }
- if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
- eventType = USER_ACTIVITY_EVENT_TOUCH;
+ if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
+ eventType = USER_ACTIVITY_EVENT_TOUCH;
+ }
+ break;
}
- break;
- }
- case EventEntry::TYPE_KEY: {
- const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
- if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
- return;
+ case EventEntry::TYPE_KEY: {
+ const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
+ if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+ return;
+ }
+ eventType = USER_ACTIVITY_EVENT_BUTTON;
+ break;
}
- eventType = USER_ACTIVITY_EVENT_BUTTON;
- break;
- }
}
std::unique_ptr<CommandEntry> commandEntry =
@@ -2015,22 +1970,22 @@
}
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ const sp<Connection>& connection,
+ EventEntry* eventEntry,
+ const InputTarget* inputTarget) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf(
- "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
- connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ std::string message =
+ StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
ATRACE_NAME(message.c_str());
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
- "windowScaleFactor=(%f, %f), pointerIds=0x%x",
- connection->getInputChannelName().c_str(), inputTarget->flags,
- inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->globalScaleFactor,
- inputTarget->windowXScale, inputTarget->windowYScale,
- inputTarget->pointerIds.value);
+ "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+ "windowScaleFactor=(%f, %f), pointerIds=0x%x",
+ connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
+ inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
+ inputTarget->windowYScale, inputTarget->pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -2038,7 +1993,7 @@
if (connection->status != Connection::STATUS_NORMAL) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getInputChannelName().c_str(), connection->getStatusLabel());
+ connection->getInputChannelName().c_str(), connection->getStatusLabel());
#endif
return;
}
@@ -2049,18 +2004,17 @@
MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
- MotionEntry* splitMotionEntry = splitMotionEvent(
- originalMotionEntry, inputTarget->pointerIds);
+ MotionEntry* splitMotionEntry =
+ splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
if (!splitMotionEntry) {
return; // split event was dropped
}
-#if DEBUG_FOCUS
- ALOGD("channel '%s' ~ Split motion event.",
- connection->getInputChannelName().c_str());
- logOutboundMotionDetails(" ", splitMotionEntry);
-#endif
- enqueueDispatchEntriesLocked(currentTime, connection,
- splitMotionEntry, inputTarget);
+ if (DEBUG_FOCUS) {
+ ALOGD("channel '%s' ~ Split motion event.",
+ connection->getInputChannelName().c_str());
+ logOutboundMotionDetails(" ", splitMotionEntry);
+ }
+ enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
splitMotionEntry->release();
return;
}
@@ -2071,11 +2025,14 @@
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ const sp<Connection>& connection,
+ EventEntry* eventEntry,
+ const InputTarget* inputTarget) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf(
- "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
- connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ std::string message =
+ StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
+ ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
ATRACE_NAME(message.c_str());
}
@@ -2083,17 +2040,17 @@
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_IS);
+ InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.empty()) {
@@ -2101,14 +2058,14 @@
}
}
-void InputDispatcher::enqueueDispatchEntryLocked(
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
- int32_t dispatchMode) {
+void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry,
+ const InputTarget* inputTarget,
+ int32_t dispatchMode) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf(
- "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
- connection->getInputChannelName().c_str(),
- dispatchModeToString(dispatchMode).c_str());
+ std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
+ connection->getInputChannelName().c_str(),
+ dispatchModeToString(dispatchMode).c_str());
ATRACE_NAME(message.c_str());
}
int32_t inputTargetFlags = inputTarget->flags;
@@ -2119,78 +2076,81 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
- inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->globalScaleFactor, inputTarget->windowXScale,
- inputTarget->windowYScale);
+ DispatchEntry* dispatchEntry =
+ new DispatchEntry(eventEntry, // increments ref
+ inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->globalScaleFactor, inputTarget->windowXScale,
+ inputTarget->windowYScale);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- dispatchEntry->resolvedAction = keyEntry->action;
- dispatchEntry->resolvedFlags = keyEntry->flags;
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+ dispatchEntry->resolvedAction = keyEntry->action;
+ dispatchEntry->resolvedFlags = keyEntry->flags;
- if (!connection->inputState.trackKey(keyEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+ if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
+ dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
- connection->getInputChannelName().c_str());
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
+ connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
- return; // skip the inconsistent event
+ delete dispatchEntry;
+ return; // skip the inconsistent event
+ }
+ break;
}
- break;
- }
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
- if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
- } else {
- dispatchEntry->resolvedAction = motionEntry->action;
- }
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- && !connection->inputState.isHovering(
- motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+ } else {
+ dispatchEntry->resolvedAction = motionEntry->action;
+ }
+ 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());
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
+ "event",
+ connection->getInputChannelName().c_str());
#endif
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- }
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ }
- dispatchEntry->resolvedFlags = motionEntry->flags;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- }
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- }
+ dispatchEntry->resolvedFlags = motionEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+ dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+ dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
- if (!connection->inputState.trackMotion(motionEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+ if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
+ dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
- connection->getInputChannelName().c_str());
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+ "event",
+ connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
- return; // skip the inconsistent event
+ delete dispatchEntry;
+ return; // skip the inconsistent event
+ }
+
+ dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction,
+ inputTarget->inputChannel->getToken());
+
+ break;
}
-
- dispatchPointerDownOutsideFocus(motionEntry->source,
- dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
-
- break;
- }
}
// Remember that we are waiting for this dispatch to complete.
@@ -2201,11 +2161,10 @@
// Enqueue the dispatch entry.
connection->outboundQueue.push_back(dispatchEntry);
traceOutboundQueueLength(connection);
-
}
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
- const sp<IBinder>& newToken) {
+ const sp<IBinder>& newToken) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
@@ -2233,15 +2192,14 @@
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
+ const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
- connection->getInputChannelName().c_str());
+ connection->getInputChannelName().c_str());
ATRACE_NAME(message.c_str());
}
#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ startDispatchCycle",
- connection->getInputChannelName().c_str());
+ ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
#endif
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
@@ -2252,76 +2210,80 @@
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- // Publish the key event.
- status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
- keyEntry->deviceId, keyEntry->source, keyEntry->displayId,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
- keyEntry->keyCode, keyEntry->scanCode,
- keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-
- PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry->pointerCoords;
-
- // Set the X and Y offset depending on the input source.
- float xOffset, yOffset;
- if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
- && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float globalScaleFactor = dispatchEntry->globalScaleFactor;
- float wxs = dispatchEntry->windowXScale;
- float wys = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * wxs;
- yOffset = dispatchEntry->yOffset * wys;
- if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(globalScaleFactor, wxs, wys);
- }
- usingCoords = scaledCoords;
- }
- } else {
- xOffset = 0.0f;
- yOffset = 0.0f;
-
- // We don't want the dispatch target to know.
- if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i].clear();
- }
- usingCoords = scaledCoords;
- }
+ // Publish the key event.
+ status = connection->inputPublisher
+ .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
+ keyEntry->source, keyEntry->displayId,
+ dispatchEntry->resolvedAction,
+ dispatchEntry->resolvedFlags, keyEntry->keyCode,
+ keyEntry->scanCode, keyEntry->metaState,
+ keyEntry->repeatCount, keyEntry->downTime,
+ keyEntry->eventTime);
+ break;
}
- // Publish the motion event.
- status =
- connection->inputPublisher
- .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
- motionEntry->source, motionEntry->displayId,
- dispatchEntry->resolvedAction,
- motionEntry->actionButton,
- dispatchEntry->resolvedFlags,
- motionEntry->edgeFlags, motionEntry->metaState,
- motionEntry->buttonState,
- motionEntry->classification, xOffset, yOffset,
- motionEntry->xPrecision, motionEntry->yPrecision,
- motionEntry->xCursorPosition,
- motionEntry->yCursorPosition, motionEntry->downTime,
- motionEntry->eventTime, motionEntry->pointerCount,
- motionEntry->pointerProperties, usingCoords);
- break;
- }
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
- default:
- ALOG_ASSERT(false);
- return;
+ PointerCoords scaledCoords[MAX_POINTERS];
+ const PointerCoords* usingCoords = motionEntry->pointerCoords;
+
+ // Set the X and Y offset depending on the input source.
+ float xOffset, yOffset;
+ if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+ !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+ float globalScaleFactor = dispatchEntry->globalScaleFactor;
+ float wxs = dispatchEntry->windowXScale;
+ float wys = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * wxs;
+ yOffset = dispatchEntry->yOffset * wys;
+ if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+ for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i] = motionEntry->pointerCoords[i];
+ scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+ }
+ usingCoords = scaledCoords;
+ }
+ } else {
+ xOffset = 0.0f;
+ yOffset = 0.0f;
+
+ // We don't want the dispatch target to know.
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+ for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i].clear();
+ }
+ usingCoords = scaledCoords;
+ }
+ }
+
+ // Publish the motion event.
+ status = connection->inputPublisher
+ .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
+ motionEntry->source, motionEntry->displayId,
+ dispatchEntry->resolvedAction,
+ motionEntry->actionButton,
+ dispatchEntry->resolvedFlags,
+ motionEntry->edgeFlags, motionEntry->metaState,
+ motionEntry->buttonState,
+ motionEntry->classification, xOffset, yOffset,
+ motionEntry->xPrecision,
+ motionEntry->yPrecision,
+ motionEntry->xCursorPosition,
+ motionEntry->yCursorPosition,
+ motionEntry->downTime, motionEntry->eventTime,
+ motionEntry->pointerCount,
+ motionEntry->pointerProperties, usingCoords);
+ reportTouchEventForStatistics(*motionEntry);
+ break;
+ }
+
+ default:
+ ALOG_ASSERT(false);
+ return;
}
// Check the result.
@@ -2329,24 +2291,25 @@
if (status == WOULD_BLOCK) {
if (connection->waitQueue.empty()) {
ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
- "This is unexpected because the wait queue is empty, so the pipe "
- "should be empty and we shouldn't have any problems writing an "
- "event to it, status=%d", connection->getInputChannelName().c_str(),
- status);
+ "This is unexpected because the wait queue is empty, so the pipe "
+ "should be empty and we shouldn't have any problems writing an "
+ "event to it, status=%d",
+ connection->getInputChannelName().c_str(), status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
- "waiting for the application to catch up",
- connection->getInputChannelName().c_str());
+ "waiting for the application to catch up",
+ connection->getInputChannelName().c_str());
#endif
connection->inputPublisherBlocked = true;
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
- "status=%d", connection->getInputChannelName().c_str(), status);
+ "status=%d",
+ connection->getInputChannelName().c_str(), status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
@@ -2363,16 +2326,17 @@
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, uint32_t seq, bool handled) {
+ const sp<Connection>& connection, uint32_t seq,
+ bool handled) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
- connection->getInputChannelName().c_str(), seq, toString(handled));
+ connection->getInputChannelName().c_str(), seq, toString(handled));
#endif
connection->inputPublisherBlocked = false;
- if (connection->status == Connection::STATUS_BROKEN
- || connection->status == Connection::STATUS_ZOMBIE) {
+ if (connection->status == Connection::STATUS_BROKEN ||
+ connection->status == Connection::STATUS_ZOMBIE) {
return;
}
@@ -2381,10 +2345,11 @@
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, bool notify) {
+ const sp<Connection>& connection,
+ bool notify) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
- connection->getInputChannelName().c_str(), toString(notify));
+ connection->getInputChannelName().c_str(), toString(notify));
#endif
// Clear the dispatch queues.
@@ -2428,7 +2393,8 @@
if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
ALOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x", fd, events);
+ "fd=%d, events=0x%x",
+ fd, events);
return 0; // remove the callback
}
@@ -2437,7 +2403,8 @@
if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
if (!(events & ALOOPER_EVENT_INPUT)) {
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", connection->getInputChannelName().c_str(), events);
+ "events=0x%x",
+ connection->getInputChannelName().c_str(), events);
return 1;
}
@@ -2464,7 +2431,7 @@
notify = status != DEAD_OBJECT || !connection->monitor;
if (notify) {
ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
- connection->getInputChannelName().c_str(), status);
+ connection->getInputChannelName().c_str(), status);
}
} else {
// Monitor channels are never explicitly unregistered.
@@ -2473,24 +2440,25 @@
notify = !connection->monitor;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x", connection->getInputChannelName().c_str(), events);
+ "events=0x%x",
+ connection->getInputChannelName().c_str(), events);
}
}
// Unregister the channel.
d->unregisterInputChannelLocked(connection->inputChannel, notify);
return 0; // remove the callback
- } // release lock
+ } // release lock
}
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked (
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
for (const auto& pair : mConnectionsByFd) {
synthesizeCancelationEventsForConnectionLocked(pair.second, options);
}
}
-void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options) {
synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay);
synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay);
@@ -2526,32 +2494,31 @@
nsecs_t currentTime = now();
std::vector<EventEntry*> cancelationEvents;
- connection->inputState.synthesizeCancelationEvents(currentTime,
- cancelationEvents, options);
+ connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options);
if (!cancelationEvents.empty()) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
- "with reality: %s, mode=%d.",
- connection->getInputChannelName().c_str(), cancelationEvents.size(),
- options.reason, options.mode);
+ "with reality: %s, mode=%d.",
+ connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+ options.mode);
#endif
for (size_t i = 0; i < cancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
- case EventEntry::TYPE_KEY:
- logOutboundKeyDetails("cancel - ",
- static_cast<KeyEntry*>(cancelationEventEntry));
- break;
- case EventEntry::TYPE_MOTION:
- logOutboundMotionDetails("cancel - ",
- static_cast<MotionEntry*>(cancelationEventEntry));
- break;
+ case EventEntry::TYPE_KEY:
+ logOutboundKeyDetails("cancel - ",
+ static_cast<KeyEntry*>(cancelationEventEntry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ logOutboundMotionDetails("cancel - ",
+ static_cast<MotionEntry*>(cancelationEventEntry));
+ break;
}
InputTarget target;
- sp<InputWindowHandle> windowHandle = getWindowHandleLocked(
- connection->inputChannel->getToken());
+ sp<InputWindowHandle> windowHandle =
+ getWindowHandleLocked(connection->inputChannel->getToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.xOffset = -windowInfo->frameLeft;
@@ -2568,7 +2535,7 @@
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- &target, InputTarget::FLAG_DISPATCH_AS_IS);
+ &target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
}
@@ -2577,8 +2544,8 @@
}
}
-InputDispatcher::MotionEntry*
-InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry,
+ BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -2589,7 +2556,7 @@
uint32_t splitPointerCount = 0;
for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
- originalPointerIndex++) {
+ originalPointerIndex++) {
const PointerProperties& pointerProperties =
originalMotionEntry->pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
@@ -2609,16 +2576,16 @@
// or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
// in this way.
ALOGW("Dropping split motion event because the pointer count is %d but "
- "we expected there to be %d pointers. This probably means we received "
- "a broken sequence of pointer ids from the input device.",
- splitPointerCount, pointerIds.count());
+ "we expected there to be %d pointers. This probably means we received "
+ "a broken sequence of pointer ids from the input device.",
+ splitPointerCount, pointerIds.count());
return nullptr;
}
int32_t action = originalMotionEntry->action;
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
- if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
const PointerProperties& pointerProperties =
originalMotionEntry->pointerProperties[originalPointerIndex];
@@ -2627,15 +2594,16 @@
if (pointerIds.count() == 1) {
// The first/last pointer went down/up.
action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ ? AMOTION_EVENT_ACTION_DOWN
+ : AMOTION_EVENT_ACTION_UP;
} else {
// A secondary pointer went down/up.
uint32_t splitPointerIndex = 0;
while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) {
splitPointerIndex += 1;
}
- action = maskedAction | (splitPointerIndex
- << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ action = maskedAction |
+ (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
}
} else {
// An unrelated pointer changed.
@@ -2689,7 +2657,7 @@
* This will potentially overwrite keyCode and metaState.
*/
void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
- int32_t& keyCode, int32_t& metaState) {
+ int32_t& keyCode, int32_t& metaState) {
if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
int32_t newKeyCode = AKEYCODE_UNKNOWN;
if (keyCode == AKEYCODE_DEL) {
@@ -2721,12 +2689,12 @@
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyKey - eventTime=%" PRId64
- ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
- args->action, args->flags, args->keyCode, args->scanCode,
- args->metaState, args->downTime);
+ ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ "policyFlags=0x%x, action=0x%x, "
+ "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+ args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+ args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
+ args->downTime);
#endif
if (!validateKeyEvent(args->action)) {
return;
@@ -2752,15 +2720,14 @@
accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
KeyEvent event;
- event.initialize(args->deviceId, args->source, args->displayId, args->action,
- flags, keyCode, args->scanCode, metaState, repeatCount,
- args->downTime, args->eventTime);
+ event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
+ args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
android::base::Timer t;
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
- std::to_string(t.duration().count()).c_str());
+ std::to_string(t.duration().count()).c_str());
}
bool needWake;
@@ -2778,10 +2745,10 @@
mLock.lock();
}
- KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
- args->deviceId, args->source, args->displayId, policyFlags,
- args->action, flags, keyCode, args->scanCode,
- metaState, repeatCount, args->downTime);
+ KeyEntry* newEntry =
+ new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source,
+ args->displayId, policyFlags, args->action, flags, keyCode,
+ args->scanCode, metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
@@ -2805,28 +2772,27 @@
"yCursorPosition=%f, downTime=%" PRId64,
args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
- args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition,
+ args->edgeFlags, args->xPrecision, args->yPrecision, args->xCursorPosition,
args->yCursorPosition, args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, args->pointerProperties[i].id,
- args->pointerProperties[i].toolType,
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ "x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
#endif
- if (!validateMotionEvent(args->action, args->actionButton,
- args->pointerCount, args->pointerProperties)) {
+ if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties)) {
return;
}
@@ -2837,7 +2803,7 @@
mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
- std::to_string(t.duration().count()).c_str());
+ std::to_string(t.duration().count()).c_str());
}
bool needWake;
@@ -2889,20 +2855,19 @@
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
- "switchMask=0x%08x",
- args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+ "switchMask=0x%08x",
+ args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
#endif
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->notifySwitch(args->eventTime,
- args->switchValues, args->switchMask, policyFlags);
+ mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags);
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d",
- args->eventTime, args->deviceId);
+ ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+ args->deviceId);
#endif
bool needWake;
@@ -2919,13 +2884,13 @@
}
}
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) {
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
+ int32_t injectorUid, int32_t syncMode,
+ int32_t timeoutMillis, uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
+ "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -2937,87 +2902,74 @@
std::queue<EventEntry*> injectedEntries;
switch (event->getType()) {
- case AINPUT_EVENT_TYPE_KEY: {
- KeyEvent keyEvent;
- keyEvent.initialize(*static_cast<const KeyEvent*>(event));
- int32_t action = keyEvent.getAction();
- if (! validateKeyEvent(action)) {
- return INPUT_EVENT_INJECTION_FAILED;
- }
-
- int32_t flags = keyEvent.getFlags();
- int32_t keyCode = keyEvent.getKeyCode();
- int32_t metaState = keyEvent.getMetaState();
- accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
- /*byref*/ keyCode, /*byref*/ metaState);
- keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
- action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
- keyEvent.getDownTime(), keyEvent.getEventTime());
-
- if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
- policyFlags |= POLICY_FLAG_VIRTUAL;
- }
-
- if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
- if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
- ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
- std::to_string(t.duration().count()).c_str());
+ case AINPUT_EVENT_TYPE_KEY: {
+ KeyEvent keyEvent;
+ keyEvent.initialize(*static_cast<const KeyEvent*>(event));
+ int32_t action = keyEvent.getAction();
+ if (!validateKeyEvent(action)) {
+ return INPUT_EVENT_INJECTION_FAILED;
}
- }
- mLock.lock();
- KeyEntry* injectedEntry =
- new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
- keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
- policyFlags, action, flags, keyEvent.getKeyCode(),
- keyEvent.getScanCode(), keyEvent.getMetaState(),
- keyEvent.getRepeatCount(), keyEvent.getDownTime());
- injectedEntries.push(injectedEntry);
- break;
- }
+ int32_t flags = keyEvent.getFlags();
+ int32_t keyCode = keyEvent.getKeyCode();
+ int32_t metaState = keyEvent.getMetaState();
+ accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+ /*byref*/ keyCode, /*byref*/ metaState);
+ keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
+ keyEvent.getDisplayId(), action, flags, keyCode,
+ keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
+ keyEvent.getDownTime(), keyEvent.getEventTime());
- case AINPUT_EVENT_TYPE_MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
- int32_t action = motionEvent->getAction();
- size_t pointerCount = motionEvent->getPointerCount();
- const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
- int32_t actionButton = motionEvent->getActionButton();
- int32_t displayId = motionEvent->getDisplayId();
- if (! validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
- return INPUT_EVENT_INJECTION_FAILED;
- }
-
- if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- nsecs_t eventTime = motionEvent->getEventTime();
- android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
- if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
- ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
- std::to_string(t.duration().count()).c_str());
+ if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
+ policyFlags |= POLICY_FLAG_VIRTUAL;
}
+
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ android::base::Timer t;
+ mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
+ if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+ ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
+ std::to_string(t.duration().count()).c_str());
+ }
+ }
+
+ mLock.lock();
+ KeyEntry* injectedEntry =
+ new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
+ keyEvent.getDeviceId(), keyEvent.getSource(),
+ keyEvent.getDisplayId(), policyFlags, action, flags,
+ keyEvent.getKeyCode(), keyEvent.getScanCode(),
+ keyEvent.getMetaState(), keyEvent.getRepeatCount(),
+ keyEvent.getDownTime());
+ injectedEntries.push(injectedEntry);
+ break;
}
- mLock.lock();
- const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
- const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
- MotionEntry* injectedEntry =
- new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
- motionEvent->getDeviceId(), motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags, action, actionButton,
- motionEvent->getFlags(), motionEvent->getMetaState(),
- motionEvent->getButtonState(), motionEvent->getClassification(),
- motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
- motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(),
- uint32_t(pointerCount), pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(injectedEntry);
- for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
- sampleEventTimes += 1;
- samplePointerCoords += pointerCount;
- MotionEntry* nextInjectedEntry =
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+ int32_t action = motionEvent->getAction();
+ size_t pointerCount = motionEvent->getPointerCount();
+ const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
+ int32_t actionButton = motionEvent->getActionButton();
+ int32_t displayId = motionEvent->getDisplayId();
+ if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ nsecs_t eventTime = motionEvent->getEventTime();
+ android::base::Timer t;
+ mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+ if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+ ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
+ std::to_string(t.duration().count()).c_str());
+ }
+ }
+
+ mLock.lock();
+ const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ MotionEntry* injectedEntry =
new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
motionEvent->getDeviceId(), motionEvent->getSource(),
motionEvent->getDisplayId(), policyFlags, action, actionButton,
@@ -3030,14 +2982,32 @@
motionEvent->getDownTime(), uint32_t(pointerCount),
pointerProperties, samplePointerCoords,
motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(nextInjectedEntry);
+ injectedEntries.push(injectedEntry);
+ for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ sampleEventTimes += 1;
+ samplePointerCoords += pointerCount;
+ MotionEntry* nextInjectedEntry =
+ new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags, action,
+ actionButton, motionEvent->getFlags(),
+ motionEvent->getMetaState(), motionEvent->getButtonState(),
+ motionEvent->getClassification(),
+ motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
+ motionEvent->getYPrecision(),
+ motionEvent->getRawXCursorPosition(),
+ motionEvent->getRawYCursorPosition(),
+ motionEvent->getDownTime(), uint32_t(pointerCount),
+ pointerProperties, samplePointerCoords,
+ motionEvent->getXOffset(), motionEvent->getYOffset());
+ injectedEntries.push(nextInjectedEntry);
+ }
+ break;
}
- break;
- }
- default:
- ALOGW("Cannot inject event of type %d", event->getType());
- return INPUT_EVENT_INJECTION_FAILED;
+ default:
+ ALOGW("Cannot inject event of type %d", event->getType());
+ return INPUT_EVENT_INJECTION_FAILED;
}
InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
@@ -3077,7 +3047,7 @@
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
ALOGD("injectInputEvent - Timed out waiting for injection result "
- "to become available.");
+ "to become available.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
break;
@@ -3086,18 +3056,18 @@
mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
- && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED &&
+ syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectionState->pendingForegroundDispatches);
+ injectionState->pendingForegroundDispatches);
#endif
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Timed out waiting for pending foreground "
- "dispatches to finish.");
+ ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+ "dispatches to finish.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
break;
@@ -3113,16 +3083,16 @@
#if DEBUG_INJECTION
ALOGD("injectInputEvent - Finished with result %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectorPid, injectorUid);
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
#endif
return injectionResult;
}
bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
- return injectorUid == 0
- || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+ return injectorUid == 0 ||
+ mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
@@ -3130,26 +3100,25 @@
if (injectionState) {
#if DEBUG_INJECTION
ALOGD("Setting input event injection result to %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (injectionState->injectionIsAsync
- && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
+ if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
- case INPUT_EVENT_INJECTION_SUCCEEDED:
- ALOGV("Asynchronous input event injection succeeded.");
- break;
- case INPUT_EVENT_INJECTION_FAILED:
- ALOGW("Asynchronous input event injection failed.");
- break;
- case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
- ALOGW("Asynchronous input event injection permission denied.");
- break;
- case INPUT_EVENT_INJECTION_TIMED_OUT:
- ALOGW("Asynchronous input event injection timed out.");
- break;
+ case INPUT_EVENT_INJECTION_SUCCEEDED:
+ ALOGV("Asynchronous input event injection succeeded.");
+ break;
+ case INPUT_EVENT_INJECTION_FAILED:
+ ALOGW("Asynchronous input event injection failed.");
+ break;
+ case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ ALOGW("Asynchronous input event injection permission denied.");
+ break;
+ case INPUT_EVENT_INJECTION_TIMED_OUT:
+ ALOGW("Asynchronous input event injection timed out.");
+ break;
}
}
@@ -3201,9 +3170,9 @@
if (handle->getToken() == windowHandle->getToken()) {
if (windowHandle->getInfo()->displayId != it.first) {
ALOGE("Found window %s in display %" PRId32
- ", but it should belong to display %" PRId32,
- windowHandle->getName().c_str(), it.first,
- windowHandle->getInfo()->displayId);
+ ", but it should belong to display %" PRId32,
+ windowHandle->getName().c_str(), it.first,
+ windowHandle->getInfo()->displayId);
}
return true;
}
@@ -3285,10 +3254,15 @@
* For removed handle, check if need to send a cancel event if already in touch.
*/
void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
- int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-#if DEBUG_FOCUS
- ALOGD("setInputWindows displayId=%" PRId32, displayId);
-#endif
+ int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+ if (DEBUG_FOCUS) {
+ std::string windowList;
+ for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
+ windowList += iwh->getName() + " ";
+ }
+ ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
+ }
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3320,55 +3294,53 @@
if (oldFocusedWindowHandle != newFocusedWindowHandle) {
if (oldFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
- ALOGD("Focus left window: %s in display %" PRId32,
- oldFocusedWindowHandle->getName().c_str(), displayId);
-#endif
- sp<InputChannel> focusedInputChannel = getInputChannelLocked(
- oldFocusedWindowHandle->getToken());
+ if (DEBUG_FOCUS) {
+ ALOGD("Focus left window: %s in display %" PRId32,
+ oldFocusedWindowHandle->getName().c_str(), displayId);
+ }
+ sp<InputChannel> focusedInputChannel =
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForInputChannelLocked(
- focusedInputChannel, options);
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
}
mFocusedWindowHandlesByDisplay.erase(displayId);
}
if (newFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
- ALOGD("Focus entered window: %s in display %" PRId32,
- newFocusedWindowHandle->getName().c_str(), displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Focus entered window: %s in display %" PRId32,
+ newFocusedWindowHandle->getName().c_str(), displayId);
+ }
mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
}
if (mFocusedDisplayId == displayId) {
onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
}
-
}
ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
if (stateIndex >= 0) {
TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
- for (size_t i = 0; i < state.windows.size(); ) {
+ for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
-#if DEBUG_FOCUS
- ALOGD("Touched window was removed: %s in display %" PRId32,
- touchedWindow.windowHandle->getName().c_str(), displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Touched window was removed: %s in display %" PRId32,
+ touchedWindow.windowHandle->getName().c_str(), displayId);
+ }
sp<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "touched window was removed");
- synthesizeCancelationEventsForInputChannelLocked(
- touchedInputChannel, options);
+ "touched window was removed");
+ synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel,
+ options);
}
state.windows.erase(state.windows.begin() + i);
} else {
- ++i;
+ ++i;
}
}
}
@@ -3379,9 +3351,9 @@
// which might not happen until the next GC.
for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
if (!hasWindowHandleLocked(oldWindowHandle)) {
-#if DEBUG_FOCUS
- ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
+ }
oldWindowHandle->releaseChannel();
}
}
@@ -3397,9 +3369,10 @@
void InputDispatcher::setFocusedApplication(
int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
-#if DEBUG_FOCUS
- ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
+ inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
+ }
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3417,10 +3390,6 @@
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
-
-#if DEBUG_FOCUS
- //logDispatchStateLocked();
-#endif
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3437,9 +3406,9 @@
* display. The display-specified events won't be affected.
*/
void InputDispatcher::setFocusedDisplay(int32_t displayId) {
-#if DEBUG_FOCUS
- ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+ }
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3448,11 +3417,11 @@
getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
if (oldFocusedWindowHandle != nullptr) {
sp<InputChannel> inputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (inputChannel != nullptr) {
- CancelationOptions options(
- CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "The display which contains this window no longer has focus.");
+ CancelationOptions
+ options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "The display which contains this window no longer has focus.");
options.displayId = ADISPLAY_ID_NONE;
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
@@ -3471,16 +3440,16 @@
for (auto& it : mFocusedWindowHandlesByDisplay) {
const int32_t displayId = it.first;
const sp<InputWindowHandle>& windowHandle = it.second;
- ALOGE("Display #%" PRId32 " has focused window: '%s'\n",
- displayId, windowHandle->getName().c_str());
+ ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
+ windowHandle->getName().c_str());
}
}
}
}
-#if DEBUG_FOCUS
- logDispatchStateLocked();
-#endif
+ if (DEBUG_FOCUS) {
+ logDispatchStateLocked();
+ }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3488,9 +3457,9 @@
}
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
- ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+ }
bool changed;
{ // acquire lock
@@ -3512,9 +3481,9 @@
changed = false;
}
-#if DEBUG_FOCUS
- logDispatchStateLocked();
-#endif
+ if (DEBUG_FOCUS) {
+ logDispatchStateLocked();
+ }
} // release lock
if (changed) {
@@ -3524,9 +3493,9 @@
}
void InputDispatcher::setInputFilterEnabled(bool enabled) {
-#if DEBUG_FOCUS
- ALOGD("setInputFilterEnabled: enabled=%d", enabled);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("setInputFilterEnabled: enabled=%d", enabled);
+ }
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3545,9 +3514,9 @@
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
if (fromToken == toToken) {
-#if DEBUG_FOCUS
- ALOGD("Trivial transfer to same window.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Trivial transfer to same window.");
+ }
return true;
}
@@ -3560,14 +3529,14 @@
ALOGW("Cannot transfer focus because from or to window not found.");
return false;
}
-#if DEBUG_FOCUS
- ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
- fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
+ fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
+ }
if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-#if DEBUG_FOCUS
- ALOGD("Cannot transfer focus because windows are on different displays.");
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Cannot transfer focus because windows are on different displays.");
+ }
return false;
}
@@ -3582,9 +3551,9 @@
state.windows.erase(state.windows.begin() + i);
- int32_t newTargetFlags = oldTargetFlags
- & (InputTarget::FLAG_FOREGROUND
- | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+ int32_t newTargetFlags = oldTargetFlags &
+ (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+ InputTarget::FLAG_DISPATCH_AS_IS);
state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
found = true;
@@ -3592,12 +3561,12 @@
}
}
}
-Found:
+ Found:
- if (! found) {
-#if DEBUG_FOCUS
- ALOGD("Focus transfer failed because from window did not have focus.");
-#endif
+ if (!found) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Focus transfer failed because from window did not have focus.");
+ }
return false;
}
@@ -3607,14 +3576,15 @@
sp<Connection> toConnection = getConnectionLocked(toChannel);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "transferring touch focus from this window to another window");
+ CancelationOptions
+ options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
}
-#if DEBUG_FOCUS
- logDispatchStateLocked();
-#endif
+ if (DEBUG_FOCUS) {
+ logDispatchStateLocked();
+ }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3623,9 +3593,9 @@
}
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
-#if DEBUG_FOCUS
- ALOGD("Resetting and dropping all events (%s).", reason);
-#endif
+ if (DEBUG_FOCUS) {
+ ALOGD("Resetting and dropping all events (%s).", reason);
+ }
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -3663,12 +3633,12 @@
for (auto& it : mFocusedApplicationHandlesByDisplay) {
const int32_t displayId = it.first;
const sp<InputApplicationHandle>& applicationHandle = it.second;
- dump += StringPrintf(
- INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n",
- displayId,
- applicationHandle->getName().c_str(),
- applicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32
+ ", name='%s', dispatchingTimeout=%0.3fms\n",
+ displayId, applicationHandle->getName().c_str(),
+ applicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
+ 1000000.0);
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -3679,8 +3649,8 @@
for (auto& it : mFocusedWindowHandlesByDisplay) {
const int32_t displayId = it.first;
const sp<InputWindowHandle>& windowHandle = it.second;
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n",
- displayId, windowHandle->getName().c_str());
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ windowHandle->getName().c_str());
}
} else {
dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
@@ -3691,16 +3661,16 @@
for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
const TouchState& state = mTouchStatesByDisplay.valueAt(i);
dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
- state.displayId, toString(state.down), toString(state.split),
- state.deviceId, state.source);
+ state.displayId, toString(state.down), toString(state.split),
+ state.deviceId, state.source);
if (!state.windows.empty()) {
dump += INDENT3 "Windows:\n";
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
- dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
- i, touchedWindow.windowHandle->getName().c_str(),
- touchedWindow.pointerIds.value,
- touchedWindow.targetFlags);
+ dump += StringPrintf(INDENT4
+ "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+ i, touchedWindow.windowHandle->getName().c_str(),
+ touchedWindow.pointerIds.value, touchedWindow.targetFlags);
}
} else {
dump += INDENT3 "Windows: <none>\n";
@@ -3709,8 +3679,8 @@
dump += INDENT3 "Portal windows:\n";
for (size_t i = 0; i < state.portalWindows.size(); i++) {
const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i];
- dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
- i, portalWindowHandle->getName().c_str());
+ dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
+ portalWindowHandle->getName().c_str());
}
}
}
@@ -3719,7 +3689,7 @@
}
if (!mWindowHandlesByDisplay.empty()) {
- for (auto& it : mWindowHandlesByDisplay) {
+ for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
if (!windowHandles.empty()) {
@@ -3729,28 +3699,31 @@
const InputWindowInfo* windowInfo = windowHandle->getInfo();
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
- "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
- "touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->displayId,
- windowInfo->portalToDisplayId,
- toString(windowInfo->paused),
- toString(windowInfo->hasFocus),
- toString(windowInfo->hasWallpaper),
- toString(windowInfo->visible),
- toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
- windowInfo->layer,
- windowInfo->frameLeft, windowInfo->frameTop,
- windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->globalScaleFactor,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ "portalToDisplayId=%d, paused=%s, hasFocus=%s, "
+ "hasWallpaper=%s, "
+ "visible=%s, canReceiveKeys=%s, flags=0x%08x, "
+ "type=0x%08x, layer=%d, "
+ "frame=[%d,%d][%d,%d], globalScale=%f, "
+ "windowScale=(%f,%f), "
+ "touchableRegion=",
+ i, windowInfo->name.c_str(), windowInfo->displayId,
+ windowInfo->portalToDisplayId,
+ toString(windowInfo->paused),
+ toString(windowInfo->hasFocus),
+ toString(windowInfo->hasWallpaper),
+ toString(windowInfo->visible),
+ toString(windowInfo->canReceiveKeys),
+ windowInfo->layoutParamsFlags,
+ windowInfo->layoutParamsType, windowInfo->layer,
+ windowInfo->frameLeft, windowInfo->frameTop,
+ windowInfo->frameRight, windowInfo->frameBottom,
+ windowInfo->globalScaleFactor, windowInfo->windowXScale,
+ windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
- windowInfo->ownerPid, windowInfo->ownerUid,
- windowInfo->dispatchingTimeout / 1000000.0);
+ windowInfo->ownerPid, windowInfo->ownerUid,
+ windowInfo->dispatchingTimeout / 1000000.0);
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -3761,16 +3734,16 @@
}
if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) {
- for (auto& it : mGlobalMonitorsByDisplay) {
+ for (auto& it : mGlobalMonitorsByDisplay) {
const std::vector<Monitor>& monitors = it.second;
dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first);
dumpMonitors(dump, monitors);
- }
- for (auto& it : mGestureMonitorsByDisplay) {
+ }
+ for (auto& it : mGestureMonitorsByDisplay) {
const std::vector<Monitor>& monitors = it.second;
dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first);
dumpMonitors(dump, monitors);
- }
+ }
} else {
dump += INDENT "Monitors: <none>\n";
}
@@ -3783,8 +3756,7 @@
for (EventEntry* entry : mRecentQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n",
- (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
}
} else {
dump += INDENT "RecentQueue: <empty>\n";
@@ -3796,7 +3768,7 @@
dump += INDENT2;
mPendingEvent->appendDescription(dump);
dump += StringPrintf(", age=%0.1fms\n",
- (currentTime - mPendingEvent->eventTime) * 0.000001f);
+ (currentTime - mPendingEvent->eventTime) * 0.000001f);
} else {
dump += INDENT "PendingEvent: <none>\n";
}
@@ -3807,8 +3779,7 @@
for (EventEntry* entry : mInboundQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n",
- (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
}
} else {
dump += INDENT "InboundQueue: <empty>\n";
@@ -3819,8 +3790,8 @@
for (size_t i = 0; i < mReplacedKeys.size(); i++) {
const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
int32_t newKeyCode = mReplacedKeys.valueAt(i);
- dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n",
- i, replacement.keyCode, replacement.deviceId, newKeyCode);
+ dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+ replacement.keyCode, replacement.deviceId, newKeyCode);
}
} else {
dump += INDENT "ReplacedKeys: <empty>\n";
@@ -3844,8 +3815,8 @@
dump.append(INDENT4);
entry->eventEntry->appendDescription(dump);
dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
- entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+ entry->targetFlags, entry->resolvedAction,
+ (currentTime - entry->eventEntry->eventTime) * 0.000001f);
}
} else {
dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -3858,10 +3829,10 @@
dump += INDENT4;
entry->eventEntry->appendDescription(dump);
dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%0.1fms, wait=%0.1fms\n",
- entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f,
- (currentTime - entry->deliveryTime) * 0.000001f);
+ "age=%0.1fms, wait=%0.1fms\n",
+ entry->targetFlags, entry->resolvedAction,
+ (currentTime - entry->eventEntry->eventTime) * 0.000001f,
+ (currentTime - entry->deliveryTime) * 0.000001f);
}
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
@@ -3873,16 +3844,15 @@
if (isAppSwitchPendingLocked()) {
dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
- (mAppSwitchDueTime - now()) / 1000000.0);
+ (mAppSwitchDueTime - now()) / 1000000.0);
} else {
dump += INDENT "AppSwitch: not pending\n";
}
dump += INDENT "Configuration:\n";
- dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n",
- mConfig.keyRepeatDelay * 0.000001f);
+ dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
- mConfig.keyRepeatTimeout * 0.000001f);
+ mConfig.keyRepeatTimeout * 0.000001f);
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -3896,10 +3866,10 @@
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- int32_t displayId) {
+ int32_t displayId) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
- inputChannel->getName().c_str(), displayId);
+ inputChannel->getName().c_str(), displayId);
#endif
{ // acquire lock
@@ -3907,7 +3877,7 @@
sp<Connection> existingConnection = getConnectionLocked(inputChannel);
if (existingConnection != nullptr) {
ALOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().c_str());
+ inputChannel->getName().c_str());
return BAD_VALUE;
}
@@ -3926,7 +3896,7 @@
}
status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+ int32_t displayId, bool isGestureMonitor) {
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3946,13 +3916,11 @@
mConnectionsByFd[fd] = connection;
mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
- auto& monitorsByDisplay = isGestureMonitor
- ? mGestureMonitorsByDisplay
- : mGlobalMonitorsByDisplay;
+ auto& monitorsByDisplay =
+ isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
monitorsByDisplay[displayId].emplace_back(inputChannel);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
-
}
// Wake the looper because some connections have changed.
mLooper->wake();
@@ -3980,11 +3948,11 @@
}
status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
- bool notify) {
+ bool notify) {
sp<Connection> connection = getConnectionLocked(inputChannel);
if (connection == nullptr) {
ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().c_str());
+ inputChannel->getName().c_str());
return BAD_VALUE;
}
@@ -4010,16 +3978,17 @@
removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
}
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+void InputDispatcher::removeMonitorChannelLocked(
+ const sp<InputChannel>& inputChannel,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
- for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) {
+ for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
std::vector<Monitor>& monitors = it->second;
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
- if (monitors[i].inputChannel == inputChannel) {
- monitors.erase(monitors.begin() + i);
- break;
- }
+ if (monitors[i].inputChannel == inputChannel) {
+ monitors.erase(monitors.begin() + i);
+ break;
+ }
}
if (monitors.empty()) {
it = monitorsByDisplay.erase(it);
@@ -4055,14 +4024,14 @@
}
if (!foundDeviceId || !state.down) {
ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
- " Ignoring.");
+ " Ignoring.");
return BAD_VALUE;
}
int32_t deviceId = foundDeviceId.value();
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "gesture monitor stole pointer stream");
+ "gesture monitor stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
for (const TouchedWindow& window : state.windows) {
@@ -4075,7 +4044,6 @@
return OK;
}
-
std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
const sp<IBinder>& token) {
for (const auto& it : mGestureMonitorsByDisplay) {
@@ -4089,8 +4057,7 @@
return std::nullopt;
}
-sp<InputDispatcher::Connection> InputDispatcher::getConnectionLocked(
- const sp<InputChannel>& inputChannel) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<InputChannel>& inputChannel) {
if (inputChannel == nullptr) {
return nullptr;
}
@@ -4105,8 +4072,9 @@
return nullptr;
}
-void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
+void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, uint32_t seq,
+ bool handled) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
commandEntry->connection = connection;
@@ -4116,10 +4084,10 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection) {
+void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
- connection->getInputChannelName().c_str());
+ connection->getInputChannelName().c_str());
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
@@ -4128,7 +4096,7 @@
}
void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) {
+ const sp<InputWindowHandle>& newFocus) {
sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
@@ -4138,16 +4106,16 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onANRLocked(
- nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
+void InputDispatcher::onANRLocked(nsecs_t currentTime,
+ const sp<InputApplicationHandle>& applicationHandle,
+ const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
+ nsecs_t waitStartTime, const char* reason) {
float dispatchLatency = (currentTime - eventTime) * 0.000001f;
float waitDuration = (currentTime - waitStartTime) * 0.000001f;
ALOGI("Application is not responding: %s. "
- "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
- dispatchLatency, waitDuration, reason);
+ "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
+ waitDuration, reason);
// Capture a record of the InputDispatcher state at the time of the ANR.
time_t t = time(nullptr);
@@ -4158,8 +4126,9 @@
mLastANRState.clear();
mLastANRState += INDENT "ANR:\n";
mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
- mLastANRState += StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
+ mLastANRState +=
+ StringPrintf(INDENT2 "Window: %s\n",
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
@@ -4168,14 +4137,13 @@
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputChannel = windowHandle != nullptr ?
- getInputChannelLocked(windowHandle->getToken()) : nullptr;
+ commandEntry->inputChannel =
+ windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
commandEntry->reason = reason;
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible (
- CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
@@ -4183,8 +4151,7 @@
mLock.lock();
}
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
- CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
if (connection->status != Connection::STATUS_ZOMBIE) {
@@ -4196,8 +4163,7 @@
}
}
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
- CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
sp<IBinder> oldToken = commandEntry->oldToken;
sp<IBinder> newToken = commandEntry->newToken;
mLock.unlock();
@@ -4205,19 +4171,18 @@
mLock.lock();
}
-void InputDispatcher::doNotifyANRLockedInterruptible(
- CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- nsecs_t newTimeout = mPolicy->notifyANR(
- commandEntry->inputApplicationHandle,
- commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
- commandEntry->reason);
+ nsecs_t newTimeout =
+ mPolicy->notifyANR(commandEntry->inputApplicationHandle,
+ commandEntry->inputChannel ? commandEntry->inputChannel->getToken()
+ : nullptr,
+ commandEntry->reason);
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
- commandEntry->inputChannel);
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4230,13 +4195,13 @@
mLock.unlock();
android::base::Timer t;
- sp<IBinder> token = commandEntry->inputChannel != nullptr ?
- commandEntry->inputChannel->getToken() : nullptr;
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
- &event, entry->policyFlags);
+ sp<IBinder> token = commandEntry->inputChannel != nullptr
+ ? commandEntry->inputChannel->getToken()
+ : nullptr;
+ nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
- std::to_string(t.duration().count()).c_str());
+ std::to_string(t.duration().count()).c_str());
}
mLock.lock();
@@ -4258,16 +4223,14 @@
mLock.lock();
}
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
- CommandEntry* commandEntry) {
+void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
const nsecs_t finishTime = commandEntry->eventTime;
uint32_t seq = commandEntry->seq;
const bool handled = commandEntry->handled;
// Handle post-event policy actions.
- std::deque<InputDispatcher::DispatchEntry*>::iterator dispatchEntryIt =
- connection->findWaitQueueEntry(seq);
+ std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
if (dispatchEntryIt == connection->waitQueue.end()) {
return;
}
@@ -4317,7 +4280,8 @@
}
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) {
+ DispatchEntry* dispatchEntry,
+ KeyEntry* keyEntry, bool handled) {
if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
@@ -4342,9 +4306,9 @@
// Dispatch the unhandled key to the policy with the cancel flag.
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
#endif
KeyEvent event;
initializeKeyEvent(&event, keyEntry);
@@ -4352,8 +4316,8 @@
mLock.unlock();
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
- &event, keyEntry->policyFlags, &event);
+ mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+ keyEntry->policyFlags, &event);
mLock.lock();
@@ -4372,15 +4336,13 @@
// If the application did not handle a non-fallback key, first check
// that we are in a good state to perform unhandled key event processing
// Then ask the policy what to do with it.
- bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
- && keyEntry->repeatCount == 0;
+ bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Skipping unhandled key event processing "
- "since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
+ "since this is not an initial down. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
#endif
return false;
}
@@ -4388,17 +4350,16 @@
// Dispatch the unhandled key to the policy.
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to perform fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
#endif
KeyEvent event;
initializeKeyEvent(&event, keyEntry);
mLock.unlock();
- bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
- &event, keyEntry->policyFlags, &event);
+ bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+ keyEntry->policyFlags, &event);
mLock.lock();
@@ -4423,19 +4384,19 @@
// Cancel the fallback key if the policy decides not to send it anymore.
// We will continue to dispatch the key to the policy but we will no
// longer dispatch a fallback key to the application.
- if (fallbackKeyCode != AKEYCODE_UNKNOWN
- && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
+ (!fallback || fallbackKeyCode != event.getKeyCode())) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
if (fallback) {
ALOGD("Unhandled key event: Policy requested to send key %d"
- "as a fallback for %d, but on the DOWN it had requested "
- "to send %d instead. Fallback canceled.",
- event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+ "as a fallback for %d, but on the DOWN it had requested "
+ "to send %d instead. Fallback canceled.",
+ event.getKeyCode(), originalKeyCode, fallbackKeyCode);
} else {
ALOGD("Unhandled key event: Policy did not request fallback for %d, "
- "but on the DOWN it had requested to send %d. "
- "Fallback canceled.",
- originalKeyCode, fallbackKeyCode);
+ "but on the DOWN it had requested to send %d. "
+ "Fallback canceled.",
+ originalKeyCode, fallbackKeyCode);
}
#endif
@@ -4447,8 +4408,7 @@
fallback = false;
fallbackKeyCode = AKEYCODE_UNKNOWN;
if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
- connection->inputState.setFallbackKey(originalKeyCode,
- fallbackKeyCode);
+ connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
}
}
@@ -4458,11 +4418,10 @@
const KeyedVector<int32_t, int32_t>& fallbackKeys =
connection->inputState.getFallbackKeys();
for (size_t i = 0; i < fallbackKeys.size(); i++) {
- msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
- fallbackKeys.valueAt(i));
+ msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
}
ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
- fallbackKeys.size(), msg.c_str());
+ fallbackKeys.size(), msg.c_str());
}
#endif
@@ -4482,8 +4441,8 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+ "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+ originalKeyCode, fallbackKeyCode, keyEntry->metaState);
#endif
return true; // restart the event
} else {
@@ -4499,7 +4458,8 @@
}
bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) {
+ DispatchEntry* dispatchEntry,
+ MotionEntry* motionEntry, bool handled) {
return false;
}
@@ -4513,15 +4473,44 @@
void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
- entry->downTime, entry->eventTime);
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
}
void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
- int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+ int32_t injectionResult,
+ nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
}
+/**
+ * Report the touch event latency to the statsd server.
+ * Input events are reported for statistics if:
+ * - This is a touchscreen event
+ * - InputFilter is not enabled
+ * - Event is not injected or synthesized
+ *
+ * Statistics should be reported before calling addValue, to prevent a fresh new sample
+ * from getting aggregated with the "old" data.
+ */
+void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry)
+ REQUIRES(mLock) {
+ const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) &&
+ !(motionEntry.isSynthesized()) && !mInputFilterEnabled;
+ if (!reportForStatistics) {
+ return;
+ }
+
+ if (mTouchStatistics.shouldReport()) {
+ android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(),
+ mTouchStatistics.getMax(), mTouchStatistics.getMean(),
+ mTouchStatistics.getStDev(), mTouchStatistics.getCount());
+ mTouchStatistics.reset();
+ }
+ const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime);
+ mTouchStatistics.addValue(latencyMicros);
+}
+
void InputDispatcher::traceInboundQueueLengthLocked() {
if (ATRACE_ENABLED()) {
ATRACE_INT("iq", mInboundQueue.size());
@@ -4563,800 +4552,4 @@
mDispatcherIsAlive.wait(_l);
}
-
-// --- InputDispatcher::InjectionState ---
-
-InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) :
- refCount(1),
- injectorPid(injectorPid), injectorUid(injectorUid),
- injectionResult(INPUT_EVENT_INJECTION_PENDING), injectionIsAsync(false),
- pendingForegroundDispatches(0) {
-}
-
-InputDispatcher::InjectionState::~InjectionState() {
-}
-
-void InputDispatcher::InjectionState::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
-
-// --- InputDispatcher::EventEntry ---
-
-InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type,
- nsecs_t eventTime, uint32_t policyFlags) :
- sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime),
- policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) {
-}
-
-InputDispatcher::EventEntry::~EventEntry() {
- releaseInjectionState();
-}
-
-void InputDispatcher::EventEntry::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
-void InputDispatcher::EventEntry::releaseInjectionState() {
- if (injectionState) {
- injectionState->release();
- injectionState = nullptr;
- }
-}
-
-
-// --- InputDispatcher::ConfigurationChangedEntry ---
-
-InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(
- uint32_t sequenceNum, nsecs_t eventTime) :
- EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
-}
-
-InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {
-}
-
-void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
-}
-
-
-// --- InputDispatcher::DeviceResetEntry ---
-
-InputDispatcher::DeviceResetEntry::DeviceResetEntry(
- uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
- EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0),
- deviceId(deviceId) {
-}
-
-InputDispatcher::DeviceResetEntry::~DeviceResetEntry() {
-}
-
-void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x",
- deviceId, policyFlags);
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime) :
- EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
- deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags),
- keyCode(keyCode), scanCode(scanCode), metaState(metaState),
- repeatCount(repeatCount), downTime(downTime),
- syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
- interceptKeyWakeupTime(0) {
-}
-
-InputDispatcher::KeyEntry::~KeyEntry() {
-}
-
-void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
- "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
- "repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode,
- scanCode, metaState, repeatCount, policyFlags);
-}
-
-void InputDispatcher::KeyEntry::recycle() {
- releaseInjectionState();
-
- dispatchInProgress = false;
- syntheticRepeat = false;
- interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
- interceptKeyWakeupTime = 0;
-}
-
-
-// --- InputDispatcher::MotionEntry ---
-
-InputDispatcher::MotionEntry::MotionEntry(
- uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xOffset, float yOffset)
- : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
- eventTime(eventTime),
- deviceId(deviceId),
- source(source),
- displayId(displayId),
- action(action),
- actionButton(actionButton),
- flags(flags),
- metaState(metaState),
- buttonState(buttonState),
- classification(classification),
- edgeFlags(edgeFlags),
- xPrecision(xPrecision),
- yPrecision(yPrecision),
- xCursorPosition(xCursorPosition),
- yCursorPosition(yCursorPosition),
- downTime(downTime),
- pointerCount(pointerCount) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
- if (xOffset || yOffset) {
- this->pointerCoords[i].applyOffset(xOffset, yOffset);
- }
- }
-}
-
-InputDispatcher::MotionEntry::~MotionEntry() {
-}
-
-void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
- ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
- "buttonState=0x%08x, "
- "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
- "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, motionActionToString(action).c_str(),
- actionButton, flags, metaState, buttonState,
- motionClassificationToString(classification), edgeFlags, xPrecision,
- yPrecision, xCursorPosition, yCursorPosition);
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- if (i) {
- msg += ", ";
- }
- msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id,
- pointerCoords[i].getX(), pointerCoords[i].getY());
- }
- msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
-}
-
-
-// --- InputDispatcher::DispatchEntry ---
-
-volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
-
-InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
- float windowXScale, float windowYScale) :
- seq(nextSeq()),
- eventEntry(eventEntry), targetFlags(targetFlags),
- xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
- windowXScale(windowXScale), windowYScale(windowYScale),
- deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
- eventEntry->refCount += 1;
-}
-
-InputDispatcher::DispatchEntry::~DispatchEntry() {
- eventEntry->release();
-}
-
-uint32_t InputDispatcher::DispatchEntry::nextSeq() {
- // Sequence number 0 is reserved and will never be returned.
- uint32_t seq;
- do {
- seq = android_atomic_inc(&sNextSeqAtomic);
- } while (!seq);
- return seq;
-}
-
-
-// --- InputDispatcher::InputState ---
-
-InputDispatcher::InputState::InputState() {
-}
-
-InputDispatcher::InputState::~InputState() {
-}
-
-bool InputDispatcher::InputState::isNeutral() const {
- return mKeyMementos.empty() && mMotionMementos.empty();
-}
-
-bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source,
- int32_t displayId) const {
- for (const MotionMemento& memento : mMotionMementos) {
- if (memento.deviceId == deviceId
- && memento.source == source
- && memento.displayId == displayId
- && memento.hovering) {
- return true;
- }
- }
- return false;
-}
-
-bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
- int32_t action, int32_t flags) {
- switch (action) {
- case AKEY_EVENT_ACTION_UP: {
- if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
- for (size_t i = 0; i < mFallbackKeys.size(); ) {
- if (mFallbackKeys.valueAt(i) == entry->keyCode) {
- mFallbackKeys.removeItemsAt(i);
- } else {
- i += 1;
- }
- }
- }
- ssize_t index = findKeyMemento(entry);
- if (index >= 0) {
- mKeyMementos.erase(mKeyMementos.begin() + index);
- return true;
- }
- /* FIXME: We can't just drop the key up event because that prevents creating
- * popup windows that are automatically shown when a key is held and then
- * dismissed when the key is released. The problem is that the popup will
- * not have received the original key down, so the key up will be considered
- * to be inconsistent with its observed state. We could perhaps handle this
- * by synthesizing a key down but that will cause other problems.
- *
- * So for now, allow inconsistent key up events to be dispatched.
- *
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
- "keyCode=%d, scanCode=%d",
- entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
-#endif
- return false;
- */
- return true;
- }
-
- case AKEY_EVENT_ACTION_DOWN: {
- ssize_t index = findKeyMemento(entry);
- if (index >= 0) {
- mKeyMementos.erase(mKeyMementos.begin() + index);
- }
- addKeyMemento(entry, flags);
- return true;
- }
-
- default:
- return true;
- }
-}
-
-bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry,
- int32_t action, int32_t flags) {
- int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
- switch (actionMasked) {
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL: {
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
- if (index >= 0) {
- mMotionMementos.erase(mMotionMementos.begin() + index);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
- "displayId=%" PRId32 ", actionMasked=%d",
- entry->deviceId, entry->source, entry->displayId, actionMasked);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_DOWN: {
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
- if (index >= 0) {
- mMotionMementos.erase(mMotionMementos.begin() + index);
- }
- addMotionMemento(entry, flags, false /*hovering*/);
- return true;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_MOVE: {
- if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
- // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to
- // generate cancellation events for these since they're based in relative rather than
- // absolute units.
- return true;
- }
-
- ssize_t index = findMotionMemento(entry, false /*hovering*/);
-
- if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
- // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
- // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any
- // other value and we need to track the motion so we can send cancellation events for
- // anything generating fallback events (e.g. DPad keys for joystick movements).
- if (index >= 0) {
- if (entry->pointerCoords[0].isEmpty()) {
- mMotionMementos.erase(mMotionMementos.begin() + index);
- } else {
- MotionMemento& memento = mMotionMementos[index];
- memento.setPointers(entry);
- }
- } else if (!entry->pointerCoords[0].isEmpty()) {
- addMotionMemento(entry, flags, false /*hovering*/);
- }
-
- // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
- return true;
- }
- if (index >= 0) {
- MotionMemento& memento = mMotionMementos[index];
- memento.setPointers(entry);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion pointer up/down or move event: "
- "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
- entry->deviceId, entry->source, entry->displayId, actionMasked);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_HOVER_EXIT: {
- ssize_t index = findMotionMemento(entry, true /*hovering*/);
- if (index >= 0) {
- mMotionMementos.erase(mMotionMementos.begin() + index);
- return true;
- }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
- "displayId=%" PRId32,
- entry->deviceId, entry->source, entry->displayId);
-#endif
- return false;
- }
-
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE: {
- ssize_t index = findMotionMemento(entry, true /*hovering*/);
- if (index >= 0) {
- mMotionMementos.erase(mMotionMementos.begin() + index);
- }
- addMotionMemento(entry, flags, true /*hovering*/);
- return true;
- }
-
- default:
- return true;
- }
-}
-
-ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
- for (size_t i = 0; i < mKeyMementos.size(); i++) {
- const KeyMemento& memento = mKeyMementos[i];
- if (memento.deviceId == entry->deviceId
- && memento.source == entry->source
- && memento.displayId == entry->displayId
- && memento.keyCode == entry->keyCode
- && memento.scanCode == entry->scanCode) {
- return i;
- }
- }
- return -1;
-}
-
-ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
- bool hovering) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos[i];
- if (memento.deviceId == entry->deviceId
- && memento.source == entry->source
- && memento.displayId == entry->displayId
- && memento.hovering == hovering) {
- return i;
- }
- }
- return -1;
-}
-
-void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
- KeyMemento memento;
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.displayId = entry->displayId;
- memento.keyCode = entry->keyCode;
- memento.scanCode = entry->scanCode;
- memento.metaState = entry->metaState;
- memento.flags = flags;
- memento.downTime = entry->downTime;
- memento.policyFlags = entry->policyFlags;
- mKeyMementos.push_back(memento);
-}
-
-void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
- int32_t flags, bool hovering) {
- MotionMemento memento;
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.displayId = entry->displayId;
- memento.flags = flags;
- memento.xPrecision = entry->xPrecision;
- memento.yPrecision = entry->yPrecision;
- memento.xCursorPosition = entry->xCursorPosition;
- memento.yCursorPosition = entry->yCursorPosition;
- memento.downTime = entry->downTime;
- memento.setPointers(entry);
- memento.hovering = hovering;
- memento.policyFlags = entry->policyFlags;
- mMotionMementos.push_back(memento);
-}
-
-void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
- pointerCount = entry->pointerCount;
- for (uint32_t i = 0; i < entry->pointerCount; i++) {
- pointerProperties[i].copyFrom(entry->pointerProperties[i]);
- pointerCoords[i].copyFrom(entry->pointerCoords[i]);
- }
-}
-
-void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
- std::vector<EventEntry*>& outEvents, const CancelationOptions& options) {
- for (KeyMemento& memento : mKeyMementos) {
- if (shouldCancelKey(memento, options)) {
- outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
- memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
- AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
- memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
- }
- }
-
- for (const MotionMemento& memento : mMotionMementos) {
- if (shouldCancelMotion(memento, options)) {
- const int32_t action = memento.hovering ?
- AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
- outEvents.push_back(
- new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags, action,
- 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime, memento.pointerCount,
- memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
- 0 /*yOffset*/));
- }
- }
-}
-
-void InputDispatcher::InputState::clear() {
- mKeyMementos.clear();
- mMotionMementos.clear();
- mFallbackKeys.clear();
-}
-
-void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos[i];
- if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
- for (size_t j = 0; j < other.mMotionMementos.size(); ) {
- const MotionMemento& otherMemento = other.mMotionMementos[j];
- if (memento.deviceId == otherMemento.deviceId
- && memento.source == otherMemento.source
- && memento.displayId == otherMemento.displayId) {
- other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
- } else {
- j += 1;
- }
- }
- other.mMotionMementos.push_back(memento);
- }
- }
-}
-
-int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) {
- ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
- return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
-}
-
-void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode,
- int32_t fallbackKeyCode) {
- ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
- if (index >= 0) {
- mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
- } else {
- mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
- }
-}
-
-void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) {
- mFallbackKeys.removeItem(originalKeyCode);
-}
-
-bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
- const CancelationOptions& options) {
- if (options.keyCode && memento.keyCode != options.keyCode.value()) {
- return false;
- }
-
- if (options.deviceId && memento.deviceId != options.deviceId.value()) {
- return false;
- }
-
- if (options.displayId && memento.displayId != options.displayId.value()) {
- return false;
- }
-
- switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
- return true;
- case CancelationOptions::CANCEL_FALLBACK_EVENTS:
- return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
- default:
- return false;
- }
-}
-
-bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
- const CancelationOptions& options) {
- if (options.deviceId && memento.deviceId != options.deviceId.value()) {
- return false;
- }
-
- if (options.displayId && memento.displayId != options.displayId.value()) {
- return false;
- }
-
- switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- return true;
- case CancelationOptions::CANCEL_POINTER_EVENTS:
- return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
- return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
- default:
- return false;
- }
-}
-
-
-// --- InputDispatcher::Connection ---
-
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) :
- status(STATUS_NORMAL), inputChannel(inputChannel),
- monitor(monitor),
- inputPublisher(inputChannel), inputPublisherBlocked(false) {
-}
-
-InputDispatcher::Connection::~Connection() {
-}
-
-const std::string InputDispatcher::Connection::getWindowName() const {
- if (inputChannel != nullptr) {
- return inputChannel->getName();
- }
- if (monitor) {
- return "monitor";
- }
- return "?";
-}
-
-const char* InputDispatcher::Connection::getStatusLabel() const {
- switch (status) {
- case STATUS_NORMAL:
- return "NORMAL";
-
- case STATUS_BROKEN:
- return "BROKEN";
-
- case STATUS_ZOMBIE:
- return "ZOMBIE";
-
- default:
- return "UNKNOWN";
- }
-}
-
-std::deque<InputDispatcher::DispatchEntry*>::iterator
-InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
- for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
- if ((*it)->seq == seq) {
- return it;
- }
- }
- return waitQueue.end();
-}
-
-// --- InputDispatcher::Monitor
-InputDispatcher::Monitor::Monitor(const sp<InputChannel>& inputChannel) :
- inputChannel(inputChannel) {
-}
-
-
-// --- InputDispatcher::CommandEntry ---
-//
-InputDispatcher::CommandEntry::CommandEntry(Command command) :
- command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0),
- seq(0), handled(false) {
-}
-
-InputDispatcher::CommandEntry::~CommandEntry() {
-}
-
-// --- InputDispatcher::TouchedMonitor ---
-InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset,
- float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {
-}
-
-// --- InputDispatcher::TouchState ---
-
-InputDispatcher::TouchState::TouchState() :
- down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {
-}
-
-InputDispatcher::TouchState::~TouchState() {
-}
-
-void InputDispatcher::TouchState::reset() {
- down = false;
- split = false;
- deviceId = -1;
- source = 0;
- displayId = ADISPLAY_ID_NONE;
- windows.clear();
- portalWindows.clear();
- gestureMonitors.clear();
-}
-
-void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
- down = other.down;
- split = other.split;
- deviceId = other.deviceId;
- source = other.source;
- displayId = other.displayId;
- windows = other.windows;
- portalWindows = other.portalWindows;
- gestureMonitors = other.gestureMonitors;
-}
-
-void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds) {
- if (targetFlags & InputTarget::FLAG_SPLIT) {
- split = true;
- }
-
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows[i];
- if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.targetFlags |= targetFlags;
- if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
- }
- touchedWindow.pointerIds.value |= pointerIds.value;
- return;
- }
- }
-
- TouchedWindow touchedWindow;
- touchedWindow.windowHandle = windowHandle;
- touchedWindow.targetFlags = targetFlags;
- touchedWindow.pointerIds = pointerIds;
- windows.push_back(touchedWindow);
-}
-
-void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
- size_t numWindows = portalWindows.size();
- for (size_t i = 0; i < numWindows; i++) {
- if (portalWindows[i] == windowHandle) {
- return;
- }
- }
- portalWindows.push_back(windowHandle);
-}
-
-void InputDispatcher::TouchState::addGestureMonitors(
- const std::vector<TouchedMonitor>& newMonitors) {
- const size_t newSize = gestureMonitors.size() + newMonitors.size();
- gestureMonitors.reserve(newSize);
- gestureMonitors.insert(std::end(gestureMonitors),
- std::begin(newMonitors), std::end(newMonitors));
-}
-
-void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
- for (size_t i = 0; i < windows.size(); i++) {
- if (windows[i].windowHandle == windowHandle) {
- windows.erase(windows.begin() + i);
- return;
- }
- }
-}
-
-void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
- for (size_t i = 0; i < windows.size(); i++) {
- if (windows[i].windowHandle->getToken() == token) {
- windows.erase(windows.begin() + i);
- return;
- }
- }
-}
-
-void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
- for (size_t i = 0 ; i < windows.size(); ) {
- TouchedWindow& window = windows[i];
- if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
- | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
- window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
- window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
- i += 1;
- } else {
- windows.erase(windows.begin() + i);
- }
- }
-}
-
-void InputDispatcher::TouchState::filterNonMonitors() {
- windows.clear();
- portalWindows.clear();
-}
-
-sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const {
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows[i];
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
- return window.windowHandle;
- }
- }
- return nullptr;
-}
-
-bool InputDispatcher::TouchState::isSlippery() const {
- // Must have exactly one foreground window.
- bool haveSlipperyForegroundWindow = false;
- for (const TouchedWindow& window : windows) {
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
- if (haveSlipperyForegroundWindow
- || !(window.windowHandle->getInfo()->layoutParamsFlags
- & InputWindowInfo::FLAG_SLIPPERY)) {
- return false;
- }
- haveSlipperyForegroundWindow = true;
- }
- }
- return haveSlipperyForegroundWindow;
-}
-
-
-// --- InputDispatcherThread ---
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
- Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
-}
-
-InputDispatcherThread::~InputDispatcherThread() {
-}
-
-bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
-}
-
-} // namespace android
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
new file mode 100644
index 0000000..0d9d6b0
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2010 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 _UI_INPUT_DISPATCHER_H
+#define _UI_INPUT_DISPATCHER_H
+
+#include "CancelationOptions.h"
+#include "Entry.h"
+#include "InjectionState.h"
+#include "InputDispatcherConfiguration.h"
+#include "InputDispatcherInterface.h"
+#include "InputDispatcherPolicyInterface.h"
+#include "InputState.h"
+#include "InputTarget.h"
+#include "Monitor.h"
+#include "TouchState.h"
+#include "TouchedWindow.h"
+
+#include <input/Input.h>
+#include <input/InputApplication.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+#include <input/LatencyStatistics.h>
+#include <limits.h>
+#include <stddef.h>
+#include <ui/Region.h>
+#include <unistd.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+#include <condition_variable>
+#include <deque>
+#include <optional>
+#include <unordered_map>
+
+#include <InputListener.h>
+#include <InputReporterInterface.h>
+
+namespace android::inputdispatcher {
+
+class Connection;
+
+/* Dispatches events to input targets. Some functions of the input dispatcher, such as
+ * identifying input targets, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ * Because the policy can potentially block or cause re-entrance into the input dispatcher,
+ * the input dispatcher never calls into the policy while holding its internal locks.
+ * The implementation is also carefully designed to recover from scenarios such as an
+ * input channel becoming unregistered while identifying input targets or processing timeouts.
+ *
+ * Methods marked 'Locked' must be called with the lock acquired.
+ *
+ * Methods marked 'LockedInterruptible' must be called with the lock acquired but
+ * may during the course of their execution release the lock, call into the policy, and
+ * then reacquire the lock. The caller is responsible for recovering gracefully.
+ *
+ * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
+ */
+class InputDispatcher : public android::InputDispatcherInterface {
+protected:
+ virtual ~InputDispatcher();
+
+public:
+ explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+
+ virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
+
+ virtual void dispatchOnce() override;
+
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+ virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
+ int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) override;
+
+ virtual void setInputWindows(
+ const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
+ virtual void setFocusedApplication(
+ int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
+ virtual void setFocusedDisplay(int32_t displayId) override;
+ virtual void setInputDispatchMode(bool enabled, bool frozen) override;
+ virtual void setInputFilterEnabled(bool enabled) override;
+
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
+ const sp<IBinder>& toToken) override;
+
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
+ int32_t displayId) override;
+ virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
+ bool isGestureMonitor) override;
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual status_t pilferPointers(const sp<IBinder>& token) override;
+
+private:
+
+ enum DropReason {
+ DROP_REASON_NOT_DROPPED = 0,
+ DROP_REASON_POLICY = 1,
+ DROP_REASON_APP_SWITCH = 2,
+ DROP_REASON_DISABLED = 3,
+ DROP_REASON_BLOCKED = 4,
+ DROP_REASON_STALE = 5,
+ };
+
+ sp<InputDispatcherPolicyInterface> mPolicy;
+ android::InputDispatcherConfiguration mConfig;
+
+ std::mutex mLock;
+
+ std::condition_variable mDispatcherIsAlive;
+
+ sp<Looper> mLooper;
+
+ EventEntry* mPendingEvent GUARDED_BY(mLock);
+ std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
+ std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+ std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+ DropReason mLastDropReason GUARDED_BY(mLock);
+
+ void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
+
+ // Enqueues an inbound event. Returns true if mLooper->wake() should be called.
+ bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+ // Cleans up input state when dropping an inbound event.
+ void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
+
+ // Adds an event to a queue of recent events for debugging purposes.
+ void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+ // App switch latency optimization.
+ bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
+ nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
+
+ bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+ bool isAppSwitchPendingLocked() REQUIRES(mLock);
+ void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
+
+ // Stale event latency optimization.
+ static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
+
+ // Blocked event latency optimization. Drops old events when the user intends
+ // to transfer focus to a new application.
+ EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
+
+ sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ bool addOutsideTargets = false,
+ bool addPortalWindows = false) REQUIRES(mLock);
+
+ // All registered connections mapped by channel file descriptor.
+ std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& b) const {
+ return std::hash<IBinder*>{}(b.get());
+ }
+ };
+ std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
+ GUARDED_BY(mLock);
+
+ // Finds the display ID of the gesture monitor identified by the provided token.
+ std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
+ REQUIRES(mLock);
+
+ sp<Connection> getConnectionLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+
+ // Input channels that will receive a copy of all input events sent to the provided display.
+ std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
+
+ // Input channels that will receive pointer events that start within the corresponding display.
+ // These are a bit special when compared to global monitors since they'll cause gesture streams
+ // to continue even when there isn't a touched window,and have the ability to steal the rest of
+ // the pointer stream in order to claim it for a system gesture.
+ std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock);
+
+ // Event injection and synchronization.
+ std::condition_variable mInjectionResultAvailable;
+ bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
+ void setInjectionResult(EventEntry* entry, int32_t injectionResult);
+
+ std::condition_variable mInjectionSyncFinished;
+ void incrementPendingForegroundDispatches(EventEntry* entry);
+ void decrementPendingForegroundDispatches(EventEntry* entry);
+
+ // Key repeat tracking.
+ struct KeyRepeatState {
+ KeyEntry* lastKeyEntry; // or null if no repeat
+ nsecs_t nextRepeatTime;
+ } mKeyRepeatState GUARDED_BY(mLock);
+
+ void resetKeyRepeatLocked() REQUIRES(mLock);
+ KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
+
+ // Key replacement tracking
+ struct KeyReplacement {
+ int32_t keyCode;
+ int32_t deviceId;
+ bool operator==(const KeyReplacement& rhs) const {
+ return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
+ }
+ bool operator<(const KeyReplacement& rhs) const {
+ return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+ }
+ };
+ // Maps the key code replaced, device id tuple to the key code it was replaced with
+ KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+ // Process certain Meta + Key combinations
+ void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
+ int32_t& metaState);
+
+ // Deferred command processing.
+ bool haveCommandsLocked() const REQUIRES(mLock);
+ bool runCommandsLockedInterruptible() REQUIRES(mLock);
+ void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+
+ // Input filter processing.
+ bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
+ bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+
+ // Inbound event processing.
+ void drainInboundQueueLocked() REQUIRES(mLock);
+ void releasePendingEventLocked() REQUIRES(mLock);
+ void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+ // Dispatch state.
+ bool mDispatchEnabled GUARDED_BY(mLock);
+ bool mDispatchFrozen GUARDED_BY(mLock);
+ bool mInputFilterEnabled GUARDED_BY(mLock);
+
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
+ GUARDED_BY(mLock);
+ // Get window handles by display, return an empty vector if not found.
+ std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+ REQUIRES(mLock);
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
+ REQUIRES(mLock);
+ sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+ bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+
+ /*
+ * Validate and update InputWindowHandles for a given display.
+ */
+ void updateWindowHandlesForDisplayLocked(
+ const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
+ REQUIRES(mLock);
+
+ // Focus tracking for keys, trackball, etc.
+ std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
+ GUARDED_BY(mLock);
+
+ KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+ TouchState mTempTouchState GUARDED_BY(mLock);
+
+ // Focused applications.
+ std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
+ GUARDED_BY(mLock);
+
+ // Top focused display.
+ int32_t mFocusedDisplayId GUARDED_BY(mLock);
+
+ // Dispatcher state at time of last ANR.
+ std::string mLastANRState GUARDED_BY(mLock);
+
+ // Dispatch inbound events.
+ bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
+ REQUIRES(mLock);
+ bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
+ bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason,
+ nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
+ nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
+ const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+
+ void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
+ void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
+
+ // Keeping track of ANR timeouts.
+ enum InputTargetWaitCause {
+ INPUT_TARGET_WAIT_CAUSE_NONE,
+ INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
+ INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
+ };
+
+ InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
+ nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
+ nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
+ bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
+ sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+
+ // Contains the last window which received a hover event.
+ sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
+
+ // Finding targets for input events.
+ int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+ const sp<InputApplicationHandle>& applicationHandle,
+ const sp<InputWindowHandle>& windowHandle,
+ nsecs_t* nextWakeupTime, const char* reason)
+ REQUIRES(mLock);
+
+ void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
+ void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel)
+ REQUIRES(mLock);
+ nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
+ void resetANRTimeoutsLocked() REQUIRES(mLock);
+
+ int32_t getTargetDisplayId(const EventEntry* entry);
+ int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+ std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+ std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime,
+ bool* outConflictingPointerActions) REQUIRES(mLock);
+ std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+ REQUIRES(mLock);
+ void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+ float yOffset = 0);
+
+ void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
+ BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
+ REQUIRES(mLock);
+ void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+ std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
+ float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+
+ void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
+ bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
+ const InjectionState* injectionState);
+ bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
+ int32_t y) const REQUIRES(mLock);
+ bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
+ const sp<InputWindowHandle>& windowHandle);
+
+ std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
+ const sp<InputWindowHandle>& windowHandle,
+ const EventEntry* eventEntry,
+ const char* targetType) REQUIRES(mLock);
+
+ // Manage the dispatch cycle for a single connection.
+ // These methods are deliberately not Interruptible because doing all of the work
+ // with the mutex held makes it easier to ensure that connection invariants are maintained.
+ // If needed, the methods post commands to run later once the critical bits are done.
+ void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget)
+ REQUIRES(mLock);
+ void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget)
+ REQUIRES(mLock);
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
+ const InputTarget* inputTarget, int32_t dispatchMode)
+ REQUIRES(mLock);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+ REQUIRES(mLock);
+ void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ uint32_t seq, bool handled) REQUIRES(mLock);
+ void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ bool notify) REQUIRES(mLock);
+ void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
+ void releaseDispatchEntry(DispatchEntry* dispatchEntry);
+ static int handleReceiveCallback(int fd, int events, void* data);
+ // The action sent should only be of type AMOTION_EVENT_*
+ void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) REQUIRES(mLock);
+
+ void synthesizeCancelationEventsForAllConnectionsLocked(const CancelationOptions& options)
+ REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options)
+ REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
+ void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
+ const CancelationOptions& options)
+ REQUIRES(mLock);
+ void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
+ const CancelationOptions& options)
+ REQUIRES(mLock);
+
+ // Splitting motion events across windows.
+ MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
+ // Reset and drop everything the dispatcher is doing.
+ void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
+
+ // Dump state.
+ void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
+ void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
+ void logDispatchStateLocked() REQUIRES(mLock);
+
+ // Registration.
+ void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(
+ const sp<InputChannel>& inputChannel,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
+ status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ REQUIRES(mLock);
+
+ // Interesting events that we might like to log or tell the framework about.
+ void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ uint32_t seq, bool handled) REQUIRES(mLock);
+ void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
+ REQUIRES(mLock);
+ void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+ const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
+ void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
+ const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
+ nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+
+ // Outbound policy interactions.
+ void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+ DispatchEntry* dispatchEntry, KeyEntry* keyEntry,
+ bool handled) REQUIRES(mLock);
+ bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+ DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
+ bool handled) REQUIRES(mLock);
+ void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+ void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+
+ // Statistics gathering.
+ static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min;
+ LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
+
+ void reportTouchEventForStatistics(const MotionEntry& entry);
+ void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+ void traceInboundQueueLengthLocked() REQUIRES(mLock);
+ void traceOutboundQueueLength(const sp<Connection>& connection);
+ void traceWaitQueueLength(const sp<Connection>& connection);
+
+ sp<InputReporterInterface> mReporter;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
similarity index 64%
copy from services/inputflinger/InputReaderFactory.cpp
copy to services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index 072499b..8d7fa75 100644
--- a/services/inputflinger/InputReaderFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-#include "InputReaderFactory.h"
-#include "InputReader.h"
+#include "InputDispatcherFactory.h"
+#include "InputDispatcher.h"
namespace android {
-sp<InputReaderInterface> createInputReader(
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) {
- return new InputReader(std::make_unique<EventHub>(), policy, listener);
+sp<InputDispatcherInterface> createInputDispatcher(
+ const sp<InputDispatcherPolicyInterface>& policy) {
+ return new android::inputdispatcher::InputDispatcher(policy);
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
new file mode 100644
index 0000000..18b1b8c
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputDispatcherThread.h"
+
+#include "InputDispatcherInterface.h"
+
+namespace android {
+
+InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher)
+ : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {}
+
+InputDispatcherThread::~InputDispatcherThread() {}
+
+bool InputDispatcherThread::threadLoop() {
+ mDispatcher->dispatchOnce();
+ return true;
+}
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
new file mode 100644
index 0000000..c60700e
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputState.h"
+
+namespace android::inputdispatcher {
+
+InputState::InputState() {}
+
+InputState::~InputState() {}
+
+bool InputState::isNeutral() const {
+ return mKeyMementos.empty() && mMotionMementos.empty();
+}
+
+bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
+ for (const MotionMemento& memento : mMotionMementos) {
+ if (memento.deviceId == deviceId && memento.source == source &&
+ memento.displayId == displayId && memento.hovering) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_UP: {
+ if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+ for (size_t i = 0; i < mFallbackKeys.size();) {
+ if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+ mFallbackKeys.removeItemsAt(i);
+ } else {
+ i += 1;
+ }
+ }
+ }
+ ssize_t index = findKeyMemento(entry);
+ if (index >= 0) {
+ mKeyMementos.erase(mKeyMementos.begin() + index);
+ return true;
+ }
+ /* FIXME: We can't just drop the key up event because that prevents creating
+ * popup windows that are automatically shown when a key is held and then
+ * dismissed when the key is released. The problem is that the popup will
+ * not have received the original key down, so the key up will be considered
+ * to be inconsistent with its observed state. We could perhaps handle this
+ * by synthesizing a key down but that will cause other problems.
+ *
+ * So for now, allow inconsistent key up events to be dispatched.
+ *
+ #if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
+ "keyCode=%d, scanCode=%d",
+ entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+ #endif
+ return false;
+ */
+ return true;
+ }
+
+ case AKEY_EVENT_ACTION_DOWN: {
+ ssize_t index = findKeyMemento(entry);
+ if (index >= 0) {
+ mKeyMementos.erase(mKeyMementos.begin() + index);
+ }
+ addKeyMemento(entry, flags);
+ return true;
+ }
+
+ default:
+ return true;
+ }
+}
+
+bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) {
+ int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.erase(mMotionMementos.begin() + index);
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
+ "displayId=%" PRId32 ", actionMasked=%d",
+ entry->deviceId, entry->source, entry->displayId, actionMasked);
+#endif
+ return false;
+ }
+
+ case AMOTION_EVENT_ACTION_DOWN: {
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.erase(mMotionMementos.begin() + index);
+ }
+ addMotionMemento(entry, flags, false /*hovering*/);
+ return true;
+ }
+
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_MOVE: {
+ if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+ // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
+ // to generate cancellation events for these since they're based in relative rather
+ // than absolute units.
+ return true;
+ }
+
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+
+ if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+ // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
+ // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
+ // Any other value and we need to track the motion so we can send cancellation
+ // events for anything generating fallback events (e.g. DPad keys for joystick
+ // movements).
+ if (index >= 0) {
+ if (entry->pointerCoords[0].isEmpty()) {
+ mMotionMementos.erase(mMotionMementos.begin() + index);
+ } else {
+ MotionMemento& memento = mMotionMementos[index];
+ memento.setPointers(entry);
+ }
+ } else if (!entry->pointerCoords[0].isEmpty()) {
+ addMotionMemento(entry, flags, false /*hovering*/);
+ }
+
+ // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
+ return true;
+ }
+ if (index >= 0) {
+ MotionMemento& memento = mMotionMementos[index];
+ memento.setPointers(entry);
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Dropping inconsistent motion pointer up/down or move event: "
+ "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
+ entry->deviceId, entry->source, entry->displayId, actionMasked);
+#endif
+ return false;
+ }
+
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ ssize_t index = findMotionMemento(entry, true /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.erase(mMotionMementos.begin() + index);
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
+ "displayId=%" PRId32,
+ entry->deviceId, entry->source, entry->displayId);
+#endif
+ return false;
+ }
+
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE: {
+ ssize_t index = findMotionMemento(entry, true /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.erase(mMotionMementos.begin() + index);
+ }
+ addMotionMemento(entry, flags, true /*hovering*/);
+ return true;
+ }
+
+ default:
+ return true;
+ }
+}
+
+ssize_t InputState::findKeyMemento(const KeyEntry* entry) const {
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ const KeyMemento& memento = mKeyMementos[i];
+ if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
+ memento.displayId == entry->displayId && memento.keyCode == entry->keyCode &&
+ memento.scanCode == entry->scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const {
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos[i];
+ if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
+ memento.displayId == entry->displayId && memento.hovering == hovering) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+ KeyMemento memento;
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.displayId = entry->displayId;
+ memento.keyCode = entry->keyCode;
+ memento.scanCode = entry->scanCode;
+ memento.metaState = entry->metaState;
+ memento.flags = flags;
+ memento.downTime = entry->downTime;
+ memento.policyFlags = entry->policyFlags;
+ mKeyMementos.push_back(memento);
+}
+
+void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) {
+ MotionMemento memento;
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.displayId = entry->displayId;
+ memento.flags = flags;
+ memento.xPrecision = entry->xPrecision;
+ memento.yPrecision = entry->yPrecision;
+ memento.xCursorPosition = entry->xCursorPosition;
+ memento.yCursorPosition = entry->yCursorPosition;
+ memento.downTime = entry->downTime;
+ memento.setPointers(entry);
+ memento.hovering = hovering;
+ memento.policyFlags = entry->policyFlags;
+ mMotionMementos.push_back(memento);
+}
+
+void InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+ pointerCount = entry->pointerCount;
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ pointerProperties[i].copyFrom(entry->pointerProperties[i]);
+ pointerCoords[i].copyFrom(entry->pointerCoords[i]);
+ }
+}
+
+void InputState::synthesizeCancelationEvents(nsecs_t currentTime,
+ std::vector<EventEntry*>& outEvents,
+ const CancelationOptions& options) {
+ for (KeyMemento& memento : mKeyMementos) {
+ if (shouldCancelKey(memento, options)) {
+ outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId,
+ memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ memento.flags | AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, memento.metaState,
+ 0, memento.downTime));
+ }
+ }
+
+ for (const MotionMemento& memento : mMotionMementos) {
+ if (shouldCancelMotion(memento, options)) {
+ const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
+ : AMOTION_EVENT_ACTION_CANCEL;
+ outEvents.push_back(
+ new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
+ memento.source, memento.displayId, memento.policyFlags, action,
+ 0 /*actionButton*/, memento.flags, AMETA_NONE,
+ 0 /*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime, memento.pointerCount,
+ memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
+ 0 /*yOffset*/));
+ }
+ }
+}
+
+void InputState::clear() {
+ mKeyMementos.clear();
+ mMotionMementos.clear();
+ mFallbackKeys.clear();
+}
+
+void InputState::copyPointerStateTo(InputState& other) const {
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos[i];
+ if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+ for (size_t j = 0; j < other.mMotionMementos.size();) {
+ const MotionMemento& otherMemento = other.mMotionMementos[j];
+ if (memento.deviceId == otherMemento.deviceId &&
+ memento.source == otherMemento.source &&
+ memento.displayId == otherMemento.displayId) {
+ other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
+ } else {
+ j += 1;
+ }
+ }
+ other.mMotionMementos.push_back(memento);
+ }
+ }
+}
+
+int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
+}
+
+void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ if (index >= 0) {
+ mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
+ } else {
+ mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
+ }
+}
+
+void InputState::removeFallbackKey(int32_t originalKeyCode) {
+ mFallbackKeys.removeItem(originalKeyCode);
+}
+
+bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) {
+ if (options.keyCode && memento.keyCode != options.keyCode.value()) {
+ return false;
+ }
+
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
+ return false;
+ }
+
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ return true;
+ case CancelationOptions::CANCEL_FALLBACK_EVENTS:
+ return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
+ default:
+ return false;
+ }
+}
+
+bool InputState::shouldCancelMotion(const MotionMemento& memento,
+ const CancelationOptions& options) {
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
+ return false;
+ }
+
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
+ return true;
+ case CancelationOptions::CANCEL_POINTER_EVENTS:
+ return memento.source & AINPUT_SOURCE_CLASS_POINTER;
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+ default:
+ return false;
+ }
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
new file mode 100644
index 0000000..47e9b36
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+
+#include "CancelationOptions.h"
+#include "Entry.h"
+
+#include <utils/Timers.h>
+
+namespace android::inputdispatcher {
+
+/* Tracks dispatched key and motion event state so that cancellation events can be
+ * synthesized when events are dropped. */
+class InputState {
+public:
+ InputState();
+ ~InputState();
+
+ // Returns true if there is no state to be canceled.
+ bool isNeutral() const;
+
+ // Returns true if the specified source is known to have received a hover enter
+ // motion event.
+ bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
+
+ // 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);
+
+ // 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);
+
+ // Synthesizes cancelation events for the current state and resets the tracked state.
+ void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents,
+ const CancelationOptions& options);
+
+ // Clears the current state.
+ void clear();
+
+ // Copies pointer-related parts of the input state to another instance.
+ void copyPointerStateTo(InputState& other) const;
+
+ // Gets the fallback key associated with a keycode.
+ // Returns -1 if none.
+ // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
+ int32_t getFallbackKey(int32_t originalKeyCode);
+
+ // Sets the fallback key for a particular keycode.
+ void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
+
+ // Removes the fallback key for a particular keycode.
+ void removeFallbackKey(int32_t originalKeyCode);
+
+ inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const { return mFallbackKeys; }
+
+private:
+ struct KeyMemento {
+ int32_t deviceId;
+ uint32_t source;
+ int32_t displayId;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t metaState;
+ int32_t flags;
+ nsecs_t downTime;
+ uint32_t policyFlags;
+ };
+
+ struct MotionMemento {
+ int32_t deviceId;
+ uint32_t source;
+ int32_t displayId;
+ int32_t flags;
+ float xPrecision;
+ float yPrecision;
+ float xCursorPosition;
+ float yCursorPosition;
+ nsecs_t downTime;
+ uint32_t pointerCount;
+ PointerProperties pointerProperties[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+ bool hovering;
+ uint32_t policyFlags;
+
+ void setPointers(const MotionEntry* entry);
+ };
+
+ std::vector<KeyMemento> mKeyMementos;
+ std::vector<MotionMemento> mMotionMementos;
+ KeyedVector<int32_t, int32_t> mFallbackKeys;
+
+ ssize_t findKeyMemento(const KeyEntry* entry) const;
+ ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+
+ void addKeyMemento(const KeyEntry* entry, int32_t flags);
+ void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+
+ static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
+ static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
new file mode 100644
index 0000000..80fa2cb
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputTarget.h"
+
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
+#include <string>
+
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+std::string dispatchModeToString(int32_t dispatchMode) {
+ switch (dispatchMode) {
+ case InputTarget::FLAG_DISPATCH_AS_IS:
+ return "DISPATCH_AS_IS";
+ case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
+ return "DISPATCH_AS_OUTSIDE";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
+ return "DISPATCH_AS_HOVER_ENTER";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
+ return "DISPATCH_AS_HOVER_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
+ return "DISPATCH_AS_SLIPPERY_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
+ return "DISPATCH_AS_SLIPPERY_ENTER";
+ }
+ return StringPrintf("%" PRId32, dispatchMode);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
new file mode 100644
index 0000000..5acf92b
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+
+#include <input/InputTransport.h>
+#include <utils/BitSet.h>
+#include <utils/RefBase.h>
+
+namespace android::inputdispatcher {
+
+/*
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ */
+struct InputTarget {
+ enum {
+ /* This flag indicates that the event is being delivered to a foreground application. */
+ FLAG_FOREGROUND = 1 << 0,
+
+ /* This flag indicates that the MotionEvent falls within the area of the target
+ * obscured by another visible window above it. The motion event should be
+ * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
+ FLAG_WINDOW_IS_OBSCURED = 1 << 1,
+
+ /* This flag indicates that a motion event is being split across multiple windows. */
+ FLAG_SPLIT = 1 << 2,
+
+ /* This flag indicates that the pointer coordinates dispatched to the application
+ * will be zeroed out to avoid revealing information to an application. This is
+ * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
+ * the same UID from watching all touches. */
+ FLAG_ZERO_COORDS = 1 << 3,
+
+ /* This flag indicates that the event should be sent as is.
+ * Should always be set unless the event is to be transmuted. */
+ FLAG_DISPATCH_AS_IS = 1 << 8,
+
+ /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
+ * of the area of this target and so should instead be delivered as an
+ * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
+ FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
+
+ /* This flag indicates that a hover sequence is starting in the given window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
+
+ /* This flag indicates that a hover event happened outside of a window which handled
+ * previous hover events, signifying the end of the current hover sequence for that
+ * window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
+
+ /* This flag indicates that the event should be canceled.
+ * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
+ * outside of a window. */
+ FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
+
+ /* This flag indicates that the event should be dispatched as an initial down.
+ * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
+ * into a new window. */
+ FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
+
+ /* Mask for all dispatch modes. */
+ FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE |
+ FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT |
+ FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
+
+ /* This flag indicates that the target of a MotionEvent is partly or wholly
+ * obscured by another visible window above it. The motion event should be
+ * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
+ FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
+
+ };
+
+ // The input channel to be targeted.
+ sp<InputChannel> inputChannel;
+
+ // Flags for the input target.
+ int32_t flags;
+
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float xOffset, yOffset;
+
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+
+ // The subset of pointer ids to include in motion events dispatched to this input target
+ // if FLAG_SPLIT is set.
+ BitSet32 pointerIds;
+};
+
+std::string dispatchModeToString(int32_t dispatchMode);
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
new file mode 100644
index 0000000..289b084
--- /dev/null
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Monitor.h"
+
+namespace android::inputdispatcher {
+
+// --- Monitor ---
+Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+
+// --- TouchedMonitor ---
+TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
+ : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
new file mode 100644
index 0000000..b67c9eb
--- /dev/null
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+
+#include <input/InputTransport.h>
+
+namespace android::inputdispatcher {
+
+struct Monitor {
+ sp<InputChannel> inputChannel; // never null
+
+ explicit Monitor(const sp<InputChannel>& inputChannel);
+};
+
+// For tracking the offsets we need to apply when adding gesture monitor targets.
+struct TouchedMonitor {
+ Monitor monitor;
+ float xOffset = 0.f;
+ float yOffset = 0.f;
+
+ explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
new file mode 100644
index 0000000..18848a0
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputWindow.h>
+
+#include "InputTarget.h"
+
+#include "TouchState.h"
+
+using android::InputWindowHandle;
+
+namespace android::inputdispatcher {
+
+TouchState::TouchState()
+ : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {}
+
+TouchState::~TouchState() {}
+
+void TouchState::reset() {
+ down = false;
+ split = false;
+ deviceId = -1;
+ source = 0;
+ displayId = ADISPLAY_ID_NONE;
+ windows.clear();
+ portalWindows.clear();
+ gestureMonitors.clear();
+}
+
+void TouchState::copyFrom(const TouchState& other) {
+ down = other.down;
+ split = other.split;
+ deviceId = other.deviceId;
+ source = other.source;
+ displayId = other.displayId;
+ windows = other.windows;
+ portalWindows = other.portalWindows;
+ gestureMonitors = other.gestureMonitors;
+}
+
+void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
+ BitSet32 pointerIds) {
+ if (targetFlags & InputTarget::FLAG_SPLIT) {
+ split = true;
+ }
+
+ for (size_t i = 0; i < windows.size(); i++) {
+ TouchedWindow& touchedWindow = windows[i];
+ if (touchedWindow.windowHandle == windowHandle) {
+ touchedWindow.targetFlags |= targetFlags;
+ if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
+ }
+ touchedWindow.pointerIds.value |= pointerIds.value;
+ return;
+ }
+ }
+
+ TouchedWindow touchedWindow;
+ touchedWindow.windowHandle = windowHandle;
+ touchedWindow.targetFlags = targetFlags;
+ touchedWindow.pointerIds = pointerIds;
+ windows.push_back(touchedWindow);
+}
+
+void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+ size_t numWindows = portalWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ if (portalWindows[i] == windowHandle) {
+ return;
+ }
+ }
+ portalWindows.push_back(windowHandle);
+}
+
+void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
+ const size_t newSize = gestureMonitors.size() + newMonitors.size();
+ gestureMonitors.reserve(newSize);
+ gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors),
+ std::end(newMonitors));
+}
+
+void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
+ for (size_t i = 0; i < windows.size(); i++) {
+ if (windows[i].windowHandle == windowHandle) {
+ windows.erase(windows.begin() + i);
+ return;
+ }
+ }
+}
+
+void TouchState::removeWindowByToken(const sp<IBinder>& token) {
+ for (size_t i = 0; i < windows.size(); i++) {
+ if (windows[i].windowHandle->getToken() == token) {
+ windows.erase(windows.begin() + i);
+ return;
+ }
+ }
+}
+
+void TouchState::filterNonAsIsTouchWindows() {
+ for (size_t i = 0; i < windows.size();) {
+ TouchedWindow& window = windows[i];
+ if (window.targetFlags &
+ (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
+ window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
+ window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
+ i += 1;
+ } else {
+ windows.erase(windows.begin() + i);
+ }
+ }
+}
+
+void TouchState::filterNonMonitors() {
+ windows.clear();
+ portalWindows.clear();
+}
+
+sp<InputWindowHandle> TouchState::getFirstForegroundWindowHandle() const {
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& window = windows[i];
+ if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ return window.windowHandle;
+ }
+ }
+ return nullptr;
+}
+
+bool TouchState::isSlippery() const {
+ // Must have exactly one foreground window.
+ bool haveSlipperyForegroundWindow = false;
+ for (const TouchedWindow& window : windows) {
+ if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ if (haveSlipperyForegroundWindow ||
+ !(window.windowHandle->getInfo()->layoutParamsFlags &
+ InputWindowInfo::FLAG_SLIPPERY)) {
+ return false;
+ }
+ haveSlipperyForegroundWindow = true;
+ }
+ }
+ return haveSlipperyForegroundWindow;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
new file mode 100644
index 0000000..3e0e0eb
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+
+#include "Monitor.h"
+#include "TouchedWindow.h"
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+
+struct TouchState {
+ bool down;
+ bool split;
+ int32_t deviceId; // id of the device that is currently down, others are rejected
+ uint32_t source; // source of the device that is current down, others are rejected
+ int32_t displayId; // id to the display that currently has a touch, others are rejected
+ std::vector<TouchedWindow> windows;
+
+ // This collects the portal windows that the touch has gone through. Each portal window
+ // targets a display (embedded display for most cases). With this info, we can add the
+ // monitoring channels of the displays touched.
+ std::vector<sp<android::InputWindowHandle>> portalWindows;
+
+ std::vector<TouchedMonitor> gestureMonitors;
+
+ TouchState();
+ ~TouchState();
+ void reset();
+ void copyFrom(const TouchState& other);
+ void addOrUpdateWindow(const sp<android::InputWindowHandle>& windowHandle, int32_t targetFlags,
+ BitSet32 pointerIds);
+ void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle);
+ void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
+ void removeWindow(const sp<android::InputWindowHandle>& windowHandle);
+ void removeWindowByToken(const sp<IBinder>& token);
+ void filterNonAsIsTouchWindows();
+ void filterNonMonitors();
+ sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
+ bool isSlippery() const;
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
new file mode 100644
index 0000000..8713aa3
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+
+// Focus tracking for touch.
+struct TouchedWindow {
+ sp<android::InputWindowHandle> windowHandle;
+ int32_t targetFlags;
+ BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
new file mode 100644
index 0000000..00abf47
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/*
+ * Input dispatcher configuration.
+ *
+ * Specifies various options that modify the behavior of the input dispatcher.
+ * The values provided here are merely defaults. The actual values will come from ViewConfiguration
+ * and are passed into the dispatcher during initialization.
+ */
+struct InputDispatcherConfiguration {
+ // The key repeat initial timeout.
+ nsecs_t keyRepeatTimeout;
+
+ // The key repeat inter-key delay.
+ nsecs_t keyRepeatDelay;
+
+ InputDispatcherConfiguration()
+ : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {}
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
new file mode 100644
index 0000000..a359557
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+
+#include <utils/RefBase.h>
+
+#include "InputDispatcherInterface.h"
+#include "InputDispatcherPolicyInterface.h"
+
+namespace android {
+
+// This factory method is used to encapsulate implementation details in internal header files.
+sp<InputDispatcherInterface> createInputDispatcher(
+ const sp<InputDispatcherPolicyInterface>& policy);
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
new file mode 100644
index 0000000..9329ca6
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+
+#include <InputListener.h>
+#include <input/ISetInputWindowsListener.h>
+
+namespace android {
+
+class InputApplicationHandle;
+class InputChannel;
+class InputWindowHandle;
+
+/*
+ * Constants used to report the outcome of input event injection.
+ */
+enum {
+ /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+ INPUT_EVENT_INJECTION_PENDING = -1,
+
+ /* Injection succeeded. */
+ INPUT_EVENT_INJECTION_SUCCEEDED = 0,
+
+ /* Injection failed because the injector did not have permission to inject
+ * into the application with input focus. */
+ INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
+
+ /* Injection failed because there were no available input targets. */
+ INPUT_EVENT_INJECTION_FAILED = 2,
+
+ /* Injection failed due to a timeout. */
+ INPUT_EVENT_INJECTION_TIMED_OUT = 3
+};
+
+/* Notifies the system about input events generated by the input reader.
+ * The dispatcher is expected to be mostly asynchronous. */
+class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
+protected:
+ InputDispatcherInterface() {}
+ virtual ~InputDispatcherInterface() {}
+
+public:
+ /* Dumps the state of the input dispatcher.
+ *
+ * This method may be called on any thread (usually by the input manager). */
+ virtual void dump(std::string& dump) = 0;
+
+ /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
+ virtual void monitor() = 0;
+
+ /* Runs a single iteration of the dispatch loop.
+ * Nominally processes one queued event, a timeout, or a response from an input consumer.
+ *
+ * This method should only be called on the input dispatcher thread.
+ */
+ virtual void dispatchOnce() = 0;
+
+ /* Injects an input event and optionally waits for sync.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
+ int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) = 0;
+
+ /* Sets the list of input windows.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setInputWindows(
+ const std::vector<sp<InputWindowHandle> >& inputWindowHandles, int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
+
+ /* Sets the focused application on the given display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setFocusedApplication(
+ int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+ /* Sets the focused display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setFocusedDisplay(int32_t displayId) = 0;
+
+ /* Sets the input dispatching mode.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+
+ /* Sets whether input event filtering is enabled.
+ * When enabled, incoming input events are sent to the policy's filterInputEvent
+ * method instead of being dispatched. The filter is expected to use
+ * injectInputEvent to inject the events it would like to have dispatched.
+ * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
+ */
+ virtual void setInputFilterEnabled(bool enabled) = 0;
+
+ /* Transfers touch focus from one window to another window.
+ *
+ * Returns true on success. False if the window did not actually have touch focus.
+ */
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+
+ /* Registers input channels that may be used as targets for input events.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
+ int32_t displayId) = 0;
+
+ /* Registers input channels to be used to monitor input events.
+ *
+ * Each monitor must target a specific display and will only receive input events sent to that
+ * display. If the monitor is a gesture monitor, it will only receive pointer events on the
+ * targeted display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
+ bool gestureMonitor) = 0;
+
+ /* Unregister input channels that will no longer receive input events.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Allows an input monitor steal the current pointer stream away from normal input windows.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
new file mode 100644
index 0000000..4214488
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+
+#include "InputDispatcherConfiguration.h"
+
+#include <binder/IBinder.h>
+#include <input/Input.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class InputApplicationHandle;
+
+/*
+ * Input dispatcher policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI. This interface is also mocked in the unit tests.
+ */
+class InputDispatcherPolicyInterface : public virtual RefBase {
+protected:
+ InputDispatcherPolicyInterface() {}
+ virtual ~InputDispatcherPolicyInterface() {}
+
+public:
+ /* Notifies the system that a configuration change has occurred. */
+ virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
+ /* Notifies the system that an application is not responding.
+ * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) = 0;
+
+ /* Notifies the system that an input channel is unrecoverably broken. */
+ virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+ virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
+
+ /* Gets the input dispatcher configuration. */
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
+
+ /* Filters an input event.
+ * Return true to dispatch the event unmodified, false to consume the event.
+ * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
+ * to injectInputEvent.
+ */
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+
+ /* Intercepts a key event immediately before queueing it.
+ * The policy can use this method as an opportunity to perform power management functions
+ * and early event preprocessing such as updating policy flags.
+ *
+ * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
+ * should be dispatched to applications.
+ */
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
+
+ /* Intercepts a touch, trackball or other motion event before queueing it.
+ * The policy can use this method as an opportunity to perform power management functions
+ * and early event preprocessing such as updating policy flags.
+ *
+ * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
+ * should be dispatched to applications.
+ */
+ virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ uint32_t& policyFlags) = 0;
+
+ /* Allows the policy a chance to intercept a key before dispatching. */
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
+ const KeyEvent* keyEvent,
+ uint32_t policyFlags) = 0;
+
+ /* Allows the policy a chance to perform default processing for an unhandled key.
+ * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
+ virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
+
+ /* Notifies the policy about switch events.
+ */
+ virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) = 0;
+
+ /* Poke user activity for an event dispatched to a window. */
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+
+ /* Checks whether a given application pid/uid has permission to inject input events
+ * into other applications.
+ *
+ * This method is special in that its implementation promises to be non-reentrant and
+ * is safe to call while holding other locks. (Most other methods make no such guarantees!)
+ */
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
+ int32_t injectorUid) = 0;
+
+ /* Notifies the policy that a pointer down event has occurred outside the current focused
+ * window.
+ *
+ * The touchedToken passed as an argument is the window that received the input event.
+ */
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
new file mode 100644
index 0000000..2604959
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
+
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class InputDispatcherInterface;
+
+/* Enqueues and dispatches input events, endlessly. */
+class InputDispatcherThread : public Thread {
+public:
+ explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
+ ~InputDispatcherThread();
+
+private:
+ virtual bool threadLoop();
+
+ sp<InputDispatcherInterface> mDispatcher;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index d8b352c..973b4f9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -42,7 +42,6 @@
virtual status_t dump(int fd, const Vector<String16>& args);
void setInputWindows(const std::vector<InputWindowInfo>&,
const sp<ISetInputWindowsListener>&) {}
- void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {}
void registerInputChannel(const sp<InputChannel>&) {}
void unregisterInputChannel(const sp<InputChannel>&) {}
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
new file mode 100644
index 0000000..3c16070
--- /dev/null
+++ b/services/inputflinger/reader/Android.bp
@@ -0,0 +1,72 @@
+// Copyright (C) 2019 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.
+
+cc_library_headers {
+ name: "libinputreader_headers",
+ export_include_dirs: [
+ "include",
+ "mapper",
+ "mapper/accumulator",
+ ],
+}
+
+cc_library_shared {
+ name: "libinputreader",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "EventHub.cpp",
+ "InputDevice.cpp",
+ "mapper/accumulator/CursorButtonAccumulator.cpp",
+ "mapper/accumulator/CursorScrollAccumulator.cpp",
+ "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
+ "mapper/accumulator/TouchButtonAccumulator.cpp",
+ "mapper/CursorInputMapper.cpp",
+ "mapper/ExternalStylusInputMapper.cpp",
+ "mapper/InputMapper.cpp",
+ "mapper/JoystickInputMapper.cpp",
+ "mapper/KeyboardInputMapper.cpp",
+ "mapper/MultiTouchInputMapper.cpp",
+ "mapper/RotaryEncoderInputMapper.cpp",
+ "mapper/SingleTouchInputMapper.cpp",
+ "mapper/SwitchInputMapper.cpp",
+ "mapper/TouchInputMapper.cpp",
+ "mapper/VibratorInputMapper.cpp",
+ "InputReader.cpp",
+ "InputReaderFactory.cpp",
+ "TouchVideoDevice.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcap",
+ "libinputflinger_base",
+ "libcrypto",
+ "libcutils",
+ "libinput",
+ "liblog",
+ "libui",
+ "libutils",
+ "libhardware_legacy",
+ ],
+
+ header_libs: [
+ "libinputflinger_headers",
+ "libinputreader_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libinputflinger_headers",
+ ],
+}
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
similarity index 81%
rename from services/inputflinger/EventHub.cpp
rename to services/inputflinger/reader/EventHub.cpp
index af02314..c8da0ab 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -24,10 +24,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/capability.h>
#include <sys/epoll.h>
-#include <sys/limits.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
+#include <sys/limits.h>
#include <unistd.h>
#define LOG_TAG "EventHub"
@@ -41,13 +42,13 @@
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <openssl/sha.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <input/KeyLayoutMap.h>
#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
/* this macro is used to tell if "bit" is set in "array"
@@ -55,10 +56,10 @@
* operation with a byte that only has the relevant bit set.
* eg. to check for the 12th bit, we do (array[1] & 1<<4)
*/
-#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
+#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) (((bits) + 7) / 8)
+#define sizeof_bit_array(bits) (((bits) + 7) / 8)
#define INDENT " "
#define INDENT2 " "
@@ -70,10 +71,10 @@
static constexpr bool DEBUG = false;
-static const char *WAKE_LOCK_ID = "KeyEvents";
-static const char *DEVICE_PATH = "/dev/input";
+static const char* WAKE_LOCK_ID = "KeyEvents";
+static const char* DEVICE_PATH = "/dev/input";
// v4l2 devices go directly into /dev
-static const char *VIDEO_DEVICE_PATH = "/dev";
+static const char* VIDEO_DEVICE_PATH = "/dev";
static inline const char* toString(bool value) {
return value ? "true" : "false";
@@ -117,7 +118,7 @@
* directly from /dev.
*/
static bool isV4lScanningEnabled() {
- return property_get_bool("ro.input.video_enabled", true /* default_value */);
+ return property_get_bool("ro.input.video_enabled", true /* default_value */);
}
static nsecs_t processEventTimestamp(const struct input_event& event) {
@@ -144,27 +145,27 @@
// Touch devices get dibs on touch-related axes.
if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
switch (axis) {
- case ABS_X:
- case ABS_Y:
- case ABS_PRESSURE:
- case ABS_TOOL_WIDTH:
- case ABS_DISTANCE:
- case ABS_TILT_X:
- case ABS_TILT_Y:
- case ABS_MT_SLOT:
- case ABS_MT_TOUCH_MAJOR:
- case ABS_MT_TOUCH_MINOR:
- case ABS_MT_WIDTH_MAJOR:
- case ABS_MT_WIDTH_MINOR:
- case ABS_MT_ORIENTATION:
- case ABS_MT_POSITION_X:
- case ABS_MT_POSITION_Y:
- case ABS_MT_TOOL_TYPE:
- case ABS_MT_BLOB_ID:
- case ABS_MT_TRACKING_ID:
- case ABS_MT_PRESSURE:
- case ABS_MT_DISTANCE:
- return INPUT_DEVICE_CLASS_TOUCH;
+ case ABS_X:
+ case ABS_Y:
+ case ABS_PRESSURE:
+ case ABS_TOOL_WIDTH:
+ case ABS_DISTANCE:
+ case ABS_TILT_X:
+ case ABS_TILT_Y:
+ case ABS_MT_SLOT:
+ case ABS_MT_TOUCH_MAJOR:
+ case ABS_MT_TOUCH_MINOR:
+ case ABS_MT_WIDTH_MAJOR:
+ case ABS_MT_WIDTH_MINOR:
+ case ABS_MT_ORIENTATION:
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
+ case ABS_MT_TOOL_TYPE:
+ case ABS_MT_BLOB_ID:
+ case ABS_MT_TRACKING_ID:
+ case ABS_MT_PRESSURE:
+ case ABS_MT_DISTANCE:
+ return INPUT_DEVICE_CLASS_TOUCH;
}
}
@@ -182,12 +183,20 @@
// --- EventHub::Device ---
EventHub::Device::Device(int fd, int32_t id, const std::string& path,
- const InputDeviceIdentifier& identifier) :
- next(nullptr),
- fd(fd), id(id), path(path), identifier(identifier),
- classes(0), configuration(nullptr), virtualKeyMap(nullptr),
- ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
- enabled(true), isVirtual(fd < 0) {
+ const InputDeviceIdentifier& identifier)
+ : next(nullptr),
+ fd(fd),
+ id(id),
+ path(path),
+ identifier(identifier),
+ classes(0),
+ configuration(nullptr),
+ virtualKeyMap(nullptr),
+ ffEffectPlaying(false),
+ ffEffectId(-1),
+ controllerNumber(0),
+ enabled(true),
+ isVirtual(fd < 0) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
@@ -211,7 +220,7 @@
status_t EventHub::Device::enable() {
fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
- if(fd < 0) {
+ if (fd < 0) {
ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno));
return -errno;
}
@@ -229,16 +238,64 @@
return !isVirtual && enabled;
}
+/**
+ * Get the capabilities for the current process.
+ * Crashes the system if unable to create / check / destroy the capabilities object.
+ */
+class Capabilities final {
+public:
+ explicit Capabilities() {
+ mCaps = cap_get_proc();
+ LOG_ALWAYS_FATAL_IF(mCaps == nullptr, "Could not get capabilities of the current process");
+ }
+
+ /**
+ * Check whether the current process has a specific capability
+ * in the set of effective capabilities.
+ * Return CAP_SET if the process has the requested capability
+ * Return CAP_CLEAR otherwise.
+ */
+ cap_flag_value_t checkEffectiveCapability(cap_value_t capability) {
+ cap_flag_value_t value;
+ const int result = cap_get_flag(mCaps, capability, CAP_EFFECTIVE, &value);
+ LOG_ALWAYS_FATAL_IF(result == -1, "Could not obtain the requested capability");
+ return value;
+ }
+
+ ~Capabilities() {
+ const int result = cap_free(mCaps);
+ LOG_ALWAYS_FATAL_IF(result == -1, "Could not release the capabilities structure");
+ }
+
+private:
+ cap_t mCaps;
+};
+
+static void ensureProcessCanBlockSuspend() {
+ Capabilities capabilities;
+ const bool canBlockSuspend =
+ capabilities.checkEffectiveCapability(CAP_BLOCK_SUSPEND) == CAP_SET;
+ LOG_ALWAYS_FATAL_IF(!canBlockSuspend,
+ "Input must be able to block suspend to properly process events");
+}
+
// --- EventHub ---
const int EventHub::EPOLL_MAX_EVENTS;
-EventHub::EventHub(void) :
- mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
- mOpeningDevices(nullptr), mClosingDevices(nullptr),
+EventHub::EventHub(void)
+ : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
+ mNextDeviceId(1),
+ mControllerNumbers(),
+ mOpeningDevices(nullptr),
+ mClosingDevices(nullptr),
mNeedToSendFinishedDeviceScan(false),
- mNeedToReopenDevices(false), mNeedToScanDevices(true),
- mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
+ mNeedToReopenDevices(false),
+ mNeedToScanDevices(true),
+ mPendingEventCount(0),
+ mPendingEventIndex(0),
+ mPendingINotify(false) {
+ ensureProcessCanBlockSuspend();
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
@@ -246,20 +303,19 @@
mINotifyFd = inotify_init();
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
- DEVICE_PATH, strerror(errno));
+ LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
+ strerror(errno));
if (isV4lScanningEnabled()) {
mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
- VIDEO_DEVICE_PATH, strerror(errno));
+ VIDEO_DEVICE_PATH, strerror(errno));
} else {
mVideoWd = -1;
ALOGI("Video device scanning disabled");
}
- struct epoll_event eventItem;
- memset(&eventItem, 0, sizeof(eventItem));
- eventItem.events = EPOLLIN;
+ struct epoll_event eventItem = {};
+ eventItem.events = EPOLLIN | EPOLLWAKEUP;
eventItem.data.fd = mINotifyFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
@@ -273,16 +329,16 @@
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
- errno);
+ errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
- errno);
+ errno);
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
- errno);
+ errno);
}
EventHub::~EventHub(void) {
@@ -334,7 +390,7 @@
}
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
+ RawAbsoluteAxisInfo* outAxisInfo) const {
outAxisInfo->clear();
if (axis >= 0 && axis <= ABS_MAX) {
@@ -343,9 +399,9 @@
Device* device = getDeviceLocked(deviceId);
if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
struct input_absinfo info;
- if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
- axis, device->identifier.name.c_str(), device->fd, errno);
+ if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
+ ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
+ device->identifier.name.c_str(), device->fd, errno);
return -errno;
}
@@ -452,9 +508,9 @@
Device* device = getDeviceLocked(deviceId);
if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
struct input_absinfo info;
- if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
- axis, device->identifier.name.c_str(), device->fd, errno);
+ if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
+ ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
+ device->identifier.name.c_str(), device->fd, errno);
return -errno;
}
@@ -465,8 +521,8 @@
return -1;
}
-bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) const {
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -475,9 +531,9 @@
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
- status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(
- keyCodes[codeIndex], &scanCodes);
- if (! err) {
+ status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex],
+ &scanCodes);
+ if (!err) {
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
for (size_t sc = 0; sc < scanCodes.size(); sc++) {
@@ -493,9 +549,8 @@
return false;
}
-status_t EventHub::mapKey(int32_t deviceId,
- int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
+status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
@@ -602,7 +657,7 @@
}
void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
- std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
outVirtualKeys.clear();
AutoMutex _l(mLock);
@@ -623,15 +678,13 @@
return nullptr;
}
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
- const sp<KeyCharacterMap>& map) {
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device) {
if (map != device->overlayKeyMap) {
device->overlayKeyMap = map;
- device->combinedKeyMap = KeyCharacterMap::combine(
- device->keyMap.keyCharacterMap, map);
+ device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
return true;
}
}
@@ -640,8 +693,7 @@
static std::string generateDescriptor(InputDeviceIdentifier& identifier) {
std::string rawDescriptor;
- rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor,
- identifier.product);
+ rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, identifier.product);
// TODO add handling for USB devices to not uniqueify kbs that show up twice
if (!identifier.uniqueId.empty()) {
rawDescriptor += "uniqueId:";
@@ -680,13 +732,13 @@
if (identifier.uniqueId.empty()) {
// If it didn't have a unique id check for conflicts and enforce
// uniqueness if necessary.
- while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
+ while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
identifier.nonce++;
rawDescriptor = generateDescriptor(identifier);
}
}
ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(),
- identifier.descriptor.c_str());
+ identifier.descriptor.c_str());
}
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
@@ -703,7 +755,7 @@
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
- device->identifier.name.c_str(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
device->ffEffectId = effect.id;
@@ -716,7 +768,7 @@
ev.value = 1;
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
ALOGW("Could not start force feedback effect on device %s due to error %d.",
- device->identifier.name.c_str(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
device->ffEffectPlaying = true;
@@ -738,7 +790,7 @@
ev.value = 0;
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
ALOGW("Could not stop force feedback effect on device %s due to error %d.",
- device->identifier.name.c_str(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
}
@@ -825,12 +877,12 @@
// Report any devices that had last been added/removed.
while (mClosingDevices) {
Device* device = mClosingDevices;
- ALOGV("Reporting device closed: id=%d, name=%s\n",
- device->id, device->path.c_str());
+ ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
mClosingDevices = device->next;
event->when = now;
- event->deviceId = (device->id == mBuiltInKeyboardId) ?
- ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
+ event->deviceId = (device->id == mBuiltInKeyboardId)
+ ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
+ : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
@@ -848,8 +900,7 @@
while (mOpeningDevices != nullptr) {
Device* device = mOpeningDevices;
- ALOGV("Reporting device opened: id=%d, name=%s\n",
- device->id, device->path.c_str());
+ ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
@@ -895,15 +946,15 @@
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
- eventItem.events);
+ eventItem.events);
}
continue;
}
Device* device = getDeviceByFdLocked(eventItem.data.fd);
if (!device) {
- ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.",
- eventItem.events, eventItem.data.fd);
+ ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
+ eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
continue;
}
@@ -912,30 +963,30 @@
size_t numFrames = device->videoDevice->readAndQueueFrames();
if (numFrames == 0) {
ALOGE("Received epoll event for video device %s, but could not read frame",
- device->videoDevice->getName().c_str());
+ device->videoDevice->getName().c_str());
}
} else if (eventItem.events & EPOLLHUP) {
// TODO(b/121395353) - consider adding EPOLLRDHUP
ALOGI("Removing video device %s due to epoll hang-up event.",
- device->videoDevice->getName().c_str());
+ device->videoDevice->getName().c_str());
unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
device->videoDevice = nullptr;
} else {
- ALOGW("Received unexpected epoll event 0x%08x for device %s.",
- eventItem.events, device->videoDevice->getName().c_str());
+ ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
+ device->videoDevice->getName().c_str());
ALOG_ASSERT(!DEBUG);
}
continue;
}
// This must be an input event
if (eventItem.events & EPOLLIN) {
- int32_t readSize = read(device->fd, readBuffer,
- sizeof(struct input_event) * capacity);
+ int32_t readSize =
+ read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
- " bufferSize: %zu capacity: %zu errno: %d)\n",
- device->fd, readSize, bufferSize, capacity, errno);
+ " bufferSize: %zu capacity: %zu errno: %d)\n",
+ device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
@@ -967,12 +1018,12 @@
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
- device->identifier.name.c_str());
+ device->identifier.name.c_str());
deviceChanged = true;
closeDeviceLocked(device);
} else {
- ALOGW("Received unexpected epoll event 0x%08x for device %s.",
- eventItem.events, device->identifier.name.c_str());
+ ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
+ device->identifier.name.c_str());
}
}
@@ -1067,7 +1118,7 @@
void EventHub::scanDevicesLocked() {
status_t result = scanDirLocked(DEVICE_PATH);
- if(result < 0) {
+ if (result < 0) {
ALOGE("scan dir failed for %s", DEVICE_PATH);
}
if (isV4lScanningEnabled()) {
@@ -1095,12 +1146,12 @@
}
static const int32_t GAMEPAD_KEYCODES[] = {
- AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
- AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
- AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
- AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
- AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
- AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
+ AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
+ AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
+ AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, //
+ AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, //
+ AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, //
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, //
};
status_t EventHub::registerFdForEpoll(int fd) {
@@ -1167,7 +1218,7 @@
status_t result = unregisterFdFromEpoll(videoDevice.getFd());
if (result != OK) {
ALOGW("Could not remove video device fd from epoll for device: %s",
- videoDevice.getName().c_str());
+ videoDevice.getName().c_str());
}
}
}
@@ -1178,7 +1229,7 @@
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
- if(fd < 0) {
+ if (fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
@@ -1186,7 +1237,7 @@
InputDeviceIdentifier identifier;
// Get device name.
- if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
+ if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
@@ -1205,7 +1256,7 @@
// Get device driver version.
int driverVersion;
- if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
+ if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
@@ -1213,7 +1264,7 @@
// Get device identifier.
struct input_id inputId;
- if(ioctl(fd, EVIOCGID, &inputId)) {
+ if (ioctl(fd, EVIOCGID, &inputId)) {
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
@@ -1224,16 +1275,16 @@
identifier.version = inputId.version;
// Get device physical location.
- if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
+ if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
+ // fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.location = buffer;
}
// Get device unique id.
- if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
+ if (ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
+ // fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId = buffer;
@@ -1248,16 +1299,16 @@
ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV(" bus: %04x\n"
- " vendor %04x\n"
- " product %04x\n"
- " version %04x\n",
- identifier.bus, identifier.vendor, identifier.product, identifier.version);
+ " vendor %04x\n"
+ " product %04x\n"
+ " version %04x\n",
+ identifier.bus, identifier.vendor, identifier.product, identifier.version);
ALOGV(" name: \"%s\"\n", identifier.name.c_str());
ALOGV(" location: \"%s\"\n", identifier.location.c_str());
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str());
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str());
- ALOGV(" driver: v%d.%d.%d\n",
- driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
+ ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff,
+ driverVersion & 0xff);
// Load the configuration file for the device.
loadConfigurationLocked(device);
@@ -1273,21 +1324,21 @@
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
- bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
- || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX + 1));
+ bool haveKeyboardKeys =
+ containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
+ containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
+ sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
- sizeof_bit_array(BTN_MOUSE))
- || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
- sizeof_bit_array(BTN_DIGI));
+ sizeof_bit_array(BTN_MOUSE)) ||
+ containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
+ sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
- if (test_bit(BTN_MOUSE, device->keyBitmask)
- && test_bit(REL_X, device->relBitmask)
- && test_bit(REL_Y, device->relBitmask)) {
+ if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
+ test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
@@ -1295,31 +1346,29 @@
String8 deviceType = String8();
if (device->configuration &&
device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
- if (!deviceType.compare(String8("rotaryEncoder"))) {
- device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
- }
+ if (!deviceType.compare(String8("rotaryEncoder"))) {
+ device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
+ }
}
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
- && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+ if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
+ test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
- // Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, device->keyBitmask)
- && test_bit(ABS_X, device->absBitmask)
- && test_bit(ABS_Y, device->absBitmask)) {
+ // Is this an old style single-touch driver?
+ } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
+ test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
- // Is this a BT stylus?
+ // Is this a BT stylus?
} else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
- test_bit(BTN_TOUCH, device->keyBitmask))
- && !test_bit(ABS_X, device->absBitmask)
- && !test_bit(ABS_Y, device->absBitmask)) {
+ test_bit(BTN_TOUCH, device->keyBitmask)) &&
+ !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to reserve those so we
// can fuse it with the touch screen data, so just take them back. Note this means an
@@ -1333,8 +1382,8 @@
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
- if (test_bit(i, device->absBitmask)
- && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (test_bit(i, device->absBitmask) &&
+ (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
}
@@ -1375,10 +1424,8 @@
// Configure the keyboard, gamepad or virtual keyboard.
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
- if (!keyMapStatus
- && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
- && isEligibleBuiltInKeyboard(device->identifier,
- device->configuration, &device->keyMap)) {
+ if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
+ isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
@@ -1389,15 +1436,15 @@
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
+ hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
+ for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
@@ -1407,8 +1454,8 @@
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'",
- deviceId, devicePath, device->identifier.name.c_str());
+ ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+ device->identifier.name.c_str());
delete device;
return -1;
}
@@ -1423,8 +1470,8 @@
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
- if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
- && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) &&
+ device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
device->controllerNumber = getNextControllerNumberLocked(device);
setLedForControllerLocked(device);
}
@@ -1437,10 +1484,12 @@
break;
}
}
- mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
- mUnattachedVideoDevices.end(),
- [](const std::unique_ptr<TouchVideoDevice>& videoDevice){
- return videoDevice == nullptr; }), mUnattachedVideoDevices.end());
+ mUnattachedVideoDevices
+ .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
+ [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+ return videoDevice == nullptr;
+ }),
+ mUnattachedVideoDevices.end());
if (registerDeviceForEpollLocked(device) != OK) {
delete device;
@@ -1450,13 +1499,10 @@
configureFd(device);
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
- "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
- deviceId, fd, devicePath, device->identifier.name.c_str(),
- device->classes,
- device->configurationFile.c_str(),
- device->keyMap.keyLayoutFile.c_str(),
- device->keyMap.keyCharacterMapFile.c_str(),
- toString(mBuiltInKeyboardId == deviceId));
+ "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
+ deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
+ device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
+ device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
addDeviceLocked(device);
return OK;
@@ -1468,8 +1514,8 @@
// Disable kernel key repeat since we handle it ourselves
unsigned int repeatRate[] = {0, 0};
if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
- ALOGW("Unable to disable kernel key repeat for %s: %s",
- device->path.c_str(), strerror(errno));
+ ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(),
+ strerror(errno));
}
}
@@ -1503,7 +1549,7 @@
// Couldn't find a matching input device, so just add it to a temporary holding queue.
// A matching input device may appear later.
ALOGI("Adding video device %s to list of unattached video devices",
- videoDevice->getName().c_str());
+ videoDevice->getName().c_str());
mUnattachedVideoDevices.push_back(std::move(videoDevice));
}
@@ -1560,12 +1606,10 @@
identifier.uniqueId = "<virtual>";
assignDescriptorLocked(identifier);
- Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
- identifier);
- device->classes = INPUT_DEVICE_CLASS_KEYBOARD
- | INPUT_DEVICE_CLASS_ALPHAKEY
- | INPUT_DEVICE_CLASS_DPAD
- | INPUT_DEVICE_CLASS_VIRTUAL;
+ Device* device =
+ new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
+ device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY |
+ INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL;
loadKeyMapLocked(device);
addDeviceLocked(device);
}
@@ -1581,14 +1625,14 @@
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
- device->identifier.name.c_str());
+ device->identifier.name.c_str());
} else {
status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
- &device->configuration);
+ &device->configuration);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- device->identifier.name.c_str());
+ "Using default configuration.",
+ device->identifier.name.c_str());
}
}
}
@@ -1632,7 +1676,7 @@
int32_t EventHub::getNextControllerNumberLocked(Device* device) {
if (mControllerNumbers.isFull()) {
ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
- device->identifier.name.c_str());
+ device->identifier.name.c_str());
return 0;
}
// Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1642,7 +1686,7 @@
void EventHub::releaseControllerNumberLocked(Device* device) {
int32_t num = device->controllerNumber;
- device->controllerNumber= 0;
+ device->controllerNumber = 0;
if (num == 0) {
return;
}
@@ -1663,7 +1707,7 @@
std::vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
const size_t N = scanCodes.size();
- for (size_t i=0; i<N && i<=KEY_MAX; i++) {
+ for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
int32_t sc = scanCodes[i];
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
return true;
@@ -1679,8 +1723,8 @@
}
int32_t scanCode;
- if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
+ if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
+ if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
*outScanCode = scanCode;
return NO_ERROR;
}
@@ -1688,7 +1732,7 @@
return NAME_NOT_FOUND;
}
-void EventHub::closeDeviceByPathLocked(const char *devicePath) {
+void EventHub::closeDeviceByPathLocked(const char* devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
if (device) {
closeDeviceLocked(device);
@@ -1713,10 +1757,13 @@
return;
}
}
- mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
- mUnattachedVideoDevices.end(), [&devicePath](
- const std::unique_ptr<TouchVideoDevice>& videoDevice) {
- return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end());
+ mUnattachedVideoDevices
+ .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
+ [&devicePath](
+ const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+ return videoDevice->getPath() == devicePath;
+ }),
+ mUnattachedVideoDevices.end());
}
void EventHub::closeAllDevicesLocked() {
@@ -1727,13 +1774,12 @@
}
void EventHub::closeDeviceLocked(Device* device) {
- ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x",
- device->path.c_str(), device->identifier.name.c_str(), device->id,
- device->fd, device->classes);
+ ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
+ device->identifier.name.c_str(), device->id, device->fd, device->classes);
if (device->id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.c_str(), mBuiltInKeyboardId);
+ device->path.c_str(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
@@ -1751,7 +1797,7 @@
// Unlink for opening devices list if it is present.
Device* pred = nullptr;
bool found = false;
- for (Device* entry = mOpeningDevices; entry != nullptr; ) {
+ for (Device* entry = mOpeningDevices; entry != nullptr;) {
if (entry == device) {
found = true;
break;
@@ -1783,30 +1829,28 @@
char event_buf[512];
int event_size;
int event_pos = 0;
- struct inotify_event *event;
+ struct inotify_event* event;
ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
res = read(mINotifyFd, event_buf, sizeof(event_buf));
- if(res < (int)sizeof(*event)) {
- if(errno == EINTR)
- return 0;
+ if (res < (int)sizeof(*event)) {
+ if (errno == EINTR) return 0;
ALOGW("could not get event, %s\n", strerror(errno));
return -1;
}
- while(res >= (int)sizeof(*event)) {
- event = (struct inotify_event *)(event_buf + event_pos);
- if(event->len) {
+ while (res >= (int)sizeof(*event)) {
+ event = (struct inotify_event*)(event_buf + event_pos);
+ if (event->len) {
if (event->wd == mInputWd) {
std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
- if(event->mask & IN_CREATE) {
+ if (event->mask & IN_CREATE) {
openDeviceLocked(filename.c_str());
} else {
ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
closeDeviceByPathLocked(filename.c_str());
}
- }
- else if (event->wd == mVideoWd) {
+ } else if (event->wd == mVideoWd) {
if (isV4lTouchNode(event->name)) {
std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
if (event->mask & IN_CREATE) {
@@ -1816,8 +1860,7 @@
closeVideoDeviceByPathLocked(filename);
}
}
- }
- else {
+ } else {
LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
}
}
@@ -1828,22 +1871,19 @@
return 0;
}
-status_t EventHub::scanDirLocked(const char *dirname)
-{
+status_t EventHub::scanDirLocked(const char* dirname) {
char devname[PATH_MAX];
- char *filename;
- DIR *dir;
- struct dirent *de;
+ char* filename;
+ DIR* dir;
+ struct dirent* de;
dir = opendir(dirname);
- if(dir == nullptr)
- return -1;
+ if (dir == nullptr) return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
- while((de = readdir(dir))) {
- if(de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] == '.' &&
+ (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);
@@ -1855,17 +1895,16 @@
/**
* Look for all dirname/v4l-touch* devices, and open them.
*/
-status_t EventHub::scanVideoDirLocked(const std::string& dirname)
-{
+status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
DIR* dir;
struct dirent* de;
dir = opendir(dirname.c_str());
- if(!dir) {
+ if (!dir) {
ALOGE("Could not open video directory %s", dirname.c_str());
return BAD_VALUE;
}
- while((de = readdir(dir))) {
+ while ((de = readdir(dir))) {
const char* name = de->d_name;
if (isV4lTouchNode(name)) {
ALOGI("Found touch video device %s", name);
@@ -1897,10 +1936,10 @@
const Device* device = mDevices.valueAt(i);
if (mBuiltInKeyboardId == device->id) {
dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
- device->id, device->identifier.name.c_str());
+ device->id, device->identifier.name.c_str());
} else {
dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
- device->identifier.name.c_str());
+ device->identifier.name.c_str());
}
dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
@@ -1910,17 +1949,17 @@
dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
- "product=0x%04x, version=0x%04x\n",
- device->identifier.bus, device->identifier.vendor,
- device->identifier.product, device->identifier.version);
+ "product=0x%04x, version=0x%04x\n",
+ device->identifier.bus, device->identifier.vendor,
+ device->identifier.product, device->identifier.version);
dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
- device->keyMap.keyLayoutFile.c_str());
+ device->keyMap.keyLayoutFile.c_str());
dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
- device->keyMap.keyCharacterMapFile.c_str());
+ device->keyMap.keyCharacterMapFile.c_str());
dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
- device->configurationFile.c_str());
+ device->configurationFile.c_str());
dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
- toString(device->overlayKeyMap != nullptr));
+ toString(device->overlayKeyMap != nullptr));
dump += INDENT3 "VideoDevice: ";
if (device->videoDevice) {
dump += device->videoDevice->dump() + "\n";
@@ -1945,5 +1984,4 @@
mLock.unlock();
}
-
}; // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
new file mode 100644
index 0000000..7fed61f
--- /dev/null
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputDevice.h"
+
+#include "InputMapper.h"
+
+namespace android {
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+ int32_t controllerNumber, const InputDeviceIdentifier& identifier,
+ uint32_t classes)
+ : mContext(context),
+ mId(id),
+ mGeneration(generation),
+ mControllerNumber(controllerNumber),
+ mIdentifier(identifier),
+ mClasses(classes),
+ mSources(0),
+ mIsExternal(false),
+ mHasMic(false),
+ mDropUntilNextSync(false) {}
+
+InputDevice::~InputDevice() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ delete mMappers[i];
+ }
+ mMappers.clear();
+}
+
+bool InputDevice::isEnabled() {
+ return getEventHub()->isDeviceEnabled(mId);
+}
+
+void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+ if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+ ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+ "but the corresponding viewport is not found",
+ getName().c_str(), *mAssociatedDisplayPort);
+ enabled = false;
+ }
+
+ if (isEnabled() == enabled) {
+ return;
+ }
+
+ if (enabled) {
+ getEventHub()->enableDevice(mId);
+ reset(when);
+ } else {
+ reset(when);
+ getEventHub()->disableDevice(mId);
+ }
+ // Must change generation to flag this device as changed
+ bumpGeneration();
+}
+
+void InputDevice::dump(std::string& dump) {
+ InputDeviceInfo deviceInfo;
+ getDeviceInfo(&deviceInfo);
+
+ dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
+ deviceInfo.getDisplayName().c_str());
+ dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
+ dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+ dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+ if (mAssociatedDisplayPort) {
+ dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+ } else {
+ dump += "<none>\n";
+ }
+ dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
+ dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+ dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+
+ const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
+ if (!ranges.empty()) {
+ dump += INDENT2 "Motion Ranges:\n";
+ for (size_t i = 0; i < ranges.size(); i++) {
+ const InputDeviceInfo::MotionRange& range = ranges[i];
+ const char* label = getAxisLabel(range.axis);
+ char name[32];
+ if (label) {
+ strncpy(name, label, sizeof(name));
+ name[sizeof(name) - 1] = '\0';
+ } else {
+ snprintf(name, sizeof(name), "%d", range.axis);
+ }
+ dump += StringPrintf(INDENT3
+ "%s: source=0x%08x, "
+ "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
+ name, range.source, range.min, range.max, range.flat, range.fuzz,
+ range.resolution);
+ }
+ }
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->dump(dump);
+ }
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+ mMappers.push_back(mapper);
+}
+
+void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ mSources = 0;
+
+ if (!isIgnored()) {
+ if (!changes) { // first time only
+ mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
+ if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+ sp<KeyCharacterMap> keyboardLayout =
+ mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
+ if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
+ bumpGeneration();
+ }
+ }
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
+ if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+ std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
+ if (mAlias != alias) {
+ mAlias = alias;
+ bumpGeneration();
+ }
+ }
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
+ auto it = config->disabledDevices.find(mId);
+ bool enabled = it == config->disabledDevices.end();
+ setEnabled(enabled, when);
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ // In most situations, no port will be specified.
+ mAssociatedDisplayPort = std::nullopt;
+ mAssociatedViewport = std::nullopt;
+ // Find the display port that corresponds to the current input port.
+ const std::string& inputPort = mIdentifier.location;
+ if (!inputPort.empty()) {
+ const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+ const auto& displayPort = ports.find(inputPort);
+ if (displayPort != ports.end()) {
+ mAssociatedDisplayPort = std::make_optional(displayPort->second);
+ }
+ }
+
+ // If the device was explicitly disabled by the user, it would be present in the
+ // "disabledDevices" list. If it is associated with a specific display, and it was not
+ // explicitly disabled, then enable/disable the device based on whether we can find the
+ // corresponding viewport.
+ bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
+ if (mAssociatedDisplayPort) {
+ mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
+ if (!mAssociatedViewport) {
+ ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+ "but the corresponding viewport is not found.",
+ getName().c_str(), *mAssociatedDisplayPort);
+ enabled = false;
+ }
+ }
+
+ if (changes) {
+ // For first-time configuration, only allow device to be disabled after mappers have
+ // finished configuring. This is because we need to read some of the properties from
+ // the device's open fd.
+ setEnabled(enabled, when);
+ }
+ }
+
+ for (InputMapper* mapper : mMappers) {
+ mapper->configure(when, config, changes);
+ mSources |= mapper->getSources();
+ }
+
+ // If a device is just plugged but it might be disabled, we need to update some info like
+ // axis range of touch from each InputMapper first, then disable it.
+ if (!changes) {
+ setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+ }
+ }
+}
+
+void InputDevice::reset(nsecs_t when) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->reset(when);
+ }
+
+ mContext->updateGlobalMetaState();
+
+ notifyReset(when);
+}
+
+void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+ // Process all of the events in order for each mapper.
+ // We cannot simply ask each mapper to process them in bulk because mappers may
+ // have side-effects that must be interleaved. For example, joystick movement events and
+ // gamepad button presses are handled by different mappers but they should be dispatched
+ // in the order received.
+ for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
+#if DEBUG_RAW_EVENTS
+ ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
+ rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
+#endif
+
+ if (mDropUntilNextSync) {
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ mDropUntilNextSync = false;
+#if DEBUG_RAW_EVENTS
+ ALOGD("Recovered from input event buffer overrun.");
+#endif
+ } else {
+#if DEBUG_RAW_EVENTS
+ ALOGD("Dropped input event while waiting for next input sync.");
+#endif
+ }
+ } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
+ ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
+ mDropUntilNextSync = true;
+ reset(rawEvent->when);
+ } else {
+ for (InputMapper* mapper : mMappers) {
+ mapper->process(rawEvent);
+ }
+ }
+ --count;
+ }
+}
+
+void InputDevice::timeoutExpired(nsecs_t when) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->timeoutExpired(when);
+ }
+}
+
+void InputDevice::updateExternalStylusState(const StylusState& state) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->updateExternalStylusState(state);
+ }
+}
+
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+ outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
+ mHasMic);
+ for (InputMapper* mapper : mMappers) {
+ mapper->populateDeviceInfo(outDeviceInfo);
+ }
+}
+
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState);
+}
+
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getState(sourceMask, scanCode, &InputMapper::getScanCodeState);
+}
+
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getState(sourceMask, switchCode, &InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+ int32_t result = AKEY_STATE_UNKNOWN;
+ for (InputMapper* mapper : mMappers) {
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+ // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
+ int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
+ if (currentResult >= AKEY_STATE_DOWN) {
+ return currentResult;
+ } else if (currentResult == AKEY_STATE_UP) {
+ result = currentResult;
+ }
+ }
+ }
+ return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ bool result = false;
+ for (InputMapper* mapper : mMappers) {
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+ }
+ }
+ return result;
+}
+
+void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputDevice::cancelVibrate(int32_t token) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->cancelVibrate(token);
+ }
+}
+
+void InputDevice::cancelTouch(nsecs_t when) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->cancelTouch(when);
+ }
+}
+
+int32_t InputDevice::getMetaState() {
+ int32_t result = 0;
+ for (InputMapper* mapper : mMappers) {
+ result |= mapper->getMetaState();
+ }
+ return result;
+}
+
+void InputDevice::updateMetaState(int32_t keyCode) {
+ for (InputMapper* mapper : mMappers) {
+ mapper->updateMetaState(keyCode);
+ }
+}
+
+void InputDevice::fadePointer() {
+ for (InputMapper* mapper : mMappers) {
+ mapper->fadePointer();
+ }
+}
+
+void InputDevice::bumpGeneration() {
+ mGeneration = mContext->bumpGeneration();
+}
+
+void InputDevice::notifyReset(nsecs_t when) {
+ NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
+ mContext->getListener()->notifyDeviceReset(&args);
+}
+
+std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+ // Check if we had associated to the specific display.
+ if (mAssociatedViewport) {
+ return mAssociatedViewport->displayId;
+ }
+
+ // No associated display port, check if some InputMapper is associated.
+ for (InputMapper* mapper : mMappers) {
+ std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
+ if (associatedDisplayId) {
+ return associatedDisplayId;
+ }
+ }
+
+ return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
new file mode 100644
index 0000000..e57604c
--- /dev/null
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputReader.h"
+
+#include "CursorInputMapper.h"
+#include "ExternalStylusInputMapper.h"
+#include "InputReaderContext.h"
+#include "JoystickInputMapper.h"
+#include "KeyboardInputMapper.h"
+#include "MultiTouchInputMapper.h"
+#include "RotaryEncoderInputMapper.h"
+#include "SingleTouchInputMapper.h"
+#include "SwitchInputMapper.h"
+#include "VibratorInputMapper.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
+
+
+using android::base::StringPrintf;
+
+namespace android {
+
+InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener)
+ : mContext(this),
+ mEventHub(eventHub),
+ mPolicy(policy),
+ mNextSequenceNum(1),
+ mGlobalMetaState(0),
+ mGeneration(1),
+ mDisableVirtualKeysTimeout(LLONG_MIN),
+ mNextTimeout(LLONG_MAX),
+ mConfigurationChangesToRefresh(0) {
+ mQueuedListener = new QueuedInputListener(listener);
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ refreshConfigurationLocked(0);
+ updateGlobalMetaStateLocked();
+ } // release lock
+}
+
+InputReader::~InputReader() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+}
+
+void InputReader::loopOnce() {
+ int32_t oldGeneration;
+ int32_t timeoutMillis;
+ bool inputDevicesChanged = false;
+ std::vector<InputDeviceInfo> inputDevices;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ oldGeneration = mGeneration;
+ timeoutMillis = -1;
+
+ uint32_t changes = mConfigurationChangesToRefresh;
+ if (changes) {
+ mConfigurationChangesToRefresh = 0;
+ timeoutMillis = 0;
+ refreshConfigurationLocked(changes);
+ } else if (mNextTimeout != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
+ }
+ } // release lock
+
+ size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+ mReaderIsAliveCondition.broadcast();
+
+ if (count) {
+ processEventsLocked(mEventBuffer, count);
+ }
+
+ if (mNextTimeout != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now >= mNextTimeout) {
+#if DEBUG_RAW_EVENTS
+ ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+#endif
+ mNextTimeout = LLONG_MAX;
+ timeoutExpiredLocked(now);
+ }
+ }
+
+ if (oldGeneration != mGeneration) {
+ inputDevicesChanged = true;
+ getInputDevicesLocked(inputDevices);
+ }
+ } // release lock
+
+ // Send out a message that the describes the changed input devices.
+ if (inputDevicesChanged) {
+ mPolicy->notifyInputDevicesChanged(inputDevices);
+ }
+
+ // Flush queued events out to the listener.
+ // This must happen outside of the lock because the listener could potentially call
+ // back into the InputReader's methods, such as getScanCodeState, or become blocked
+ // on another thread similarly waiting to acquire the InputReader lock thereby
+ // resulting in a deadlock. This situation is actually quite plausible because the
+ // listener is actually the input dispatcher, which calls into the window manager,
+ // which occasionally calls into the input reader.
+ mQueuedListener->flush();
+}
+
+void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+ for (const RawEvent* rawEvent = rawEvents; count;) {
+ int32_t type = rawEvent->type;
+ size_t batchSize = 1;
+ if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+ int32_t deviceId = rawEvent->deviceId;
+ while (batchSize < count) {
+ if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
+ rawEvent[batchSize].deviceId != deviceId) {
+ break;
+ }
+ batchSize += 1;
+ }
+#if DEBUG_RAW_EVENTS
+ ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
+#endif
+ processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
+ } else {
+ switch (rawEvent->type) {
+ case EventHubInterface::DEVICE_ADDED:
+ addDeviceLocked(rawEvent->when, rawEvent->deviceId);
+ break;
+ case EventHubInterface::DEVICE_REMOVED:
+ removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
+ break;
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ handleConfigurationChangedLocked(rawEvent->when);
+ break;
+ default:
+ ALOG_ASSERT(false); // can't happen
+ break;
+ }
+ }
+ count -= batchSize;
+ rawEvent += batchSize;
+ }
+}
+
+void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+ return;
+ }
+
+ InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
+ uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+ int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
+
+ InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+ device->configure(when, &mConfig, 0);
+ device->reset(when);
+
+ if (device->isIgnored()) {
+ ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
+ identifier.name.c_str());
+ } else {
+ ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(),
+ device->getSources());
+ }
+
+ mDevices.add(deviceId, device);
+ bumpGenerationLocked();
+
+ if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ notifyExternalStylusPresenceChanged();
+ }
+}
+
+void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
+ InputDevice* device = nullptr;
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+ return;
+ }
+
+ device = mDevices.valueAt(deviceIndex);
+ mDevices.removeItemsAt(deviceIndex, 1);
+ bumpGenerationLocked();
+
+ if (device->isIgnored()) {
+ ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(),
+ device->getName().c_str());
+ } else {
+ ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(),
+ device->getName().c_str(), device->getSources());
+ }
+
+ if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ notifyExternalStylusPresenceChanged();
+ }
+
+ device->reset(when);
+ delete device;
+}
+
+InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
+ const InputDeviceIdentifier& identifier,
+ uint32_t classes) {
+ InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
+ controllerNumber, identifier, classes);
+
+ // External devices.
+ if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
+ device->setExternal(true);
+ }
+
+ // Devices with mics.
+ if (classes & INPUT_DEVICE_CLASS_MIC) {
+ device->setMic(true);
+ }
+
+ // Switch-like devices.
+ if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ device->addMapper(new SwitchInputMapper(device));
+ }
+
+ // Scroll wheel-like devices.
+ if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+ device->addMapper(new RotaryEncoderInputMapper(device));
+ }
+
+ // Vibrator-like devices.
+ if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+ device->addMapper(new VibratorInputMapper(device));
+ }
+
+ // Keyboard-like devices.
+ uint32_t keyboardSource = 0;
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+ if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+ }
+ if (classes & INPUT_DEVICE_CLASS_DPAD) {
+ keyboardSource |= AINPUT_SOURCE_DPAD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+ }
+
+ if (keyboardSource != 0) {
+ device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
+ }
+
+ // Cursor-like devices.
+ if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+ device->addMapper(new CursorInputMapper(device));
+ }
+
+ // Touchscreens and touchpad devices.
+ if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+ device->addMapper(new MultiTouchInputMapper(device));
+ } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+ device->addMapper(new SingleTouchInputMapper(device));
+ }
+
+ // Joystick-like devices.
+ if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+ device->addMapper(new JoystickInputMapper(device));
+ }
+
+ // External stylus-like devices.
+ if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ device->addMapper(new ExternalStylusInputMapper(device));
+ }
+
+ return device;
+}
+
+void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
+ size_t count) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Discarding event for unknown deviceId %d.", deviceId);
+ return;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
+ return;
+ }
+
+ device->process(rawEvents, count);
+}
+
+void InputReader::timeoutExpiredLocked(nsecs_t when) {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (!device->isIgnored()) {
+ device->timeoutExpired(when);
+ }
+ }
+}
+
+void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
+ // Reset global meta state because it depends on the list of all configured devices.
+ updateGlobalMetaStateLocked();
+
+ // Enqueue configuration changed.
+ NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
+ mQueuedListener->notifyConfigurationChanged(&args);
+}
+
+void InputReader::refreshConfigurationLocked(uint32_t changes) {
+ mPolicy->getReaderConfiguration(&mConfig);
+ mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
+
+ if (changes) {
+ ALOGI("Reconfiguring input devices, changes=%s",
+ InputReaderConfiguration::changesToString(changes).c_str());
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+ mEventHub->requestReopenDevices();
+ } else {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->configure(now, &mConfig, changes);
+ }
+ }
+ }
+}
+
+void InputReader::updateGlobalMetaStateLocked() {
+ mGlobalMetaState = 0;
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ mGlobalMetaState |= device->getMetaState();
+ }
+}
+
+int32_t InputReader::getGlobalMetaStateLocked() {
+ return mGlobalMetaState;
+}
+
+void InputReader::notifyExternalStylusPresenceChanged() {
+ refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+}
+
+void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ outDevices.push_back(info);
+ }
+ }
+}
+
+void InputReader::dispatchExternalStylusState(const StylusState& state) {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->updateExternalStylusState(state);
+ }
+}
+
+void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
+ mDisableVirtualKeysTimeout = time;
+}
+
+bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
+ int32_t scanCode) {
+ if (now < mDisableVirtualKeysTimeout) {
+ ALOGI("Dropping virtual key from device %s because virtual keys are "
+ "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d",
+ device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode,
+ scanCode);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void InputReader::fadePointerLocked() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->fadePointer();
+ }
+}
+
+void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
+ if (when < mNextTimeout) {
+ mNextTimeout = when;
+ mEventHub->wake();
+ }
+}
+
+int32_t InputReader::bumpGenerationLocked() {
+ return ++mGeneration;
+}
+
+void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
+ AutoMutex _l(mLock);
+ getInputDevicesLocked(outInputDevices);
+}
+
+void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
+ outInputDevices.clear();
+
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (!device->isIgnored()) {
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ outInputDevices.push_back(info);
+ }
+ }
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
+ AutoMutex _l(mLock);
+
+ return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
+ AutoMutex _l(mLock);
+
+ return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+ AutoMutex _l(mLock);
+
+ return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc) {
+ int32_t result = AKEY_STATE_UNKNOWN;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+ // value. Otherwise, return AKEY_STATE_UP as long as one device reports it.
+ int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
+ if (currentResult >= AKEY_STATE_DOWN) {
+ return currentResult;
+ } else if (currentResult == AKEY_STATE_UP) {
+ result = currentResult;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+void InputReader::toggleCapsLockState(int32_t deviceId) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
+ return;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ return;
+ }
+
+ device->updateMetaState(AKEYCODE_CAPS_LOCK);
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ AutoMutex _l(mLock);
+
+ memset(outFlags, 0, numCodes);
+ return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) {
+ bool result = false;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+ }
+ }
+ }
+ return result;
+}
+
+void InputReader::requestRefreshConfiguration(uint32_t changes) {
+ AutoMutex _l(mLock);
+
+ if (changes) {
+ bool needWake = !mConfigurationChangesToRefresh;
+ mConfigurationChangesToRefresh |= changes;
+
+ if (needWake) {
+ mEventHub->wake();
+ }
+ }
+}
+
+void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->cancelVibrate(token);
+ }
+}
+
+bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ return device->isEnabled();
+ }
+ ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+ return false;
+}
+
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+ return false;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (!device->isEnabled()) {
+ ALOGW("Ignoring disabled device %s", device->getName().c_str());
+ return false;
+ }
+
+ std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
+ // No associated display. By default, can dispatch to all displays.
+ if (!associatedDisplayId) {
+ return true;
+ }
+
+ if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+ ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
+ return true;
+ }
+
+ return *associatedDisplayId == displayId;
+}
+
+void InputReader::dump(std::string& dump) {
+ AutoMutex _l(mLock);
+
+ mEventHub->dump(dump);
+ dump += "\n";
+
+ dump += "Input Reader State:\n";
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ mDevices.valueAt(i)->dump(dump);
+ }
+
+ dump += INDENT "Configuration:\n";
+ dump += INDENT2 "ExcludedDeviceNames: [";
+ for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
+ if (i != 0) {
+ dump += ", ";
+ }
+ dump += mConfig.excludedDeviceNames[i];
+ }
+ dump += "]\n";
+ dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
+ mConfig.virtualKeyQuietTime * 0.000001f);
+
+ dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: "
+ "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, "
+ "acceleration=%0.3f\n",
+ mConfig.pointerVelocityControlParameters.scale,
+ mConfig.pointerVelocityControlParameters.lowThreshold,
+ mConfig.pointerVelocityControlParameters.highThreshold,
+ mConfig.pointerVelocityControlParameters.acceleration);
+
+ dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: "
+ "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, "
+ "acceleration=%0.3f\n",
+ mConfig.wheelVelocityControlParameters.scale,
+ mConfig.wheelVelocityControlParameters.lowThreshold,
+ mConfig.wheelVelocityControlParameters.highThreshold,
+ mConfig.wheelVelocityControlParameters.acceleration);
+
+ dump += StringPrintf(INDENT2 "PointerGesture:\n");
+ dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(mConfig.pointerGesturesEnabled));
+ dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n",
+ mConfig.pointerGestureQuietInterval * 0.000001f);
+ dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
+ mConfig.pointerGestureDragMinSwitchSpeed);
+ dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n",
+ mConfig.pointerGestureTapInterval * 0.000001f);
+ dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n",
+ mConfig.pointerGestureTapDragInterval * 0.000001f);
+ dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", mConfig.pointerGestureTapSlop);
+ dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
+ mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
+ dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+ mConfig.pointerGestureMultitouchMinDistance);
+ dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
+ mConfig.pointerGestureSwipeMaxWidthRatio);
+ dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n",
+ mConfig.pointerGestureMovementSpeedRatio);
+ dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", mConfig.pointerGestureZoomSpeedRatio);
+
+ dump += INDENT3 "Viewports:\n";
+ mConfig.dump(dump);
+}
+
+void InputReader::monitor() {
+ // Acquire and release the lock to ensure that the reader has not deadlocked.
+ mLock.lock();
+ mEventHub->wake();
+ mReaderIsAliveCondition.wait(mLock);
+ mLock.unlock();
+
+ // Check the EventHub
+ mEventHub->monitor();
+}
+
+// --- InputReader::ContextImpl ---
+
+InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {}
+
+void InputReader::ContextImpl::updateGlobalMetaState() {
+ // lock is already held by the input loop
+ mReader->updateGlobalMetaStateLocked();
+}
+
+int32_t InputReader::ContextImpl::getGlobalMetaState() {
+ // lock is already held by the input loop
+ return mReader->getGlobalMetaStateLocked();
+}
+
+void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
+ // lock is already held by the input loop
+ mReader->disableVirtualKeysUntilLocked(time);
+}
+
+bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device,
+ int32_t keyCode, int32_t scanCode) {
+ // lock is already held by the input loop
+ return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
+}
+
+void InputReader::ContextImpl::fadePointer() {
+ // lock is already held by the input loop
+ mReader->fadePointerLocked();
+}
+
+void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
+ // lock is already held by the input loop
+ mReader->requestTimeoutAtTimeLocked(when);
+}
+
+int32_t InputReader::ContextImpl::bumpGeneration() {
+ // lock is already held by the input loop
+ return mReader->bumpGenerationLocked();
+}
+
+void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
+ // lock is already held by whatever called refreshConfigurationLocked
+ mReader->getExternalStylusDevicesLocked(outDevices);
+}
+
+void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
+ mReader->dispatchExternalStylusState(state);
+}
+
+InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
+ return mReader->mPolicy.get();
+}
+
+InputListenerInterface* InputReader::ContextImpl::getListener() {
+ return mReader->mQueuedListener.get();
+}
+
+EventHubInterface* InputReader::ContextImpl::getEventHub() {
+ return mReader->mEventHub.get();
+}
+
+uint32_t InputReader::ContextImpl::getNextSequenceNum() {
+ return (mReader->mNextSequenceNum)++;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
similarity index 81%
rename from services/inputflinger/InputReaderFactory.cpp
rename to services/inputflinger/reader/InputReaderFactory.cpp
index 072499b..a897141 100644
--- a/services/inputflinger/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -15,13 +15,13 @@
*/
#include "InputReaderFactory.h"
+
#include "InputReader.h"
namespace android {
-sp<InputReaderInterface> createInputReader(
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) {
+sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener) {
return new InputReader(std::make_unique<EventHub>(), policy, listener);
}
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
new file mode 100644
index 0000000..827e31a
--- /dev/null
+++ b/services/inputflinger/reader/Macros.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_MACROS_H
+#define _UI_INPUTREADER_MACROS_H
+
+#define LOG_TAG "InputReader"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages for each raw event received from the EventHub.
+#define DEBUG_RAW_EVENTS 0
+
+// Log debug messages about touch screen filtering hacks.
+#define DEBUG_HACKS 0
+
+// Log debug messages about virtual key processing.
+#define DEBUG_VIRTUAL_KEYS 0
+
+// Log debug messages about pointers.
+#define DEBUG_POINTERS 0
+
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
+// Log debug messages about gesture detection.
+#define DEBUG_GESTURES 0
+
+// Log debug messages about the vibrator.
+#define DEBUG_VIBRATOR 0
+
+// Log debug messages about fusing stylus data.
+#define DEBUG_STYLUS_FUSION 0
+
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
+#define INDENT5 " "
+
+#include <input/Input.h>
+
+namespace android {
+
+// --- Static Functions ---
+
+template <typename T>
+inline static T abs(const T& value) {
+ return value < 0 ? -value : value;
+}
+
+template <typename T>
+inline static T min(const T& a, const T& b) {
+ return a < b ? a : b;
+}
+
+inline static float avg(float x, float y) {
+ return (x + y) / 2;
+}
+
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+ return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file
diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
similarity index 84%
rename from services/inputflinger/TouchVideoDevice.cpp
rename to services/inputflinger/reader/TouchVideoDevice.cpp
index 19c1313..c075078 100644
--- a/services/inputflinger/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -37,10 +37,13 @@
namespace android {
TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
- uint32_t height, uint32_t width,
- const std::array<const int16_t*, NUM_BUFFERS>& readLocations) :
- mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)),
- mHeight(height), mWidth(width),
+ uint32_t height, uint32_t width,
+ const std::array<const int16_t*, NUM_BUFFERS>& readLocations)
+ : mFd(fd),
+ mName(std::move(name)),
+ mPath(std::move(devicePath)),
+ mHeight(height),
+ mWidth(width),
mReadLocations(readLocations) {
mFrames.reserve(MAX_QUEUE_SIZE);
};
@@ -60,11 +63,11 @@
}
if (!(cap.capabilities & V4L2_CAP_TOUCH)) {
ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. "
- "Make sure device specifies V4L2_CAP_TOUCH");
+ "Make sure device specifies V4L2_CAP_TOUCH");
return nullptr;
}
- ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i",
- cap.driver, cap.card, cap.bus_info, cap.version);
+ ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", cap.driver,
+ cap.card, cap.bus_info, cap.version);
std::string name = reinterpret_cast<const char*>(cap.card);
struct v4l2_input v4l2_input_struct;
@@ -77,7 +80,7 @@
if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) {
ALOGE("Video device does not provide touch data. "
- "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
+ "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
return nullptr;
}
@@ -120,14 +123,14 @@
return nullptr;
}
if (buf.length != height * width * sizeof(int16_t)) {
- ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")",
- buf.length, buf.m.offset);
+ ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", buf.length,
+ buf.m.offset);
return nullptr;
}
- readLocations[i] = static_cast<const int16_t*>(mmap(nullptr /* start anywhere */,
- buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */,
- fd.get(), buf.m.offset));
+ readLocations[i] = static_cast<const int16_t*>(
+ mmap(nullptr /* start anywhere */, buf.length, PROT_READ /* required */,
+ MAP_SHARED /* recommended */, fd.get(), buf.m.offset));
if (readLocations[i] == MAP_FAILED) {
ALOGE("%s: map failed: %s", __func__, strerror(errno));
return nullptr;
@@ -150,8 +153,9 @@
}
}
// Using 'new' to access a non-public constructor.
- return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(
- fd.release(), std::move(name), std::move(devicePath), height, width, readLocations));
+ return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(fd.release(), std::move(name),
+ std::move(devicePath), height,
+ width, readLocations));
}
size_t TouchVideoDevice::readAndQueueFrames() {
@@ -163,10 +167,10 @@
}
// Concatenate the vectors, then clip up to maximum size allowed
mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
- std::make_move_iterator(frames.end()));
+ std::make_move_iterator(frames.end()));
if (mFrames.size() > MAX_QUEUE_SIZE) {
ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
- mFrames.size() - MAX_QUEUE_SIZE);
+ mFrames.size() - MAX_QUEUE_SIZE);
mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
}
return numFrames;
@@ -193,8 +197,8 @@
if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
// We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
// we can't compare timestamps. Just log a warning, since this is a driver issue
- ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC",
- buf.timestamp.tv_sec, buf.timestamp.tv_usec);
+ ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec,
+ buf.timestamp.tv_usec);
}
std::vector<int16_t> data(mHeight * mWidth);
const int16_t* readFrom = mReadLocations[buf.index];
@@ -233,7 +237,7 @@
}
for (const int16_t* buffer : mReadLocations) {
void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer));
- result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t));
+ result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t));
if (result == -1) {
ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno));
}
@@ -242,9 +246,9 @@
std::string TouchVideoDevice::dump() const {
return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32
- ", fd=%i, hasValidFd=%s",
- mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(),
- hasValidFd() ? "true" : "false");
+ ", fd=%i, hasValidFd=%s",
+ mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(),
+ hasValidFd() ? "true" : "false");
}
} // namespace android
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/reader/include/EventHub.h
similarity index 79%
rename from services/inputflinger/EventHub.h
rename to services/inputflinger/reader/include/EventHub.h
index 6c3a4a2..c17f3a1 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-//
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
@@ -22,17 +21,17 @@
#include <input/Input.h>
#include <input/InputDevice.h>
-#include <input/Keyboard.h>
-#include <input/KeyLayoutMap.h>
#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
-#include <utils/Mutex.h>
-#include <utils/Log.h>
-#include <utils/List.h>
-#include <utils/Errors.h>
-#include <utils/PropertyMap.h>
-#include <utils/KeyedVector.h>
#include <utils/BitSet.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/PropertyMap.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -41,8 +40,8 @@
/* Convenience constants. */
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f // last button code
+#define BTN_FIRST 0x100 // first button code
+#define BTN_LAST 0x15f // last button code
namespace android {
@@ -61,10 +60,10 @@
struct RawAbsoluteAxisInfo {
bool valid; // true if the information is valid, false otherwise
- int32_t minValue; // minimum value
- int32_t maxValue; // maximum value
- int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
- int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+ int32_t minValue; // minimum value
+ int32_t maxValue; // maximum value
+ int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
+ int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
int32_t resolution; // resolution in units per mm or radians per mm
inline void clear() {
@@ -82,37 +81,37 @@
*/
enum {
/* The input device is a keyboard or has buttons. */
- INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
+ INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
- INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
+ INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
+ INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
- INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
+ INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
- INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
+ INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+ INPUT_DEVICE_CLASS_DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+ INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
/* The input device has switches. */
- INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+ INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+ INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
/* The input device has a vibrator (supports FF_RUMBLE). */
- INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+ INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
/* The input device has a microphone. */
- INPUT_DEVICE_CLASS_MIC = 0x00000400,
+ INPUT_DEVICE_CLASS_MIC = 0x00000400,
/* The input device is an external stylus (has data we want to fuse with touch data). */
INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800,
@@ -121,10 +120,10 @@
INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000,
/* The input device is virtual (not a real device, not part of UI configuration). */
- INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
+ INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
/* The input device is external (not built-in). */
- INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
+ INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
};
/*
@@ -148,8 +147,8 @@
*/
class EventHubInterface {
public:
- EventHubInterface() { }
- virtual ~EventHubInterface() { }
+ EventHubInterface() {}
+ virtual ~EventHubInterface() {}
// Synthetic raw event type codes produced when devices are added or removed.
enum {
@@ -173,18 +172,17 @@
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const = 0;
+ RawAbsoluteAxisInfo* outAxisInfo) const = 0;
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
- virtual status_t mapKey(int32_t deviceId,
- int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const = 0;
+ virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+ int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const = 0;
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const = 0;
+ virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const = 0;
// Sets devices that are excluded from opening.
// This can be used to ignore input devices for sensors.
@@ -212,13 +210,13 @@
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const = 0;
+ int32_t* outValue) const = 0;
/*
* Examine key input devices for specific framework keycode support
*/
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const = 0;
+ uint8_t* outFlags) const = 0;
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
@@ -226,8 +224,8 @@
virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
+ virtual void getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
@@ -258,67 +256,68 @@
virtual status_t disableDevice(int32_t deviceId) = 0;
};
-class EventHub : public EventHubInterface
-{
+class EventHub : public EventHubInterface {
public:
EventHub();
- virtual uint32_t getDeviceClasses(int32_t deviceId) const;
+ virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
+ virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const;
+ virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+ virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const;
+ RawAbsoluteAxisInfo* outAxisInfo) const override;
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
+ virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
- virtual bool hasInputProperty(int32_t deviceId, int property) const;
+ virtual bool hasInputProperty(int32_t deviceId, int property) const override;
- virtual status_t mapKey(int32_t deviceId,
- int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const;
+ virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+ int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const override;
virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const;
+ AxisInfo* outAxisInfo) const override;
- virtual void setExcludedDevices(const std::vector<std::string>& devices);
+ virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+ virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const override;
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) const;
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const override;
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
- virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
+ virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
+ virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
- virtual bool hasLed(int32_t deviceId, int32_t led) const;
- virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+ virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+ virtual bool hasLed(int32_t deviceId, int32_t led) const override;
+ virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- std::vector<VirtualKeyDefinition>& outVirtualKeys) const;
+ virtual void getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
+ virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+ const sp<KeyCharacterMap>& map) override;
- virtual void vibrate(int32_t deviceId, nsecs_t duration);
- virtual void cancelVibrate(int32_t deviceId);
+ virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
+ virtual void cancelVibrate(int32_t deviceId) override;
- virtual void requestReopenDevices();
+ virtual void requestReopenDevices() override;
- virtual void wake();
+ virtual void wake() override;
- virtual void dump(std::string& dump);
- virtual void monitor();
+ virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
- virtual ~EventHub();
+ virtual ~EventHub() override;
private:
struct Device {
@@ -355,7 +354,7 @@
int32_t controllerNumber;
Device(int fd, int32_t id, const std::string& path,
- const InputDeviceIdentifier& identifier);
+ const InputDeviceIdentifier& identifier);
~Device();
void close();
@@ -380,16 +379,16 @@
void addDeviceLocked(Device* device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
- void closeDeviceByPathLocked(const char *devicePath);
+ void closeDeviceByPathLocked(const char* devicePath);
void closeVideoDeviceByPathLocked(const std::string& devicePath);
void closeDeviceLocked(Device* device);
void closeAllDevicesLocked();
void configureFd(Device* device);
- bool isDeviceEnabled(int32_t deviceId);
- status_t enableDevice(int32_t deviceId);
- status_t disableDevice(int32_t deviceId);
+ bool isDeviceEnabled(int32_t deviceId) override;
+ status_t enableDevice(int32_t deviceId) override;
+ status_t disableDevice(int32_t deviceId) override;
status_t registerFdForEpoll(int fd);
status_t unregisterFdFromEpoll(int fd);
status_t registerDeviceForEpollLocked(Device* device);
@@ -397,7 +396,7 @@
status_t unregisterDeviceFromEpollLocked(Device* device);
void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
- status_t scanDirLocked(const char *dirname);
+ status_t scanDirLocked(const char* dirname);
status_t scanVideoDirLocked(const std::string& dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
@@ -453,8 +452,8 @@
*/
std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
- Device *mOpeningDevices;
- Device *mClosingDevices;
+ Device* mOpeningDevices;
+ Device* mClosingDevices;
bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
new file mode 100644
index 0000000..882407d
--- /dev/null
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_INPUT_DEVICE_H
+#define _UI_INPUTREADER_INPUT_DEVICE_H
+
+#include "EventHub.h"
+#include "InputReaderBase.h"
+#include "InputReaderContext.h"
+
+#include <input/DisplayViewport.h>
+#include <input/InputDevice.h>
+#include <utils/PropertyMap.h>
+
+#include <stdint.h>
+#include <optional>
+#include <vector>
+
+namespace android {
+
+class InputMapper;
+
+/* Represents the state of a single input device. */
+class InputDevice {
+public:
+ InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+ int32_t controllerNumber, const InputDeviceIdentifier& identifier,
+ uint32_t classes);
+ ~InputDevice();
+
+ inline InputReaderContext* getContext() { return mContext; }
+ inline int32_t getId() const { return mId; }
+ inline int32_t getControllerNumber() const { return mControllerNumber; }
+ inline int32_t getGeneration() const { return mGeneration; }
+ inline const std::string getName() const { return mIdentifier.name; }
+ inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+ inline uint32_t getClasses() const { return mClasses; }
+ inline uint32_t getSources() const { return mSources; }
+
+ inline bool isExternal() { return mIsExternal; }
+ inline void setExternal(bool external) { mIsExternal = external; }
+ inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+ return mAssociatedDisplayPort;
+ }
+ inline std::optional<DisplayViewport> getAssociatedViewport() const {
+ return mAssociatedViewport;
+ }
+ inline void setMic(bool hasMic) { mHasMic = hasMic; }
+ inline bool hasMic() const { return mHasMic; }
+
+ inline bool isIgnored() { return mMappers.empty(); }
+
+ bool isEnabled();
+ void setEnabled(bool enabled, nsecs_t when);
+
+ void dump(std::string& dump);
+ void addMapper(InputMapper* mapper);
+ void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
+ void reset(nsecs_t when);
+ void process(const RawEvent* rawEvents, size_t count);
+ void timeoutExpired(nsecs_t when);
+ void updateExternalStylusState(const StylusState& state);
+
+ void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags);
+ void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void cancelVibrate(int32_t token);
+ void cancelTouch(nsecs_t when);
+
+ int32_t getMetaState();
+ void updateMetaState(int32_t keyCode);
+
+ void fadePointer();
+
+ void bumpGeneration();
+
+ void notifyReset(nsecs_t when);
+
+ inline const PropertyMap& getConfiguration() { return mConfiguration; }
+ inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+ bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); }
+
+ bool hasAbsoluteAxis(int32_t code) {
+ RawAbsoluteAxisInfo info;
+ getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
+ return info.valid;
+ }
+
+ bool isKeyPressed(int32_t code) {
+ return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+ }
+
+ int32_t getAbsoluteAxisValue(int32_t code) {
+ int32_t value;
+ getEventHub()->getAbsoluteAxisValue(mId, code, &value);
+ return value;
+ }
+
+ std::optional<int32_t> getAssociatedDisplayId();
+
+private:
+ InputReaderContext* mContext;
+ int32_t mId;
+ int32_t mGeneration;
+ int32_t mControllerNumber;
+ InputDeviceIdentifier mIdentifier;
+ std::string mAlias;
+ uint32_t mClasses;
+
+ std::vector<InputMapper*> mMappers;
+
+ uint32_t mSources;
+ bool mIsExternal;
+ std::optional<uint8_t> mAssociatedDisplayPort;
+ std::optional<DisplayViewport> mAssociatedViewport;
+ bool mHasMic;
+ bool mDropUntilNextSync;
+
+ typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
+
+ PropertyMap mConfiguration;
+};
+
+} // namespace android
+
+#endif //_UI_INPUTREADER_INPUT_DEVICE_H
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
new file mode 100644
index 0000000..7b4321e
--- /dev/null
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 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 _UI_INPUTREADER_INPUT_READER_H
+#define _UI_INPUTREADER_INPUT_READER_H
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderBase.h"
+#include "InputReaderContext.h"
+
+#include <utils/Condition.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+
+#include <vector>
+
+namespace android {
+
+class InputDevice;
+class InputMapper;
+struct StylusState;
+
+/* The input reader reads raw event data from the event hub and processes it into input events
+ * that it sends to the input listener. Some functions of the input reader, such as early
+ * event filtering in low power states, are controlled by a separate policy object.
+ *
+ * The InputReader owns a collection of InputMappers. Most of the work it does happens
+ * on the input reader thread but the InputReader can receive queries from other system
+ * components running on arbitrary threads. To keep things manageable, the InputReader
+ * uses a single Mutex to guard its state. The Mutex may be held while calling into the
+ * EventHub or the InputReaderPolicy but it is never held while calling into the
+ * InputListener.
+ */
+class InputReader : public InputReaderInterface {
+public:
+ InputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener);
+ virtual ~InputReader();
+
+ virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
+
+ virtual void loopOnce() override;
+
+ virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+
+ virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) override;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) override;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+
+ virtual void toggleCapsLockState(int32_t deviceId) override;
+
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) override;
+
+ virtual void requestRefreshConfiguration(uint32_t changes) override;
+
+ virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) override;
+ virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+
+protected:
+ // These members are protected so they can be instrumented by test cases.
+ virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
+ const InputDeviceIdentifier& identifier,
+ uint32_t classes);
+
+ class ContextImpl : public InputReaderContext {
+ InputReader* mReader;
+
+ public:
+ explicit ContextImpl(InputReader* reader);
+
+ virtual void updateGlobalMetaState() override;
+ virtual int32_t getGlobalMetaState() override;
+ virtual void disableVirtualKeysUntil(nsecs_t time) override;
+ virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
+ int32_t scanCode) override;
+ virtual void fadePointer() override;
+ virtual void requestTimeoutAtTime(nsecs_t when) override;
+ virtual int32_t bumpGeneration() override;
+ virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+ virtual void dispatchExternalStylusState(const StylusState& outState) override;
+ virtual InputReaderPolicyInterface* getPolicy() override;
+ virtual InputListenerInterface* getListener() override;
+ virtual EventHubInterface* getEventHub() override;
+ virtual uint32_t getNextSequenceNum() override;
+ } mContext;
+
+ friend class ContextImpl;
+
+private:
+ Mutex mLock;
+
+ Condition mReaderIsAliveCondition;
+
+ // This could be unique_ptr, but due to the way InputReader tests are written,
+ // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
+ // in parallel to passing it to the InputReader.
+ std::shared_ptr<EventHubInterface> mEventHub;
+ sp<InputReaderPolicyInterface> mPolicy;
+ sp<QueuedInputListener> mQueuedListener;
+
+ InputReaderConfiguration mConfig;
+
+ // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
+ uint32_t mNextSequenceNum;
+
+ // The event queue.
+ static const int EVENT_BUFFER_SIZE = 256;
+ RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
+
+ KeyedVector<int32_t, InputDevice*> mDevices;
+
+ // low-level input event decoding and device management
+ void processEventsLocked(const RawEvent* rawEvents, size_t count);
+
+ void addDeviceLocked(nsecs_t when, int32_t deviceId);
+ void removeDeviceLocked(nsecs_t when, int32_t deviceId);
+ void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+ void timeoutExpiredLocked(nsecs_t when);
+
+ void handleConfigurationChangedLocked(nsecs_t when);
+
+ int32_t mGlobalMetaState;
+ void updateGlobalMetaStateLocked();
+ int32_t getGlobalMetaStateLocked();
+
+ void notifyExternalStylusPresenceChanged();
+ void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
+ void dispatchExternalStylusState(const StylusState& state);
+
+ void fadePointerLocked();
+
+ int32_t mGeneration;
+ int32_t bumpGenerationLocked();
+
+ void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
+
+ nsecs_t mDisableVirtualKeysTimeout;
+ void disableVirtualKeysUntilLocked(nsecs_t time);
+ bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
+ int32_t scanCode);
+
+ nsecs_t mNextTimeout;
+ void requestTimeoutAtTimeLocked(nsecs_t when);
+
+ uint32_t mConfigurationChangesToRefresh;
+ void refreshConfigurationLocked(uint32_t changes);
+
+ // state queries
+ typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc);
+ bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_READER_H
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
new file mode 100644
index 0000000..3472346
--- /dev/null
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+
+#include <input/InputDevice.h>
+
+#include <vector>
+
+namespace android {
+
+class EventHubInterface;
+class InputDevice;
+class InputListenerInterface;
+class InputMapper;
+class InputReaderPolicyInterface;
+struct StylusState;
+
+/* Internal interface used by individual input devices to access global input device state
+ * and parameters maintained by the input reader.
+ */
+class InputReaderContext {
+public:
+ InputReaderContext() {}
+ virtual ~InputReaderContext() {}
+
+ virtual void updateGlobalMetaState() = 0;
+ virtual int32_t getGlobalMetaState() = 0;
+
+ virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
+ virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
+ int32_t scanCode) = 0;
+
+ virtual void fadePointer() = 0;
+
+ virtual void requestTimeoutAtTime(nsecs_t when) = 0;
+ virtual int32_t bumpGeneration() = 0;
+
+ virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
+ virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
+
+ virtual InputReaderPolicyInterface* getPolicy() = 0;
+ virtual InputListenerInterface* getListener() = 0;
+ virtual EventHubInterface* getEventHub() = 0;
+
+ virtual uint32_t getNextSequenceNum() = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
new file mode 100644
index 0000000..17f158c
--- /dev/null
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_STYLUS_STATE_H
+#define _UI_INPUTREADER_STYLUS_STATE_H
+
+#include <input/Input.h>
+
+#include <limits.h>
+
+namespace android {
+
+struct StylusState {
+ /* Time the stylus event was received. */
+ nsecs_t when;
+ /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
+ float pressure;
+ /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
+ uint32_t buttons;
+ /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
+ int32_t toolType;
+
+ void copyFrom(const StylusState& other) {
+ when = other.when;
+ pressure = other.pressure;
+ buttons = other.buttons;
+ toolType = other.toolType;
+ }
+
+ void clear() {
+ when = LLONG_MAX;
+ pressure = 0.f;
+ buttons = 0;
+ toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+ }
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_STYLUS_STATE_H
diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
similarity index 92%
rename from services/inputflinger/TouchVideoDevice.h
rename to services/inputflinger/reader/include/TouchVideoDevice.h
index 0e7e2ef..5a32443 100644
--- a/services/inputflinger/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
-#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
-#include <array>
#include <android-base/unique_fd.h>
#include <input/TouchVideoFrame.h>
-#include <optional>
#include <stdint.h>
+#include <array>
+#include <optional>
#include <string>
#include <vector>
@@ -109,9 +109,9 @@
* The constructor is private because opening a v4l2 device requires many checks.
* To get a new TouchVideoDevice, use 'create' instead.
*/
- explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
- uint32_t height, uint32_t width,
- const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
+ explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, uint32_t height,
+ uint32_t width,
+ const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
/**
* Read all currently available frames.
*/
@@ -121,5 +121,7 @@
*/
std::optional<TouchVideoFrame> readFrame();
};
+
} // namespace android
-#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+
+#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
new file mode 100644
index 0000000..f69138e
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "CursorInputMapper.h"
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "TouchCursorInputMapperCommon.h"
+
+namespace android {
+
+// --- CursorMotionAccumulator ---
+
+CursorMotionAccumulator::CursorMotionAccumulator() {
+ clearRelativeAxes();
+}
+
+void CursorMotionAccumulator::reset(InputDevice* device) {
+ clearRelativeAxes();
+}
+
+void CursorMotionAccumulator::clearRelativeAxes() {
+ mRelX = 0;
+ mRelY = 0;
+}
+
+void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_REL) {
+ switch (rawEvent->code) {
+ case REL_X:
+ mRelX = rawEvent->value;
+ break;
+ case REL_Y:
+ mRelY = rawEvent->value;
+ break;
+ }
+ }
+}
+
+void CursorMotionAccumulator::finishSync() {
+ clearRelativeAxes();
+}
+
+// --- CursorInputMapper ---
+
+CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {}
+
+CursorInputMapper::~CursorInputMapper() {}
+
+uint32_t CursorInputMapper::getSources() {
+ return mSource;
+}
+
+void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ float minX, minY, maxX, maxY;
+ if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
+ }
+ } else {
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+ }
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+
+ if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+ }
+ if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+ }
+}
+
+void CursorInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Cursor Input Mapper:\n";
+ dumpParameters(dump);
+ dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
+ dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
+ dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+ dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+ dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
+ toString(mCursorScrollAccumulator.haveRelativeVWheel()));
+ dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
+ toString(mCursorScrollAccumulator.haveRelativeHWheel()));
+ dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
+ dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+ dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
+ dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
+ dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
+ dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
+}
+
+void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+
+ if (!changes) { // first time only
+ mCursorScrollAccumulator.configure(getDevice());
+
+ // Configure basic parameters.
+ configureParameters();
+
+ // Configure device mode.
+ switch (mParameters.mode) {
+ case Parameters::MODE_POINTER_RELATIVE:
+ // Should not happen during first time configuration.
+ ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
+ mParameters.mode = Parameters::MODE_POINTER;
+ [[fallthrough]];
+ case Parameters::MODE_POINTER:
+ mSource = AINPUT_SOURCE_MOUSE;
+ mXPrecision = 1.0f;
+ mYPrecision = 1.0f;
+ mXScale = 1.0f;
+ mYScale = 1.0f;
+ mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+ break;
+ case Parameters::MODE_NAVIGATION:
+ mSource = AINPUT_SOURCE_TRACKBALL;
+ mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ break;
+ }
+
+ mVWheelScale = 1.0f;
+ mHWheelScale = 1.0f;
+ }
+
+ if ((!changes && config->pointerCapture) ||
+ (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
+ if (config->pointerCapture) {
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+ mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
+ // Keep PointerController around in order to preserve the pointer position.
+ mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ } else {
+ ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
+ }
+ } else {
+ if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
+ mParameters.mode = Parameters::MODE_POINTER;
+ mSource = AINPUT_SOURCE_MOUSE;
+ } else {
+ ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
+ }
+ }
+ bumpGeneration();
+ if (changes) {
+ getDevice()->notifyReset(when);
+ }
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+ mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
+ mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
+ mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mOrientation = DISPLAY_ORIENTATION_0;
+ if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
+ }
+ }
+
+ // Update the PointerController if viewports changed.
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ getPolicy()->obtainPointerController(getDeviceId());
+ }
+ bumpGeneration();
+ }
+}
+
+void CursorInputMapper::configureParameters() {
+ mParameters.mode = Parameters::MODE_POINTER;
+ String8 cursorModeString;
+ if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
+ if (cursorModeString == "navigation") {
+ mParameters.mode = Parameters::MODE_NAVIGATION;
+ } else if (cursorModeString != "pointer" && cursorModeString != "default") {
+ ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
+ }
+ }
+
+ mParameters.orientationAware = false;
+ getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+ mParameters.orientationAware);
+
+ mParameters.hasAssociatedDisplay = false;
+ if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+ mParameters.hasAssociatedDisplay = true;
+ }
+}
+
+void CursorInputMapper::dumpParameters(std::string& dump) {
+ dump += INDENT3 "Parameters:\n";
+ dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
+ toString(mParameters.hasAssociatedDisplay));
+
+ switch (mParameters.mode) {
+ case Parameters::MODE_POINTER:
+ dump += INDENT4 "Mode: pointer\n";
+ break;
+ case Parameters::MODE_POINTER_RELATIVE:
+ dump += INDENT4 "Mode: relative pointer\n";
+ break;
+ case Parameters::MODE_NAVIGATION:
+ dump += INDENT4 "Mode: navigation\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+}
+
+void CursorInputMapper::reset(nsecs_t when) {
+ mButtonState = 0;
+ mDownTime = 0;
+
+ mPointerVelocityControl.reset();
+ mWheelXVelocityControl.reset();
+ mWheelYVelocityControl.reset();
+
+ mCursorButtonAccumulator.reset(getDevice());
+ mCursorMotionAccumulator.reset(getDevice());
+ mCursorScrollAccumulator.reset(getDevice());
+
+ InputMapper::reset(when);
+}
+
+void CursorInputMapper::process(const RawEvent* rawEvent) {
+ mCursorButtonAccumulator.process(rawEvent);
+ mCursorMotionAccumulator.process(rawEvent);
+ mCursorScrollAccumulator.process(rawEvent);
+
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ sync(rawEvent->when);
+ }
+}
+
+void CursorInputMapper::sync(nsecs_t when) {
+ int32_t lastButtonState = mButtonState;
+ int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
+ mButtonState = currentButtonState;
+
+ bool wasDown = isPointerDown(lastButtonState);
+ bool down = isPointerDown(currentButtonState);
+ bool downChanged;
+ if (!wasDown && down) {
+ mDownTime = when;
+ downChanged = true;
+ } else if (wasDown && !down) {
+ downChanged = true;
+ } else {
+ downChanged = false;
+ }
+ nsecs_t downTime = mDownTime;
+ bool buttonsChanged = currentButtonState != lastButtonState;
+ int32_t buttonsPressed = currentButtonState & ~lastButtonState;
+ int32_t buttonsReleased = lastButtonState & ~currentButtonState;
+
+ float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
+ float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
+ bool moved = deltaX != 0 || deltaY != 0;
+
+ // Rotate delta according to orientation if needed.
+ if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
+ (deltaX != 0.0f || deltaY != 0.0f)) {
+ rotateDelta(mOrientation, &deltaX, &deltaY);
+ }
+
+ // Move the pointer.
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
+
+ float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
+ float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
+ bool scrolled = vscroll != 0 || hscroll != 0;
+
+ mWheelYVelocityControl.move(when, nullptr, &vscroll);
+ mWheelXVelocityControl.move(when, &hscroll, nullptr);
+
+ mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+ int32_t displayId;
+ float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+ float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+ if (mSource == AINPUT_SOURCE_MOUSE) {
+ if (moved || scrolled || buttonsChanged) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+
+ if (moved) {
+ mPointerController->move(deltaX, deltaY);
+ }
+
+ if (buttonsChanged) {
+ mPointerController->setButtonState(currentButtonState);
+ }
+
+ mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ }
+
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
+ displayId = mPointerController->getDisplayId();
+ } else {
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+ displayId = ADISPLAY_ID_NONE;
+ }
+
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+ // Moving an external trackball or mouse should wake the device.
+ // We don't do this for internal cursor devices to prevent them from waking up
+ // the device in your pocket.
+ // TODO: Use the input device configuration to control this behavior more finely.
+ uint32_t policyFlags = 0;
+ if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+
+ // Synthesize key down from buttons if needed.
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
+ displayId, policyFlags, lastButtonState, currentButtonState);
+
+ // Send motion event.
+ if (downChanged || moved || scrolled || buttonsChanged) {
+ int32_t metaState = mContext->getGlobalMetaState();
+ int32_t buttonState = lastButtonState;
+ int32_t motionEventAction;
+ if (downChanged) {
+ motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
+ motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
+ }
+
+ if (buttonsReleased) {
+ BitSet32 released(buttonsReleased);
+ while (!released.isEmpty()) {
+ int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
+ buttonState &= ~actionButton;
+ NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&releaseArgs);
+ }
+ }
+
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, motionEventAction, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+
+ if (buttonsPressed) {
+ BitSet32 pressed(buttonsPressed);
+ while (!pressed.isEmpty()) {
+ int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
+ buttonState |= actionButton;
+ NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&pressArgs);
+ }
+ }
+
+ ALOG_ASSERT(buttonState == currentButtonState);
+
+ // Send hover move after UP to tell the application that the mouse is hovering now.
+ if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
+ NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+ 0, metaState, currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+ yCursorPosition, downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&hoverArgs);
+ }
+
+ // Send scroll events.
+ if (scrolled) {
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+ NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+ yCursorPosition, downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&scrollArgs);
+ }
+ }
+
+ // Synthesize key up from buttons if needed.
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ displayId, policyFlags, lastButtonState, currentButtonState);
+
+ mCursorMotionAccumulator.finishSync();
+ mCursorScrollAccumulator.finishSync();
+}
+
+int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
+void CursorInputMapper::fadePointer() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ }
+}
+
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ // If the device is orientationAware and not a mouse,
+ // it expects to dispatch events to any display
+ return std::make_optional(ADISPLAY_ID_NONE);
+ }
+ }
+ return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
new file mode 100644
index 0000000..77d122a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "InputMapper.h"
+
+#include <PointerControllerInterface.h>
+#include <input/VelocityControl.h>
+
+namespace android {
+
+class VelocityControl;
+class PointerControllerInterface;
+
+class CursorButtonAccumulator;
+class CursorScrollAccumulator;
+
+/* Keeps track of cursor movements. */
+class CursorMotionAccumulator {
+public:
+ CursorMotionAccumulator();
+ void reset(InputDevice* device);
+
+ void process(const RawEvent* rawEvent);
+ void finishSync();
+
+ inline int32_t getRelativeX() const { return mRelX; }
+ inline int32_t getRelativeY() const { return mRelY; }
+
+private:
+ int32_t mRelX;
+ int32_t mRelY;
+
+ void clearRelativeAxes();
+};
+
+class CursorInputMapper : public InputMapper {
+public:
+ explicit CursorInputMapper(InputDevice* device);
+ virtual ~CursorInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+
+ virtual void fadePointer() override;
+
+ virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+private:
+ // Amount that trackball needs to move in order to generate a key event.
+ static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+ // Immutable configuration parameters.
+ struct Parameters {
+ enum Mode {
+ MODE_POINTER,
+ MODE_POINTER_RELATIVE,
+ MODE_NAVIGATION,
+ };
+
+ Mode mode;
+ bool hasAssociatedDisplay;
+ bool orientationAware;
+ } mParameters;
+
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ CursorMotionAccumulator mCursorMotionAccumulator;
+ CursorScrollAccumulator mCursorScrollAccumulator;
+
+ int32_t mSource;
+ float mXScale;
+ float mYScale;
+ float mXPrecision;
+ float mYPrecision;
+
+ float mVWheelScale;
+ float mHWheelScale;
+
+ // Velocity controls for mouse pointer and wheel movements.
+ // The controls for X and Y wheel movements are separate to keep them decoupled.
+ VelocityControl mPointerVelocityControl;
+ VelocityControl mWheelXVelocityControl;
+ VelocityControl mWheelYVelocityControl;
+
+ int32_t mOrientation;
+
+ sp<PointerControllerInterface> mPointerController;
+
+ int32_t mButtonState;
+ nsecs_t mDownTime;
+
+ void configureParameters();
+ void dumpParameters(std::string& dump);
+
+ void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
new file mode 100644
index 0000000..9aa0770
--- /dev/null
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "ExternalStylusInputMapper.h"
+
+#include "SingleTouchMotionAccumulator.h"
+#include "TouchButtonAccumulator.h"
+
+namespace android {
+
+ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {}
+
+uint32_t ExternalStylusInputMapper::getSources() {
+ return AINPUT_SOURCE_STYLUS;
+}
+
+void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+}
+
+void ExternalStylusInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "External Stylus Input Mapper:\n";
+ dump += INDENT3 "Raw Stylus Axes:\n";
+ dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure");
+ dump += INDENT3 "Stylus State:\n";
+ dumpStylusState(dump, mStylusState);
+}
+
+void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
+ mTouchButtonAccumulator.configure(getDevice());
+}
+
+void ExternalStylusInputMapper::reset(nsecs_t when) {
+ InputDevice* device = getDevice();
+ mSingleTouchMotionAccumulator.reset(device);
+ mTouchButtonAccumulator.reset(device);
+ InputMapper::reset(when);
+}
+
+void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+ mSingleTouchMotionAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ sync(rawEvent->when);
+ }
+}
+
+void ExternalStylusInputMapper::sync(nsecs_t when) {
+ mStylusState.clear();
+
+ mStylusState.when = when;
+
+ mStylusState.toolType = mTouchButtonAccumulator.getToolType();
+ if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ }
+
+ int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
+ if (mRawPressureAxis.valid) {
+ mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
+ } else if (mTouchButtonAccumulator.isToolActive()) {
+ mStylusState.pressure = 1.0f;
+ } else {
+ mStylusState.pressure = 0.0f;
+ }
+
+ mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
+
+ mContext->dispatchExternalStylusState(mStylusState);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
new file mode 100644
index 0000000..34f339a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+#include "SingleTouchMotionAccumulator.h"
+#include "StylusState.h"
+#include "TouchButtonAccumulator.h"
+
+namespace android {
+
+class ExternalStylusInputMapper : public InputMapper {
+public:
+ explicit ExternalStylusInputMapper(InputDevice* device);
+ virtual ~ExternalStylusInputMapper() = default;
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+private:
+ SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+ RawAbsoluteAxisInfo mRawPressureAxis;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+
+ StylusState mStylusState;
+
+ void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
new file mode 100644
index 0000000..d941528
--- /dev/null
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputMapper.h"
+
+#include "InputDevice.h"
+
+namespace android {
+
+InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {}
+
+InputMapper::~InputMapper() {}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ info->addSource(getSources());
+}
+
+void InputMapper::dump(std::string& dump) {}
+
+void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {}
+
+void InputMapper::reset(nsecs_t when) {}
+
+void InputMapper::timeoutExpired(nsecs_t when) {}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return false;
+}
+
+void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {}
+
+void InputMapper::cancelVibrate(int32_t token) {}
+
+void InputMapper::cancelTouch(nsecs_t when) {}
+
+int32_t InputMapper::getMetaState() {
+ return 0;
+}
+
+void InputMapper::updateMetaState(int32_t keyCode) {}
+
+void InputMapper::updateExternalStylusState(const StylusState& state) {}
+
+void InputMapper::fadePointer() {}
+
+status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
+ return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
+}
+
+void InputMapper::bumpGeneration() {
+ mDevice->bumpGeneration();
+}
+
+void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+ const char* name) {
+ if (axis.valid) {
+ dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name,
+ axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution);
+ } else {
+ dump += StringPrintf(INDENT4 "%s: unknown range\n", name);
+ }
+}
+
+void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
+ dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
+ dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
+ dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
+ dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
new file mode 100644
index 0000000..a559ef8
--- /dev/null
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_INPUT_MAPPER_H
+#define _UI_INPUTREADER_INPUT_MAPPER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+#include "StylusState.h"
+
+namespace android {
+
+/* An input mapper transforms raw input events into cooked event data.
+ * A single input device can have multiple associated input mappers in order to interpret
+ * different classes of events.
+ *
+ * InputMapper lifecycle:
+ * - create
+ * - configure with 0 changes
+ * - reset
+ * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
+ * - reset
+ * - destroy
+ */
+class InputMapper {
+public:
+ explicit InputMapper(InputDevice* device);
+ virtual ~InputMapper();
+
+ inline InputDevice* getDevice() { return mDevice; }
+ inline int32_t getDeviceId() { return mDevice->getId(); }
+ inline const std::string getDeviceName() { return mDevice->getName(); }
+ inline InputReaderContext* getContext() { return mContext; }
+ inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
+ inline InputListenerInterface* getListener() { return mContext->getListener(); }
+ inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+ virtual uint32_t getSources() = 0;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void dump(std::string& dump);
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
+ virtual void reset(nsecs_t when);
+ virtual void process(const RawEvent* rawEvent) = 0;
+ virtual void timeoutExpired(nsecs_t when);
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ virtual void cancelVibrate(int32_t token);
+ virtual void cancelTouch(nsecs_t when);
+
+ virtual int32_t getMetaState();
+ virtual void updateMetaState(int32_t keyCode);
+
+ virtual void updateExternalStylusState(const StylusState& state);
+
+ virtual void fadePointer();
+ virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+
+protected:
+ InputDevice* mDevice;
+ InputReaderContext* mContext;
+
+ status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+ void bumpGeneration();
+
+ static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+ const char* name);
+ static void dumpStylusState(std::string& dump, const StylusState& state);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
new file mode 100644
index 0000000..50adf73
--- /dev/null
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "JoystickInputMapper.h"
+
+namespace android {
+
+JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {}
+
+JoystickInputMapper::~JoystickInputMapper() {}
+
+uint32_t JoystickInputMapper::getSources() {
+ return AINPUT_SOURCE_JOYSTICK;
+}
+
+void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ for (size_t i = 0; i < mAxes.size(); i++) {
+ const Axis& axis = mAxes.valueAt(i);
+ addMotionRange(axis.axisInfo.axis, axis, info);
+
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ addMotionRange(axis.axisInfo.highAxis, axis, info);
+ }
+ }
+}
+
+void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) {
+ info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz,
+ axis.resolution);
+ /* In order to ease the transition for developers from using the old axes
+ * to the newer, more semantically correct axes, we'll continue to register
+ * the old axes as duplicates of their corresponding new ones. */
+ int32_t compatAxis = getCompatAxis(axisId);
+ if (compatAxis >= 0) {
+ info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat,
+ axis.fuzz, axis.resolution);
+ }
+}
+
+/* A mapping from axes the joystick actually has to the axes that should be
+ * artificially created for compatibility purposes.
+ * Returns -1 if no compatibility axis is needed. */
+int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
+ switch (axis) {
+ case AMOTION_EVENT_AXIS_LTRIGGER:
+ return AMOTION_EVENT_AXIS_BRAKE;
+ case AMOTION_EVENT_AXIS_RTRIGGER:
+ return AMOTION_EVENT_AXIS_GAS;
+ }
+ return -1;
+}
+
+void JoystickInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Joystick Input Mapper:\n";
+
+ dump += INDENT3 "Axes:\n";
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ const Axis& axis = mAxes.valueAt(i);
+ const char* label = getAxisLabel(axis.axisInfo.axis);
+ if (label) {
+ dump += StringPrintf(INDENT4 "%s", label);
+ } else {
+ dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
+ }
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ label = getAxisLabel(axis.axisInfo.highAxis);
+ if (label) {
+ dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
+ } else {
+ dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis,
+ axis.axisInfo.splitValue);
+ }
+ } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
+ dump += " (invert)";
+ }
+
+ dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
+ dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, "
+ "highScale=%0.5f, highOffset=%0.5f\n",
+ axis.scale, axis.offset, axis.highScale, axis.highOffset);
+ dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
+ "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
+ mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+ axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
+ axis.rawAxisInfo.resolution);
+ }
+}
+
+void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+
+ if (!changes) { // first time only
+ // Collect all axes.
+ for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+ if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+ continue; // axis must be claimed by a different device
+ }
+
+ RawAbsoluteAxisInfo rawAxisInfo;
+ getAbsoluteAxisInfo(abs, &rawAxisInfo);
+ if (rawAxisInfo.valid) {
+ // Map axis.
+ AxisInfo axisInfo;
+ bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
+ if (!explicitlyMapped) {
+ // Axis is not explicitly mapped, will choose a generic axis later.
+ axisInfo.mode = AxisInfo::MODE_NORMAL;
+ axisInfo.axis = -1;
+ }
+
+ // Apply flat override.
+ int32_t rawFlat =
+ axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ // Calculate scaling factors and limits.
+ Axis axis;
+ if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+ float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
+ 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
+ } else if (isCenteredAxis(axisInfo.axis)) {
+ float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
+ offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
+ } else {
+ float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
+ 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
+ }
+
+ // To eliminate noise while the joystick is at rest, filter out small variations
+ // in axis values up front.
+ axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
+
+ mAxes.add(abs, axis);
+ }
+ }
+
+ // If there are too many axes, start dropping them.
+ // Prefer to keep explicitly mapped axes.
+ if (mAxes.size() > PointerCoords::MAX_AXES) {
+ ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
+ getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
+ pruneAxes(true);
+ pruneAxes(false);
+ }
+
+ // Assign generic axis ids to remaining axes.
+ int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ Axis& axis = mAxes.editValueAt(i);
+ if (axis.axisInfo.axis < 0) {
+ while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
+ haveAxis(nextGenericAxisId)) {
+ nextGenericAxisId += 1;
+ }
+
+ if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+ axis.axisInfo.axis = nextGenericAxisId;
+ nextGenericAxisId += 1;
+ } else {
+ ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
+ "have already been assigned to other axes.",
+ getDeviceName().c_str(), mAxes.keyAt(i));
+ mAxes.removeItemsAt(i--);
+ numAxes -= 1;
+ }
+ }
+ }
+ }
+}
+
+bool JoystickInputMapper::haveAxis(int32_t axisId) {
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ const Axis& axis = mAxes.valueAt(i);
+ if (axis.axisInfo.axis == axisId ||
+ (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
+ size_t i = mAxes.size();
+ while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
+ if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+ continue;
+ }
+ ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
+ getDeviceName().c_str(), mAxes.keyAt(i));
+ mAxes.removeItemsAt(i);
+ }
+}
+
+bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
+ switch (axis) {
+ case AMOTION_EVENT_AXIS_X:
+ case AMOTION_EVENT_AXIS_Y:
+ case AMOTION_EVENT_AXIS_Z:
+ case AMOTION_EVENT_AXIS_RX:
+ case AMOTION_EVENT_AXIS_RY:
+ case AMOTION_EVENT_AXIS_RZ:
+ case AMOTION_EVENT_AXIS_HAT_X:
+ case AMOTION_EVENT_AXIS_HAT_Y:
+ case AMOTION_EVENT_AXIS_ORIENTATION:
+ case AMOTION_EVENT_AXIS_RUDDER:
+ case AMOTION_EVENT_AXIS_WHEEL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void JoystickInputMapper::reset(nsecs_t when) {
+ // Recenter all axes.
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ Axis& axis = mAxes.editValueAt(i);
+ axis.resetValue();
+ }
+
+ InputMapper::reset(when);
+}
+
+void JoystickInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS: {
+ ssize_t index = mAxes.indexOfKey(rawEvent->code);
+ if (index >= 0) {
+ Axis& axis = mAxes.editValueAt(index);
+ float newValue, highNewValue;
+ switch (axis.axisInfo.mode) {
+ case AxisInfo::MODE_INVERT:
+ newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale +
+ axis.offset;
+ highNewValue = 0.0f;
+ break;
+ case AxisInfo::MODE_SPLIT:
+ if (rawEvent->value < axis.axisInfo.splitValue) {
+ newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale +
+ axis.offset;
+ highNewValue = 0.0f;
+ } else if (rawEvent->value > axis.axisInfo.splitValue) {
+ newValue = 0.0f;
+ highNewValue =
+ (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale +
+ axis.highOffset;
+ } else {
+ newValue = 0.0f;
+ highNewValue = 0.0f;
+ }
+ break;
+ default:
+ newValue = rawEvent->value * axis.scale + axis.offset;
+ highNewValue = 0.0f;
+ break;
+ }
+ axis.newValue = newValue;
+ axis.highNewValue = highNewValue;
+ }
+ break;
+ }
+
+ case EV_SYN:
+ switch (rawEvent->code) {
+ case SYN_REPORT:
+ sync(rawEvent->when, false /*force*/);
+ break;
+ }
+ break;
+ }
+}
+
+void JoystickInputMapper::sync(nsecs_t when, bool force) {
+ if (!filterAxes(force)) {
+ return;
+ }
+
+ int32_t metaState = mContext->getGlobalMetaState();
+ int32_t buttonState = 0;
+
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
+
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ const Axis& axis = mAxes.valueAt(i);
+ setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
+ axis.highCurrentValue);
+ }
+ }
+
+ // Moving a joystick axis should not wake the device because joysticks can
+ // be fairly noisy even when not in use. On the other hand, pushing a gamepad
+ // button will likely wake the device.
+ // TODO: Use the input device configuration to control this behavior more finely.
+ uint32_t policyFlags = 0;
+
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &pointerProperties, &pointerCoords, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+}
+
+void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
+ float value) {
+ pointerCoords->setAxisValue(axis, value);
+ /* In order to ease the transition for developers from using the old axes
+ * to the newer, more semantically correct axes, we'll continue to produce
+ * values for the old axes as mirrors of the value of their corresponding
+ * new axes. */
+ int32_t compatAxis = getCompatAxis(axis);
+ if (compatAxis >= 0) {
+ pointerCoords->setAxisValue(compatAxis, value);
+ }
+}
+
+bool JoystickInputMapper::filterAxes(bool force) {
+ bool atLeastOneSignificantChange = force;
+ size_t numAxes = mAxes.size();
+ for (size_t i = 0; i < numAxes; i++) {
+ Axis& axis = mAxes.editValueAt(i);
+ if (force ||
+ hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
+ axis.max)) {
+ axis.currentValue = axis.newValue;
+ atLeastOneSignificantChange = true;
+ }
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ if (force ||
+ hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue,
+ axis.min, axis.max)) {
+ axis.highCurrentValue = axis.highNewValue;
+ atLeastOneSignificantChange = true;
+ }
+ }
+ }
+ return atLeastOneSignificantChange;
+}
+
+bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue,
+ float currentValue, float min, float max) {
+ if (newValue != currentValue) {
+ // Filter out small changes in value unless the value is converging on the axis
+ // bounds or center point. This is intended to reduce the amount of information
+ // sent to applications by particularly noisy joysticks (such as PS3).
+ if (fabs(newValue - currentValue) > filter ||
+ hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) ||
+ hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) ||
+ hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue,
+ float currentValue,
+ float thresholdValue) {
+ float newDistance = fabs(newValue - thresholdValue);
+ if (newDistance < filter) {
+ float oldDistance = fabs(currentValue - thresholdValue);
+ if (newDistance < oldDistance) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
new file mode 100644
index 0000000..b46d27d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class JoystickInputMapper : public InputMapper {
+public:
+ explicit JoystickInputMapper(InputDevice* device);
+ virtual ~JoystickInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+private:
+ struct Axis {
+ RawAbsoluteAxisInfo rawAxisInfo;
+ AxisInfo axisInfo;
+
+ bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
+
+ float scale; // scale factor from raw to normalized values
+ float offset; // offset to add after scaling for normalization
+ float highScale; // scale factor from raw to normalized values of high split
+ float highOffset; // offset to add after scaling for normalization of high split
+
+ float min; // normalized inclusive minimum
+ float max; // normalized inclusive maximum
+ float flat; // normalized flat region size
+ float fuzz; // normalized error tolerance
+ float resolution; // normalized resolution in units/mm
+
+ float filter; // filter out small variations of this size
+ float currentValue; // current value
+ float newValue; // most recent value
+ float highCurrentValue; // current value of high split
+ float highNewValue; // most recent value of high split
+
+ void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+ bool explicitlyMapped, float scale, float offset, float highScale,
+ float highOffset, float min, float max, float flat, float fuzz,
+ float resolution) {
+ this->rawAxisInfo = rawAxisInfo;
+ this->axisInfo = axisInfo;
+ this->explicitlyMapped = explicitlyMapped;
+ this->scale = scale;
+ this->offset = offset;
+ this->highScale = highScale;
+ this->highOffset = highOffset;
+ this->min = min;
+ this->max = max;
+ this->flat = flat;
+ this->fuzz = fuzz;
+ this->resolution = resolution;
+ this->filter = 0;
+ resetValue();
+ }
+
+ void resetValue() {
+ this->currentValue = 0;
+ this->newValue = 0;
+ this->highCurrentValue = 0;
+ this->highNewValue = 0;
+ }
+ };
+
+ // Axes indexed by raw ABS_* axis index.
+ KeyedVector<int32_t, Axis> mAxes;
+
+ void sync(nsecs_t when, bool force);
+
+ bool haveAxis(int32_t axisId);
+ void pruneAxes(bool ignoreExplicitlyMappedAxes);
+ bool filterAxes(bool force);
+
+ static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue,
+ float min, float max);
+ static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue,
+ float currentValue, float thresholdValue);
+
+ static bool isCenteredAxis(int32_t axis);
+ static int32_t getCompatAxis(int32_t axis);
+
+ static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
+ static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
new file mode 100644
index 0000000..f51d4a0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "KeyboardInputMapper.h"
+
+namespace android {
+
+// --- Static Definitions ---
+
+static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
+ const int32_t map[][4], size_t mapSize) {
+ if (orientation != DISPLAY_ORIENTATION_0) {
+ for (size_t i = 0; i < mapSize; i++) {
+ if (value == map[i][0]) {
+ return map[i][orientation];
+ }
+ }
+ }
+ return value;
+}
+
+static const int32_t keyCodeRotationMap[][4] = {
+ // key codes enumerated counter-clockwise with the original (unrotated) key first
+ // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+ {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
+ {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
+ {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
+ {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
+ {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+ AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
+ {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+ AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
+ {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+ AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
+ {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
+};
+
+static const size_t keyCodeRotationMapSize =
+ sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
+
+static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
+ size_t mapSize) {
+ if (orientation == DISPLAY_ORIENTATION_180) {
+ for (size_t i = 0; i < mapSize; i++) {
+ if (value == map[i][0]) {
+ return map[i][1];
+ }
+ }
+ }
+ return value;
+}
+
+// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
+static int32_t stemKeyRotationMap[][2] = {
+ // key codes enumerated with the original (unrotated) key first
+ // no rotation, 180 degree rotation
+ {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
+ {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
+ {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
+ {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
+};
+
+static const size_t stemKeyRotationMapSize =
+ sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
+
+static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+ keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
+ return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
+ keyCodeRotationMapSize);
+}
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType)
+ : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {}
+
+KeyboardInputMapper::~KeyboardInputMapper() {}
+
+uint32_t KeyboardInputMapper::getSources() {
+ return mSource;
+}
+
+int32_t KeyboardInputMapper::getOrientation() {
+ if (mViewport) {
+ return mViewport->orientation;
+ }
+ return DISPLAY_ORIENTATION_0;
+}
+
+int32_t KeyboardInputMapper::getDisplayId() {
+ if (mViewport) {
+ return mViewport->displayId;
+ }
+ return ADISPLAY_ID_NONE;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setKeyboardType(mKeyboardType);
+ info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
+}
+
+void KeyboardInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Keyboard Input Mapper:\n";
+ dumpParameters(dump);
+ dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+ dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
+ dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
+ dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
+ dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
+}
+
+std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
+ nsecs_t when, const InputReaderConfiguration* config) {
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort) {
+ // Find the viewport that contains the same port
+ return mDevice->getAssociatedViewport();
+ }
+
+ // No associated display defined, try to find default display if orientationAware.
+ if (mParameters.orientationAware) {
+ return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ }
+
+ return std::nullopt;
+}
+
+void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+
+ if (!changes) { // first time only
+ // Configure basic parameters.
+ configureParameters();
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mViewport = findViewport(when, config);
+ }
+}
+
+static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
+ int32_t mapped = 0;
+ if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
+ for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
+ if (stemKeyRotationMap[i][0] == keyCode) {
+ stemKeyRotationMap[i][1] = mapped;
+ return;
+ }
+ }
+ }
+}
+
+void KeyboardInputMapper::configureParameters() {
+ mParameters.orientationAware = false;
+ const PropertyMap& config = getDevice()->getConfiguration();
+ config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
+
+ if (mParameters.orientationAware) {
+ mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
+ mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
+ mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
+ mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
+ }
+
+ mParameters.handlesKeyRepeat = false;
+ config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+}
+
+void KeyboardInputMapper::dumpParameters(std::string& dump) {
+ dump += INDENT3 "Parameters:\n";
+ dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+ dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
+}
+
+void KeyboardInputMapper::reset(nsecs_t when) {
+ mMetaState = AMETA_NONE;
+ mDownTime = 0;
+ mKeyDowns.clear();
+ mCurrentHidUsage = 0;
+
+ resetLedState();
+
+ InputMapper::reset(when);
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY: {
+ int32_t scanCode = rawEvent->code;
+ int32_t usageCode = mCurrentHidUsage;
+ mCurrentHidUsage = 0;
+
+ if (isKeyboardOrGamepadKey(scanCode)) {
+ processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
+ }
+ break;
+ }
+ case EV_MSC: {
+ if (rawEvent->code == MSC_SCAN) {
+ mCurrentHidUsage = rawEvent->value;
+ }
+ break;
+ }
+ case EV_SYN: {
+ if (rawEvent->code == SYN_REPORT) {
+ mCurrentHidUsage = 0;
+ }
+ }
+ }
+}
+
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+ return scanCode < BTN_MOUSE || scanCode >= KEY_OK ||
+ (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
+ (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
+}
+
+bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MEDIA_PLAY:
+ case AKEYCODE_MEDIA_PAUSE:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_RECORD:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_BACKWARD:
+ case AKEYCODE_MEDIA_STEP_FORWARD:
+ case AKEYCODE_MEDIA_STEP_BACKWARD:
+ case AKEYCODE_MEDIA_AUDIO_TRACK:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_VOLUME_MUTE:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+ return true;
+ }
+ return false;
+}
+
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
+ int32_t keyCode;
+ int32_t keyMetaState;
+ uint32_t policyFlags;
+
+ if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode,
+ &keyMetaState, &policyFlags)) {
+ keyCode = AKEYCODE_UNKNOWN;
+ keyMetaState = mMetaState;
+ policyFlags = 0;
+ }
+
+ if (down) {
+ // Rotate key codes according to orientation if needed.
+ if (mParameters.orientationAware) {
+ keyCode = rotateKeyCode(keyCode, getOrientation());
+ }
+
+ // Add key down.
+ ssize_t keyDownIndex = findKeyDown(scanCode);
+ if (keyDownIndex >= 0) {
+ // key repeat, be sure to use same keycode as before in case of rotation
+ keyCode = mKeyDowns[keyDownIndex].keyCode;
+ } else {
+ // key down
+ if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
+ mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {
+ return;
+ }
+ if (policyFlags & POLICY_FLAG_GESTURE) {
+ mDevice->cancelTouch(when);
+ }
+
+ KeyDown keyDown;
+ keyDown.keyCode = keyCode;
+ keyDown.scanCode = scanCode;
+ mKeyDowns.push_back(keyDown);
+ }
+
+ mDownTime = when;
+ } else {
+ // Remove key down.
+ ssize_t keyDownIndex = findKeyDown(scanCode);
+ if (keyDownIndex >= 0) {
+ // key up, be sure to use same keycode as before in case of rotation
+ keyCode = mKeyDowns[keyDownIndex].keyCode;
+ mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
+ } else {
+ // key was not actually down
+ ALOGI("Dropping key up from device %s because the key was not down. "
+ "keyCode=%d, scanCode=%d",
+ getDeviceName().c_str(), keyCode, scanCode);
+ return;
+ }
+ }
+
+ if (updateMetaStateIfNeeded(keyCode, down)) {
+ // If global meta state changed send it along with the key.
+ // If it has not changed then we'll use what keymap gave us,
+ // since key replacement logic might temporarily reset a few
+ // meta bits for given key.
+ keyMetaState = mMetaState;
+ }
+
+ nsecs_t downTime = mDownTime;
+
+ // Key down on external an keyboard should wake the device.
+ // We don't do this for internal keyboards to prevent them from waking up in your pocket.
+ // For internal keyboards, the key layout file should specify the policy flags for
+ // each wake key individually.
+ // TODO: Use the input device configuration to control this behavior more finely.
+ if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+
+ if (mParameters.handlesKeyRepeat) {
+ policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+ }
+
+ NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(),
+ policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
+ getListener()->notifyKey(&args);
+}
+
+ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+ size_t n = mKeyDowns.size();
+ for (size_t i = 0; i < n; i++) {
+ if (mKeyDowns[i].scanCode == scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+}
+
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
+
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
+
+int32_t KeyboardInputMapper::getMetaState() {
+ return mMetaState;
+}
+
+void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+ updateMetaStateIfNeeded(keyCode, false);
+}
+
+bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
+ int32_t oldMetaState = mMetaState;
+ int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
+ bool metaStateChanged = oldMetaState != newMetaState;
+ if (metaStateChanged) {
+ mMetaState = newMetaState;
+ updateLedState(false);
+
+ getContext()->updateGlobalMetaState();
+ }
+
+ return metaStateChanged;
+}
+
+void KeyboardInputMapper::resetLedState() {
+ initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
+ initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
+ initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK);
+
+ updateLedState(true);
+}
+
+void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
+ ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+ ledState.on = false;
+}
+
+void KeyboardInputMapper::updateLedState(bool reset) {
+ updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
+ updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
+ updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
+}
+
+void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led,
+ int32_t modifier, bool reset) {
+ if (ledState.avail) {
+ bool desiredState = (mMetaState & modifier) != 0;
+ if (reset || ledState.on != desiredState) {
+ getEventHub()->setLedState(getDeviceId(), led, desiredState);
+ ledState.on = desiredState;
+ }
+ }
+}
+
+std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+ if (mViewport) {
+ return std::make_optional(mViewport->displayId);
+ }
+ return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
new file mode 100644
index 0000000..de2a377
--- /dev/null
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class KeyboardInputMapper : public InputMapper {
+public:
+ KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
+ virtual ~KeyboardInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) override;
+
+ virtual int32_t getMetaState() override;
+ virtual void updateMetaState(int32_t keyCode) override;
+ virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+private:
+ // The current viewport.
+ std::optional<DisplayViewport> mViewport;
+
+ struct KeyDown {
+ int32_t keyCode;
+ int32_t scanCode;
+ };
+
+ uint32_t mSource;
+ int32_t mKeyboardType;
+
+ std::vector<KeyDown> mKeyDowns; // keys that are down
+ int32_t mMetaState;
+ nsecs_t mDownTime; // time of most recent key down
+
+ int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
+
+ struct LedState {
+ bool avail; // led is available
+ bool on; // we think the led is currently on
+ };
+ LedState mCapsLockLedState;
+ LedState mNumLockLedState;
+ LedState mScrollLockLedState;
+
+ // Immutable configuration parameters.
+ struct Parameters {
+ bool orientationAware;
+ bool handlesKeyRepeat;
+ } mParameters;
+
+ void configureParameters();
+ void dumpParameters(std::string& dump);
+
+ int32_t getOrientation();
+ int32_t getDisplayId();
+
+ bool isKeyboardOrGamepadKey(int32_t scanCode);
+ bool isMediaKey(int32_t keyCode);
+
+ void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
+
+ bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
+
+ ssize_t findKeyDown(int32_t scanCode);
+
+ void resetLedState();
+ void initializeLedState(LedState& ledState, int32_t led);
+ void updateLedState(bool reset);
+ void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
+ std::optional<DisplayViewport> findViewport(nsecs_t when,
+ const InputReaderConfiguration* config);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
new file mode 100644
index 0000000..c567c8b
--- /dev/null
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "MultiTouchInputMapper.h"
+
+namespace android {
+
+// --- Constants ---
+
+// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
+static constexpr size_t MAX_SLOTS = 32;
+
+// --- MultiTouchMotionAccumulator ---
+
+MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
+ : mCurrentSlot(-1),
+ mSlots(nullptr),
+ mSlotCount(0),
+ mUsingSlotsProtocol(false),
+ mHaveStylus(false) {}
+
+MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
+ delete[] mSlots;
+}
+
+void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount,
+ bool usingSlotsProtocol) {
+ mSlotCount = slotCount;
+ mUsingSlotsProtocol = usingSlotsProtocol;
+ mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
+
+ delete[] mSlots;
+ mSlots = new Slot[slotCount];
+}
+
+void MultiTouchMotionAccumulator::reset(InputDevice* device) {
+ // Unfortunately there is no way to read the initial contents of the slots.
+ // So when we reset the accumulator, we must assume they are all zeroes.
+ if (mUsingSlotsProtocol) {
+ // Query the driver for the current slot index and use it as the initial slot
+ // before we start reading events from the device. It is possible that the
+ // current slot index will not be the same as it was when the first event was
+ // written into the evdev buffer, which means the input mapper could start
+ // out of sync with the initial state of the events in the evdev buffer.
+ // In the extremely unlikely case that this happens, the data from
+ // two slots will be confused until the next ABS_MT_SLOT event is received.
+ // This can cause the touch point to "jump", but at least there will be
+ // no stuck touches.
+ int32_t initialSlot;
+ status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT,
+ &initialSlot);
+ if (status) {
+ ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
+ initialSlot = -1;
+ }
+ clearSlots(initialSlot);
+ } else {
+ clearSlots(-1);
+ }
+}
+
+void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+ if (mSlots) {
+ for (size_t i = 0; i < mSlotCount; i++) {
+ mSlots[i].clear();
+ }
+ }
+ mCurrentSlot = initialSlot;
+}
+
+void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_ABS) {
+ bool newSlot = false;
+ if (mUsingSlotsProtocol) {
+ if (rawEvent->code == ABS_MT_SLOT) {
+ mCurrentSlot = rawEvent->value;
+ newSlot = true;
+ }
+ } else if (mCurrentSlot < 0) {
+ mCurrentSlot = 0;
+ }
+
+ if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
+#if DEBUG_POINTERS
+ if (newSlot) {
+ ALOGW("MultiTouch device emitted invalid slot index %d but it "
+ "should be between 0 and %zd; ignoring this slot.",
+ mCurrentSlot, mSlotCount - 1);
+ }
+#endif
+ } else {
+ Slot* slot = &mSlots[mCurrentSlot];
+
+ switch (rawEvent->code) {
+ case ABS_MT_POSITION_X:
+ slot->mInUse = true;
+ slot->mAbsMTPositionX = rawEvent->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ slot->mInUse = true;
+ slot->mAbsMTPositionY = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ slot->mInUse = true;
+ slot->mAbsMTTouchMajor = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ slot->mInUse = true;
+ slot->mAbsMTTouchMinor = rawEvent->value;
+ slot->mHaveAbsMTTouchMinor = true;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ slot->mInUse = true;
+ slot->mAbsMTWidthMajor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ slot->mInUse = true;
+ slot->mAbsMTWidthMinor = rawEvent->value;
+ slot->mHaveAbsMTWidthMinor = true;
+ break;
+ case ABS_MT_ORIENTATION:
+ slot->mInUse = true;
+ slot->mAbsMTOrientation = rawEvent->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (mUsingSlotsProtocol && rawEvent->value < 0) {
+ // The slot is no longer in use but it retains its previous contents,
+ // which may be reused for subsequent touches.
+ slot->mInUse = false;
+ } else {
+ slot->mInUse = true;
+ slot->mAbsMTTrackingId = rawEvent->value;
+ }
+ break;
+ case ABS_MT_PRESSURE:
+ slot->mInUse = true;
+ slot->mAbsMTPressure = rawEvent->value;
+ break;
+ case ABS_MT_DISTANCE:
+ slot->mInUse = true;
+ slot->mAbsMTDistance = rawEvent->value;
+ break;
+ case ABS_MT_TOOL_TYPE:
+ slot->mInUse = true;
+ slot->mAbsMTToolType = rawEvent->value;
+ slot->mHaveAbsMTToolType = true;
+ break;
+ }
+ }
+ } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
+ // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+ mCurrentSlot += 1;
+ }
+}
+
+void MultiTouchMotionAccumulator::finishSync() {
+ if (!mUsingSlotsProtocol) {
+ clearSlots(-1);
+ }
+}
+
+bool MultiTouchMotionAccumulator::hasStylus() const {
+ return mHaveStylus;
+}
+
+// --- MultiTouchMotionAccumulator::Slot ---
+
+MultiTouchMotionAccumulator::Slot::Slot() {
+ clear();
+}
+
+void MultiTouchMotionAccumulator::Slot::clear() {
+ mInUse = false;
+ mHaveAbsMTTouchMinor = false;
+ mHaveAbsMTWidthMinor = false;
+ mHaveAbsMTToolType = false;
+ mAbsMTPositionX = 0;
+ mAbsMTPositionY = 0;
+ mAbsMTTouchMajor = 0;
+ mAbsMTTouchMinor = 0;
+ mAbsMTWidthMajor = 0;
+ mAbsMTWidthMinor = 0;
+ mAbsMTOrientation = 0;
+ mAbsMTTrackingId = -1;
+ mAbsMTPressure = 0;
+ mAbsMTDistance = 0;
+ mAbsMTToolType = 0;
+}
+
+int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
+ if (mHaveAbsMTToolType) {
+ switch (mAbsMTToolType) {
+ case MT_TOOL_FINGER:
+ return AMOTION_EVENT_TOOL_TYPE_FINGER;
+ case MT_TOOL_PEN:
+ return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ }
+ }
+ return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+}
+
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {}
+
+void MultiTouchInputMapper::reset(nsecs_t when) {
+ mMultiTouchMotionAccumulator.reset(getDevice());
+
+ mPointerIdBits.clear();
+
+ TouchInputMapper::reset(when);
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+ TouchInputMapper::process(rawEvent);
+
+ mMultiTouchMotionAccumulator.process(rawEvent);
+}
+
+void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
+ size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
+ size_t outCount = 0;
+ BitSet32 newPointerIdBits;
+ mHavePointerIds = true;
+
+ for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
+ const MultiTouchMotionAccumulator::Slot* inSlot =
+ mMultiTouchMotionAccumulator.getSlot(inIndex);
+ if (!inSlot->isInUse()) {
+ continue;
+ }
+
+ if (outCount >= MAX_POINTERS) {
+#if DEBUG_POINTERS
+ ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+ "ignoring the rest.",
+ getDeviceName().c_str(), MAX_POINTERS);
+#endif
+ break; // too many fingers!
+ }
+
+ RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
+ outPointer.x = inSlot->getX();
+ outPointer.y = inSlot->getY();
+ outPointer.pressure = inSlot->getPressure();
+ outPointer.touchMajor = inSlot->getTouchMajor();
+ outPointer.touchMinor = inSlot->getTouchMinor();
+ outPointer.toolMajor = inSlot->getToolMajor();
+ outPointer.toolMinor = inSlot->getToolMinor();
+ outPointer.orientation = inSlot->getOrientation();
+ outPointer.distance = inSlot->getDistance();
+ outPointer.tiltX = 0;
+ outPointer.tiltY = 0;
+
+ outPointer.toolType = inSlot->getToolType();
+ if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ outPointer.toolType = mTouchButtonAccumulator.getToolType();
+ if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ }
+ }
+
+ bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
+ (mTouchButtonAccumulator.isHovering() ||
+ (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
+ outPointer.isHovering = isHovering;
+
+ // Assign pointer id using tracking id if available.
+ if (mHavePointerIds) {
+ int32_t trackingId = inSlot->getTrackingId();
+ int32_t id = -1;
+ if (trackingId >= 0) {
+ for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
+ uint32_t n = idBits.clearFirstMarkedBit();
+ if (mPointerTrackingIdMap[n] == trackingId) {
+ id = n;
+ }
+ }
+
+ if (id < 0 && !mPointerIdBits.isFull()) {
+ id = mPointerIdBits.markFirstUnmarkedBit();
+ mPointerTrackingIdMap[id] = trackingId;
+ }
+ }
+ if (id < 0) {
+ mHavePointerIds = false;
+ outState->rawPointerData.clearIdBits();
+ newPointerIdBits.clear();
+ } else {
+ outPointer.id = id;
+ outState->rawPointerData.idToIndex[id] = outCount;
+ outState->rawPointerData.markIdBit(id, isHovering);
+ newPointerIdBits.markBit(id);
+ }
+ }
+ outCount += 1;
+ }
+
+ outState->rawPointerData.pointerCount = outCount;
+ mPointerIdBits = newPointerIdBits;
+
+ mMultiTouchMotionAccumulator.finishSync();
+}
+
+void MultiTouchInputMapper::configureRawPointerAxes() {
+ TouchInputMapper::configureRawPointerAxes();
+
+ getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
+ getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
+ getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
+ getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
+ getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
+ getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
+ getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
+ getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
+ getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
+ getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
+ getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
+
+ if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
+ mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
+ size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
+ if (slotCount > MAX_SLOTS) {
+ ALOGW("MultiTouch Device %s reported %zu slots but the framework "
+ "only supports a maximum of %zu slots at this time.",
+ getDeviceName().c_str(), slotCount, MAX_SLOTS);
+ slotCount = MAX_SLOTS;
+ }
+ mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
+ } else {
+ mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS,
+ false /*usingSlotsProtocol*/);
+ }
+}
+
+bool MultiTouchInputMapper::hasStylus() const {
+ return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
new file mode 100644
index 0000000..a45c3cb
--- /dev/null
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+
+#include "TouchInputMapper.h"
+
+namespace android {
+
+/* Keeps track of the state of multi-touch protocol. */
+class MultiTouchMotionAccumulator {
+public:
+ class Slot {
+ public:
+ inline bool isInUse() const { return mInUse; }
+ inline int32_t getX() const { return mAbsMTPositionX; }
+ inline int32_t getY() const { return mAbsMTPositionY; }
+ inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
+ inline int32_t getTouchMinor() const {
+ return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor;
+ }
+ inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
+ inline int32_t getToolMinor() const {
+ return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor;
+ }
+ inline int32_t getOrientation() const { return mAbsMTOrientation; }
+ inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
+ inline int32_t getPressure() const { return mAbsMTPressure; }
+ inline int32_t getDistance() const { return mAbsMTDistance; }
+ inline int32_t getToolType() const;
+
+ private:
+ friend class MultiTouchMotionAccumulator;
+
+ bool mInUse;
+ bool mHaveAbsMTTouchMinor;
+ bool mHaveAbsMTWidthMinor;
+ bool mHaveAbsMTToolType;
+
+ int32_t mAbsMTPositionX;
+ int32_t mAbsMTPositionY;
+ int32_t mAbsMTTouchMajor;
+ int32_t mAbsMTTouchMinor;
+ int32_t mAbsMTWidthMajor;
+ int32_t mAbsMTWidthMinor;
+ int32_t mAbsMTOrientation;
+ int32_t mAbsMTTrackingId;
+ int32_t mAbsMTPressure;
+ int32_t mAbsMTDistance;
+ int32_t mAbsMTToolType;
+
+ Slot();
+ void clear();
+ };
+
+ MultiTouchMotionAccumulator();
+ ~MultiTouchMotionAccumulator();
+
+ void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
+ void reset(InputDevice* device);
+ void process(const RawEvent* rawEvent);
+ void finishSync();
+ bool hasStylus() const;
+
+ inline size_t getSlotCount() const { return mSlotCount; }
+ inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
+
+private:
+ int32_t mCurrentSlot;
+ Slot* mSlots;
+ size_t mSlotCount;
+ bool mUsingSlotsProtocol;
+ bool mHaveStylus;
+
+ void clearSlots(int32_t initialSlot);
+};
+
+class MultiTouchInputMapper : public TouchInputMapper {
+public:
+ explicit MultiTouchInputMapper(InputDevice* device);
+ virtual ~MultiTouchInputMapper();
+
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+protected:
+ virtual void syncTouch(nsecs_t when, RawState* outState);
+ virtual void configureRawPointerAxes();
+ virtual bool hasStylus() const;
+
+private:
+ MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
+
+ // Specifies the pointer id bits that are in use, and their associated tracking id.
+ BitSet32 mPointerIdBits;
+ int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
new file mode 100644
index 0000000..e113cca
--- /dev/null
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "RotaryEncoderInputMapper.h"
+
+#include "CursorScrollAccumulator.h"
+
+namespace android {
+
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device)
+ : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
+ mSource = AINPUT_SOURCE_ROTARY_ENCODER;
+}
+
+RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
+
+uint32_t RotaryEncoderInputMapper::getSources() {
+ return mSource;
+}
+
+void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
+ float res = 0.0f;
+ if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
+ ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
+ }
+ if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+ mScalingFactor)) {
+ ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
+ "default to 1.0!\n");
+ mScalingFactor = 1.0f;
+ }
+ info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ res * mScalingFactor);
+ }
+}
+
+void RotaryEncoderInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Rotary Encoder Input Mapper:\n";
+ dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
+ toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
+}
+
+void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+ if (!changes) {
+ mRotaryEncoderScrollAccumulator.configure(getDevice());
+ }
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
+ } else {
+ mOrientation = DISPLAY_ORIENTATION_0;
+ }
+ }
+}
+
+void RotaryEncoderInputMapper::reset(nsecs_t when) {
+ mRotaryEncoderScrollAccumulator.reset(getDevice());
+
+ InputMapper::reset(when);
+}
+
+void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+ mRotaryEncoderScrollAccumulator.process(rawEvent);
+
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ sync(rawEvent->when);
+ }
+}
+
+void RotaryEncoderInputMapper::sync(nsecs_t when) {
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
+
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+ float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+ bool scrolled = scroll != 0;
+
+ // This is not a pointer, so it's not associated with a display.
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ // Moving the rotary encoder should wake the device (if specified).
+ uint32_t policyFlags = 0;
+ if (scrolled && getDevice()->isExternal()) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+
+ if (mOrientation == DISPLAY_ORIENTATION_180) {
+ scroll = -scroll;
+ }
+
+ // Send motion event.
+ if (scrolled) {
+ int32_t metaState = mContext->getGlobalMetaState();
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
+
+ NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+ metaState, /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+ getListener()->notifyMotion(&scrollArgs);
+ }
+
+ mRotaryEncoderScrollAccumulator.finishSync();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
new file mode 100644
index 0000000..38c7258
--- /dev/null
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+
+#include "CursorScrollAccumulator.h"
+#include "InputMapper.h"
+
+namespace android {
+
+class RotaryEncoderInputMapper : public InputMapper {
+public:
+ explicit RotaryEncoderInputMapper(InputDevice* device);
+ virtual ~RotaryEncoderInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+private:
+ CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
+
+ int32_t mSource;
+ float mScalingFactor;
+ int32_t mOrientation;
+
+ void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
new file mode 100644
index 0000000..440d282
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SingleTouchInputMapper.h"
+
+namespace android {
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+
+SingleTouchInputMapper::~SingleTouchInputMapper() {}
+
+void SingleTouchInputMapper::reset(nsecs_t when) {
+ mSingleTouchMotionAccumulator.reset(getDevice());
+
+ TouchInputMapper::reset(when);
+}
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+ TouchInputMapper::process(rawEvent);
+
+ mSingleTouchMotionAccumulator.process(rawEvent);
+}
+
+void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
+ if (mTouchButtonAccumulator.isToolActive()) {
+ outState->rawPointerData.pointerCount = 1;
+ outState->rawPointerData.idToIndex[0] = 0;
+
+ bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
+ (mTouchButtonAccumulator.isHovering() ||
+ (mRawPointerAxes.pressure.valid &&
+ mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
+ outState->rawPointerData.markIdBit(0, isHovering);
+
+ RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0];
+ outPointer.id = 0;
+ outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
+ outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
+ outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
+ outPointer.touchMajor = 0;
+ outPointer.touchMinor = 0;
+ outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
+ outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
+ outPointer.orientation = 0;
+ outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();
+ outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();
+ outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();
+ outPointer.toolType = mTouchButtonAccumulator.getToolType();
+ if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ }
+ outPointer.isHovering = isHovering;
+ }
+}
+
+void SingleTouchInputMapper::configureRawPointerAxes() {
+ TouchInputMapper::configureRawPointerAxes();
+
+ getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
+ getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
+ getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
+ getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
+ getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
+ getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
+ getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
+}
+
+bool SingleTouchInputMapper::hasStylus() const {
+ return mTouchButtonAccumulator.hasStylus();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
new file mode 100644
index 0000000..8438eee
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+
+#include "SingleTouchMotionAccumulator.h"
+#include "TouchInputMapper.h"
+
+namespace android {
+
+class SingleTouchInputMapper : public TouchInputMapper {
+public:
+ explicit SingleTouchInputMapper(InputDevice* device);
+ virtual ~SingleTouchInputMapper();
+
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+protected:
+ virtual void syncTouch(nsecs_t when, RawState* outState);
+ virtual void configureRawPointerAxes();
+ virtual bool hasStylus() const;
+
+private:
+ SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
new file mode 100644
index 0000000..4ff941f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "SwitchInputMapper.h"
+
+namespace android {
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device)
+ : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {}
+
+SwitchInputMapper::~SwitchInputMapper() {}
+
+uint32_t SwitchInputMapper::getSources() {
+ return AINPUT_SOURCE_SWITCH;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_SW:
+ processSwitch(rawEvent->code, rawEvent->value);
+ break;
+
+ case EV_SYN:
+ if (rawEvent->code == SYN_REPORT) {
+ sync(rawEvent->when);
+ }
+ }
+}
+
+void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
+ if (switchCode >= 0 && switchCode < 32) {
+ if (switchValue) {
+ mSwitchValues |= 1 << switchCode;
+ } else {
+ mSwitchValues &= ~(1 << switchCode);
+ }
+ mUpdatedSwitchMask |= 1 << switchCode;
+ }
+}
+
+void SwitchInputMapper::sync(nsecs_t when) {
+ if (mUpdatedSwitchMask) {
+ uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
+ NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
+ mUpdatedSwitchMask);
+ getListener()->notifySwitch(&args);
+
+ mUpdatedSwitchMask = 0;
+ }
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+void SwitchInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Switch Input Mapper:\n";
+ dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
new file mode 100644
index 0000000..e65d4e2
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class SwitchInputMapper : public InputMapper {
+public:
+ explicit SwitchInputMapper(InputDevice* device);
+ virtual ~SwitchInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
+ virtual void dump(std::string& dump) override;
+
+private:
+ uint32_t mSwitchValues;
+ uint32_t mUpdatedSwitchMask;
+
+ void processSwitch(int32_t switchCode, int32_t switchValue);
+ void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
new file mode 100644
index 0000000..efa3d6d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+#include <stdint.h>
+
+namespace android {
+
+// --- Static Definitions ---
+
+static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
+ float temp;
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ temp = *deltaX;
+ *deltaX = *deltaY;
+ *deltaY = -temp;
+ break;
+
+ case DISPLAY_ORIENTATION_180:
+ *deltaX = -*deltaX;
+ *deltaY = -*deltaY;
+ break;
+
+ case DISPLAY_ORIENTATION_270:
+ temp = *deltaX;
+ *deltaX = -*deltaY;
+ *deltaY = temp;
+ break;
+ }
+}
+
+// Returns true if the pointer should be reported as being down given the specified
+// button states. This determines whether the event is reported as a touch event.
+static bool isPointerDown(int32_t buttonState) {
+ return buttonState &
+ (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
+ int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t lastButtonState,
+ int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+ if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+ (currentButtonState & buttonState)) ||
+ (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+ !(currentButtonState & buttonState))) {
+ NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId,
+ policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+ context->getListener()->notifyKey(&args);
+ }
+}
+
+static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
+ int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t lastButtonState,
+ int32_t currentButtonState) {
+ synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
+ AKEYCODE_BACK);
+ synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
+ AKEYCODE_FORWARD);
+}
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
new file mode 100644
index 0000000..34603b9
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -0,0 +1,3883 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "TouchInputMapper.h"
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "TouchButtonAccumulator.h"
+#include "TouchCursorInputMapperCommon.h"
+
+namespace android {
+
+// --- Constants ---
+
+// Maximum amount of latency to add to touch events while waiting for data from an
+// external stylus.
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+
+// Maximum amount of time to wait on touch data before pushing out new pressure data.
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+
+// Artificial latency on synthetic events created from stylus data without corresponding touch
+// data.
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// --- Static Definitions ---
+
+template <typename T>
+inline static void swap(T& a, T& b) {
+ T temp = a;
+ a = b;
+ b = temp;
+}
+
+static float calculateCommonVector(float a, float b) {
+ if (a > 0 && b > 0) {
+ return a < b ? a : b;
+ } else if (a < 0 && b < 0) {
+ return a > b ? a : b;
+ } else {
+ return 0;
+ }
+}
+
+inline static float distance(float x1, float y1, float x2, float y2) {
+ return hypotf(x1 - x2, y1 - y2);
+}
+
+inline static int32_t signExtendNybble(int32_t value) {
+ return value >= 8 ? value - 16 : value;
+}
+
+// --- RawPointerAxes ---
+
+RawPointerAxes::RawPointerAxes() {
+ clear();
+}
+
+void RawPointerAxes::clear() {
+ x.clear();
+ y.clear();
+ pressure.clear();
+ touchMajor.clear();
+ touchMinor.clear();
+ toolMajor.clear();
+ toolMinor.clear();
+ orientation.clear();
+ distance.clear();
+ tiltX.clear();
+ tiltY.clear();
+ trackingId.clear();
+ slot.clear();
+}
+
+// --- RawPointerData ---
+
+RawPointerData::RawPointerData() {
+ clear();
+}
+
+void RawPointerData::clear() {
+ pointerCount = 0;
+ clearIdBits();
+}
+
+void RawPointerData::copyFrom(const RawPointerData& other) {
+ pointerCount = other.pointerCount;
+ hoveringIdBits = other.hoveringIdBits;
+ touchingIdBits = other.touchingIdBits;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointers[i] = other.pointers[i];
+
+ int id = pointers[i].id;
+ idToIndex[id] = other.idToIndex[id];
+ }
+}
+
+void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
+ float x = 0, y = 0;
+ uint32_t count = touchingIdBits.count();
+ if (count) {
+ for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const Pointer& pointer = pointerForId(id);
+ x += pointer.x;
+ y += pointer.y;
+ }
+ x /= count;
+ y /= count;
+ }
+ *outX = x;
+ *outY = y;
+}
+
+// --- CookedPointerData ---
+
+CookedPointerData::CookedPointerData() {
+ clear();
+}
+
+void CookedPointerData::clear() {
+ pointerCount = 0;
+ hoveringIdBits.clear();
+ touchingIdBits.clear();
+}
+
+void CookedPointerData::copyFrom(const CookedPointerData& other) {
+ pointerCount = other.pointerCount;
+ hoveringIdBits = other.hoveringIdBits;
+ touchingIdBits = other.touchingIdBits;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].copyFrom(other.pointerProperties[i]);
+ pointerCoords[i].copyFrom(other.pointerCoords[i]);
+
+ int id = pointerProperties[i].id;
+ idToIndex[id] = other.idToIndex[id];
+ }
+}
+
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device)
+ : InputMapper(device),
+ mSource(0),
+ mDeviceMode(DEVICE_MODE_DISABLED),
+ mSurfaceWidth(-1),
+ mSurfaceHeight(-1),
+ mSurfaceLeft(0),
+ mSurfaceTop(0),
+ mPhysicalWidth(-1),
+ mPhysicalHeight(-1),
+ mPhysicalLeft(0),
+ mPhysicalTop(0),
+ mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}
+
+TouchInputMapper::~TouchInputMapper() {}
+
+uint32_t TouchInputMapper::getSources() {
+ return mSource;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ if (mDeviceMode != DEVICE_MODE_DISABLED) {
+ info->addMotionRange(mOrientedRanges.x);
+ info->addMotionRange(mOrientedRanges.y);
+ info->addMotionRange(mOrientedRanges.pressure);
+
+ if (mOrientedRanges.haveSize) {
+ info->addMotionRange(mOrientedRanges.size);
+ }
+
+ if (mOrientedRanges.haveTouchSize) {
+ info->addMotionRange(mOrientedRanges.touchMajor);
+ info->addMotionRange(mOrientedRanges.touchMinor);
+ }
+
+ if (mOrientedRanges.haveToolSize) {
+ info->addMotionRange(mOrientedRanges.toolMajor);
+ info->addMotionRange(mOrientedRanges.toolMinor);
+ }
+
+ if (mOrientedRanges.haveOrientation) {
+ info->addMotionRange(mOrientedRanges.orientation);
+ }
+
+ if (mOrientedRanges.haveDistance) {
+ info->addMotionRange(mOrientedRanges.distance);
+ }
+
+ if (mOrientedRanges.haveTilt) {
+ info->addMotionRange(mOrientedRanges.tilt);
+ }
+
+ if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+ }
+ if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+ }
+ if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+ const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
+ x.fuzz, x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
+ y.fuzz, y.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
+ x.fuzz, x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
+ y.fuzz, y.resolution);
+ }
+ info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+ }
+}
+
+void TouchInputMapper::dump(std::string& dump) {
+ dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
+ dumpParameters(dump);
+ dumpVirtualKeys(dump);
+ dumpRawPointerAxes(dump);
+ dumpCalibration(dump);
+ dumpAffineTransformation(dump);
+ dumpSurface(dump);
+
+ dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
+ dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
+ dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
+ dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
+ dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
+ dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
+ dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
+ dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
+ dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
+ dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
+ dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
+ dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
+ dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt));
+ dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter);
+ dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale);
+ dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
+ dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
+
+ dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
+ dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
+ mLastRawState.rawPointerData.pointerCount);
+ for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
+ const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
+ dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
+ "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
+ "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
+ "toolType=%d, isHovering=%s\n",
+ i, pointer.id, pointer.x, pointer.y, pointer.pressure,
+ pointer.touchMajor, pointer.touchMinor, pointer.toolMajor,
+ pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY,
+ pointer.distance, pointer.toolType, toString(pointer.isHovering));
+ }
+
+ dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n",
+ mLastCookedState.buttonState);
+ dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
+ mLastCookedState.cookedPointerData.pointerCount);
+ for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) {
+ const PointerProperties& pointerProperties =
+ mLastCookedState.cookedPointerData.pointerProperties[i];
+ const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
+ dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
+ "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, "
+ "toolMinor=%0.3f, "
+ "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
+ "toolType=%d, isHovering=%s\n",
+ i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
+ pointerProperties.toolType,
+ toString(mLastCookedState.cookedPointerData.isHovering(i)));
+ }
+
+ dump += INDENT3 "Stylus Fusion:\n";
+ dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
+ toString(mExternalStylusConnected));
+ dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
+ dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
+ mExternalStylusFusionTimeout);
+ dump += INDENT3 "External Stylus State:\n";
+ dumpStylusState(dump, mExternalStylusState);
+
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
+ dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale);
+ dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale);
+ dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale);
+ dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale);
+ dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth);
+ }
+}
+
+const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
+ switch (deviceMode) {
+ case DEVICE_MODE_DISABLED:
+ return "disabled";
+ case DEVICE_MODE_DIRECT:
+ return "direct";
+ case DEVICE_MODE_UNSCALED:
+ return "unscaled";
+ case DEVICE_MODE_NAVIGATION:
+ return "navigation";
+ case DEVICE_MODE_POINTER:
+ return "pointer";
+ }
+ return "unknown";
+}
+
+void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ InputMapper::configure(when, config, changes);
+
+ mConfig = *config;
+
+ if (!changes) { // first time only
+ // Configure basic parameters.
+ configureParameters();
+
+ // Configure common accumulators.
+ mCursorScrollAccumulator.configure(getDevice());
+ mTouchButtonAccumulator.configure(getDevice());
+
+ // Configure absolute axis information.
+ configureRawPointerAxes();
+
+ // Prepare input device calibration.
+ parseCalibration();
+ resolveCalibration();
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
+ // Update location calibration to reflect current settings
+ updateAffineTransformation();
+ }
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+ // Update pointer speed.
+ mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
+ mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
+ mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
+ }
+
+ bool resetNeeded = false;
+ if (!changes ||
+ (changes &
+ (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
+ InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
+ // Configure device sources, surface dimensions, orientation and
+ // scaling factors.
+ configureSurface(when, &resetNeeded);
+ }
+
+ if (changes && resetNeeded) {
+ // Send reset, unless this is the first time the device has been configured,
+ // in which case the reader will call reset itself after all mappers are ready.
+ getDevice()->notifyReset(when);
+ }
+}
+
+void TouchInputMapper::resolveExternalStylusPresence() {
+ std::vector<InputDeviceInfo> devices;
+ mContext->getExternalStylusDevices(devices);
+ mExternalStylusConnected = !devices.empty();
+
+ if (!mExternalStylusConnected) {
+ resetExternalStylus();
+ }
+}
+
+void TouchInputMapper::configureParameters() {
+ // Use the pointer presentation mode for devices that do not support distinct
+ // multitouch. The spot-based presentation relies on being able to accurately
+ // locate two or more fingers on the touch pad.
+ mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
+ ? Parameters::GESTURE_MODE_SINGLE_TOUCH
+ : Parameters::GESTURE_MODE_MULTI_TOUCH;
+
+ String8 gestureModeString;
+ if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+ gestureModeString)) {
+ if (gestureModeString == "single-touch") {
+ mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
+ } else if (gestureModeString == "multi-touch") {
+ mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
+ } else if (gestureModeString != "default") {
+ ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
+ }
+ }
+
+ if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
+ // The device is a touch screen.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
+ // The device is a pointing device like a track pad.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) ||
+ getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
+ // The device is a cursor device with a touch pad attached.
+ // By default don't use the touch pad to move the pointer.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ } else {
+ // The device is a touch pad of unknown purpose.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ }
+
+ mParameters.hasButtonUnderPad =
+ getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
+
+ String8 deviceTypeString;
+ if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
+ deviceTypeString)) {
+ if (deviceTypeString == "touchScreen") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ } else if (deviceTypeString == "touchPad") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ } else if (deviceTypeString == "touchNavigation") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
+ } else if (deviceTypeString == "pointer") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ } else if (deviceTypeString != "default") {
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+ }
+ }
+
+ mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+ mParameters.orientationAware);
+
+ mParameters.hasAssociatedDisplay = false;
+ mParameters.associatedDisplayIsExternal = false;
+ if (mParameters.orientationAware ||
+ mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN ||
+ mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ mParameters.hasAssociatedDisplay = true;
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+ mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+ String8 uniqueDisplayId;
+ getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
+ uniqueDisplayId);
+ mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
+ }
+ }
+ if (getDevice()->getAssociatedDisplayPort()) {
+ mParameters.hasAssociatedDisplay = true;
+ }
+
+ // Initial downs on external touch devices should wake the device.
+ // Normally we don't do this for internal touch screens to prevent them from waking
+ // up in your pocket but you can enable it using the input device configuration.
+ mParameters.wake = getDevice()->isExternal();
+ getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+}
+
+void TouchInputMapper::dumpParameters(std::string& dump) {
+ dump += INDENT3 "Parameters:\n";
+
+ switch (mParameters.gestureMode) {
+ case Parameters::GESTURE_MODE_SINGLE_TOUCH:
+ dump += INDENT4 "GestureMode: single-touch\n";
+ break;
+ case Parameters::GESTURE_MODE_MULTI_TOUCH:
+ dump += INDENT4 "GestureMode: multi-touch\n";
+ break;
+ default:
+ assert(false);
+ }
+
+ switch (mParameters.deviceType) {
+ case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+ dump += INDENT4 "DeviceType: touchScreen\n";
+ break;
+ case Parameters::DEVICE_TYPE_TOUCH_PAD:
+ dump += INDENT4 "DeviceType: touchPad\n";
+ break;
+ case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+ dump += INDENT4 "DeviceType: touchNavigation\n";
+ break;
+ case Parameters::DEVICE_TYPE_POINTER:
+ dump += INDENT4 "DeviceType: pointer\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
+ "displayId='%s'\n",
+ toString(mParameters.hasAssociatedDisplay),
+ toString(mParameters.associatedDisplayIsExternal),
+ mParameters.uniqueDisplayId.c_str());
+ dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+}
+
+void TouchInputMapper::configureRawPointerAxes() {
+ mRawPointerAxes.clear();
+}
+
+void TouchInputMapper::dumpRawPointerAxes(std::string& dump) {
+ dump += INDENT3 "Raw Touch Axes:\n";
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
+ dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
+}
+
+bool TouchInputMapper::hasExternalStylus() const {
+ return mExternalStylusConnected;
+}
+
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+ if (mParameters.hasAssociatedDisplay) {
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort) {
+ // Find the viewport that contains the same port
+ return mDevice->getAssociatedViewport();
+ }
+
+ // Check if uniqueDisplayId is specified in idc file.
+ if (!mParameters.uniqueDisplayId.empty()) {
+ return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+ }
+
+ ViewportType viewportTypeToUse;
+ if (mParameters.associatedDisplayIsExternal) {
+ viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ } else {
+ viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ }
+
+ std::optional<DisplayViewport> viewport =
+ mConfig.getDisplayViewportByType(viewportTypeToUse);
+ if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+ ALOGW("Input device %s should be associated with external display, "
+ "fallback to internal one for the external viewport is not found.",
+ getDeviceName().c_str());
+ viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ }
+
+ return viewport;
+ }
+
+ // No associated display, return a non-display viewport.
+ DisplayViewport newViewport;
+ // Raw width and height in the natural orientation.
+ int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+ return std::make_optional(newViewport);
+}
+
+void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
+ int32_t oldDeviceMode = mDeviceMode;
+
+ resolveExternalStylusPresence();
+
+ // Determine device mode.
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&
+ mConfig.pointerGesturesEnabled) {
+ mSource = AINPUT_SOURCE_MOUSE;
+ mDeviceMode = DEVICE_MODE_POINTER;
+ if (hasStylus()) {
+ mSource |= AINPUT_SOURCE_STYLUS;
+ }
+ } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN &&
+ mParameters.hasAssociatedDisplay) {
+ mSource = AINPUT_SOURCE_TOUCHSCREEN;
+ mDeviceMode = DEVICE_MODE_DIRECT;
+ if (hasStylus()) {
+ mSource |= AINPUT_SOURCE_STYLUS;
+ }
+ if (hasExternalStylus()) {
+ mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
+ }
+ } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+ mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+ mDeviceMode = DEVICE_MODE_NAVIGATION;
+ } else {
+ mSource = AINPUT_SOURCE_TOUCHPAD;
+ mDeviceMode = DEVICE_MODE_UNSCALED;
+ }
+
+ // Ensure we have valid X and Y axes.
+ if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
+ ALOGW("Touch device '%s' did not report support for X or Y axis! "
+ "The device will be inoperable.",
+ getDeviceName().c_str());
+ mDeviceMode = DEVICE_MODE_DISABLED;
+ return;
+ }
+
+ // Get associated display dimensions.
+ std::optional<DisplayViewport> newViewport = findViewport();
+ if (!newViewport) {
+ ALOGI("Touch device '%s' could not query the properties of its associated "
+ "display. The device will be inoperable until the display size "
+ "becomes available.",
+ getDeviceName().c_str());
+ mDeviceMode = DEVICE_MODE_DISABLED;
+ return;
+ }
+
+ // Raw width and height in the natural orientation.
+ int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ int32_t rawHeight = mRawPointerAxes.getRawHeight();
+
+ bool viewportChanged = mViewport != *newViewport;
+ if (viewportChanged) {
+ mViewport = *newViewport;
+
+ if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+ // Convert rotated viewport to natural surface coordinates.
+ int32_t naturalLogicalWidth, naturalLogicalHeight;
+ int32_t naturalPhysicalWidth, naturalPhysicalHeight;
+ int32_t naturalPhysicalLeft, naturalPhysicalTop;
+ int32_t naturalDeviceWidth, naturalDeviceHeight;
+ switch (mViewport.orientation) {
+ case DISPLAY_ORIENTATION_90:
+ naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+ naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+ naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+ naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+ naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
+ naturalPhysicalTop = mViewport.physicalLeft;
+ naturalDeviceWidth = mViewport.deviceHeight;
+ naturalDeviceHeight = mViewport.deviceWidth;
+ break;
+ case DISPLAY_ORIENTATION_180:
+ naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+ naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+ naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+ naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+ naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
+ naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
+ naturalDeviceWidth = mViewport.deviceWidth;
+ naturalDeviceHeight = mViewport.deviceHeight;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+ naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+ naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+ naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+ naturalPhysicalLeft = mViewport.physicalTop;
+ naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
+ naturalDeviceWidth = mViewport.deviceHeight;
+ naturalDeviceHeight = mViewport.deviceWidth;
+ break;
+ case DISPLAY_ORIENTATION_0:
+ default:
+ naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+ naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+ naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+ naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+ naturalPhysicalLeft = mViewport.physicalLeft;
+ naturalPhysicalTop = mViewport.physicalTop;
+ naturalDeviceWidth = mViewport.deviceWidth;
+ naturalDeviceHeight = mViewport.deviceHeight;
+ break;
+ }
+
+ if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
+ ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
+ naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
+ naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
+ }
+
+ mPhysicalWidth = naturalPhysicalWidth;
+ mPhysicalHeight = naturalPhysicalHeight;
+ mPhysicalLeft = naturalPhysicalLeft;
+ mPhysicalTop = naturalPhysicalTop;
+
+ mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+ mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+ mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
+ mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+
+ mSurfaceOrientation =
+ mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
+ } else {
+ mPhysicalWidth = rawWidth;
+ mPhysicalHeight = rawHeight;
+ mPhysicalLeft = 0;
+ mPhysicalTop = 0;
+
+ mSurfaceWidth = rawWidth;
+ mSurfaceHeight = rawHeight;
+ mSurfaceLeft = 0;
+ mSurfaceTop = 0;
+ mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+ }
+ }
+
+ // If moving between pointer modes, need to reset some state.
+ bool deviceModeChanged = mDeviceMode != oldDeviceMode;
+ if (deviceModeChanged) {
+ mOrientedRanges.clear();
+ }
+
+ // Create or update pointer controller if needed.
+ if (mDeviceMode == DEVICE_MODE_POINTER ||
+ (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+ if (mPointerController == nullptr || viewportChanged) {
+ mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+ }
+ } else {
+ mPointerController.clear();
+ }
+
+ if (viewportChanged || deviceModeChanged) {
+ ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+ "display id %d",
+ getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
+ mSurfaceOrientation, mDeviceMode, mViewport.displayId);
+
+ // Configure X and Y factors.
+ mXScale = float(mSurfaceWidth) / rawWidth;
+ mYScale = float(mSurfaceHeight) / rawHeight;
+ mXTranslate = -mSurfaceLeft;
+ mYTranslate = -mSurfaceTop;
+ mXPrecision = 1.0f / mXScale;
+ mYPrecision = 1.0f / mYScale;
+
+ mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
+ mOrientedRanges.x.source = mSource;
+ mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
+ mOrientedRanges.y.source = mSource;
+
+ configureVirtualKeys();
+
+ // Scale factor for terms that are not oriented in a particular axis.
+ // If the pixels are square then xScale == yScale otherwise we fake it
+ // by choosing an average.
+ mGeometricScale = avg(mXScale, mYScale);
+
+ // Size of diagonal axis.
+ float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
+
+ // Size factors.
+ if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+ if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
+ } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
+ } else {
+ mSizeScale = 0.0f;
+ }
+
+ mOrientedRanges.haveTouchSize = true;
+ mOrientedRanges.haveToolSize = true;
+ mOrientedRanges.haveSize = true;
+
+ mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
+ mOrientedRanges.touchMajor.source = mSource;
+ mOrientedRanges.touchMajor.min = 0;
+ mOrientedRanges.touchMajor.max = diagonalSize;
+ mOrientedRanges.touchMajor.flat = 0;
+ mOrientedRanges.touchMajor.fuzz = 0;
+ mOrientedRanges.touchMajor.resolution = 0;
+
+ mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+ mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
+
+ mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
+ mOrientedRanges.toolMajor.source = mSource;
+ mOrientedRanges.toolMajor.min = 0;
+ mOrientedRanges.toolMajor.max = diagonalSize;
+ mOrientedRanges.toolMajor.flat = 0;
+ mOrientedRanges.toolMajor.fuzz = 0;
+ mOrientedRanges.toolMajor.resolution = 0;
+
+ mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
+ mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
+
+ mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
+ mOrientedRanges.size.source = mSource;
+ mOrientedRanges.size.min = 0;
+ mOrientedRanges.size.max = 1.0;
+ mOrientedRanges.size.flat = 0;
+ mOrientedRanges.size.fuzz = 0;
+ mOrientedRanges.size.resolution = 0;
+ } else {
+ mSizeScale = 0.0f;
+ }
+
+ // Pressure factors.
+ mPressureScale = 0;
+ float pressureMax = 1.0;
+ if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL ||
+ mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+ if (mCalibration.havePressureScale) {
+ mPressureScale = mCalibration.pressureScale;
+ pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
+ } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
+ mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
+ }
+ }
+
+ mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
+ mOrientedRanges.pressure.source = mSource;
+ mOrientedRanges.pressure.min = 0;
+ mOrientedRanges.pressure.max = pressureMax;
+ mOrientedRanges.pressure.flat = 0;
+ mOrientedRanges.pressure.fuzz = 0;
+ mOrientedRanges.pressure.resolution = 0;
+
+ // Tilt
+ mTiltXCenter = 0;
+ mTiltXScale = 0;
+ mTiltYCenter = 0;
+ mTiltYScale = 0;
+ mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
+ if (mHaveTilt) {
+ mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
+ mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
+ mTiltXScale = M_PI / 180;
+ mTiltYScale = M_PI / 180;
+
+ mOrientedRanges.haveTilt = true;
+
+ mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
+ mOrientedRanges.tilt.source = mSource;
+ mOrientedRanges.tilt.min = 0;
+ mOrientedRanges.tilt.max = M_PI_2;
+ mOrientedRanges.tilt.flat = 0;
+ mOrientedRanges.tilt.fuzz = 0;
+ mOrientedRanges.tilt.resolution = 0;
+ }
+
+ // Orientation
+ mOrientationScale = 0;
+ if (mHaveTilt) {
+ mOrientedRanges.haveOrientation = true;
+
+ mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+ mOrientedRanges.orientation.source = mSource;
+ mOrientedRanges.orientation.min = -M_PI;
+ mOrientedRanges.orientation.max = M_PI;
+ mOrientedRanges.orientation.flat = 0;
+ mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
+ } else if (mCalibration.orientationCalibration !=
+ Calibration::ORIENTATION_CALIBRATION_NONE) {
+ if (mCalibration.orientationCalibration ==
+ Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+ if (mRawPointerAxes.orientation.valid) {
+ if (mRawPointerAxes.orientation.maxValue > 0) {
+ mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
+ } else if (mRawPointerAxes.orientation.minValue < 0) {
+ mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
+ } else {
+ mOrientationScale = 0;
+ }
+ }
+ }
+
+ mOrientedRanges.haveOrientation = true;
+
+ mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+ mOrientedRanges.orientation.source = mSource;
+ mOrientedRanges.orientation.min = -M_PI_2;
+ mOrientedRanges.orientation.max = M_PI_2;
+ mOrientedRanges.orientation.flat = 0;
+ mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
+ }
+
+ // Distance
+ mDistanceScale = 0;
+ if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
+ if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) {
+ if (mCalibration.haveDistanceScale) {
+ mDistanceScale = mCalibration.distanceScale;
+ } else {
+ mDistanceScale = 1.0f;
+ }
+ }
+
+ mOrientedRanges.haveDistance = true;
+
+ mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
+ mOrientedRanges.distance.source = mSource;
+ mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale;
+ mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale;
+ mOrientedRanges.distance.flat = 0;
+ mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale;
+ mOrientedRanges.distance.resolution = 0;
+ }
+
+ // Compute oriented precision, scales and ranges.
+ // Note that the maximum value reported is an inclusive maximum value so it is one
+ // unit less than the total width or height of surface.
+ switch (mSurfaceOrientation) {
+ case DISPLAY_ORIENTATION_90:
+ case DISPLAY_ORIENTATION_270:
+ mOrientedXPrecision = mYPrecision;
+ mOrientedYPrecision = mXPrecision;
+
+ mOrientedRanges.x.min = mYTranslate;
+ mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.x.flat = 0;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
+
+ mOrientedRanges.y.min = mXTranslate;
+ mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.y.flat = 0;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
+ break;
+
+ default:
+ mOrientedXPrecision = mXPrecision;
+ mOrientedYPrecision = mYPrecision;
+
+ mOrientedRanges.x.min = mXTranslate;
+ mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.x.flat = 0;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
+
+ mOrientedRanges.y.min = mYTranslate;
+ mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.y.flat = 0;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
+ break;
+ }
+
+ // Location
+ updateAffineTransformation();
+
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ // Compute pointer gesture detection parameters.
+ float rawDiagonal = hypotf(rawWidth, rawHeight);
+ float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
+
+ // Scale movements such that one whole swipe of the touch pad covers a
+ // given area relative to the diagonal size of the display when no acceleration
+ // is applied.
+ // Assume that the touch pad has a square aspect ratio such that movements in
+ // X and Y of the same number of raw units cover the same physical distance.
+ mPointerXMovementScale =
+ mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal;
+ mPointerYMovementScale = mPointerXMovementScale;
+
+ // Scale zooms to cover a smaller range of the display than movements do.
+ // This value determines the area around the pointer that is affected by freeform
+ // pointer gestures.
+ mPointerXZoomScale =
+ mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal;
+ mPointerYZoomScale = mPointerXZoomScale;
+
+ // Max width between pointers to detect a swipe gesture is more than some fraction
+ // of the diagonal axis of the touch pad. Touches that are wider than this are
+ // translated into freeform gestures.
+ mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
+
+ // Abort current pointer usages because the state has changed.
+ abortPointerUsage(when, 0 /*policyFlags*/);
+ }
+
+ // Inform the dispatcher about the changes.
+ *outResetNeeded = true;
+ bumpGeneration();
+ }
+}
+
+void TouchInputMapper::dumpSurface(std::string& dump) {
+ dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
+ dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
+ dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+ dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
+ dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
+ dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
+ dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
+ dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
+ dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
+ dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
+}
+
+void TouchInputMapper::configureVirtualKeys() {
+ std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
+ getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
+
+ mVirtualKeys.clear();
+
+ if (virtualKeyDefinitions.size() == 0) {
+ return;
+ }
+
+ int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
+ int32_t touchScreenTop = mRawPointerAxes.y.minValue;
+ int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+ int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
+
+ for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) {
+ VirtualKey virtualKey;
+
+ virtualKey.scanCode = virtualKeyDefinition.scanCode;
+ int32_t keyCode;
+ int32_t dummyKeyMetaState;
+ uint32_t flags;
+ if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode,
+ &dummyKeyMetaState, &flags)) {
+ ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
+ continue; // drop the key
+ }
+
+ virtualKey.keyCode = keyCode;
+ virtualKey.flags = flags;
+
+ // convert the key definition's display coordinates into touch coordinates for a hit box
+ int32_t halfWidth = virtualKeyDefinition.width / 2;
+ int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+ virtualKey.hitLeft =
+ (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth +
+ touchScreenLeft;
+ virtualKey.hitRight =
+ (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth +
+ touchScreenLeft;
+ virtualKey.hitTop =
+ (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight +
+ touchScreenTop;
+ virtualKey.hitBottom =
+ (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight +
+ touchScreenTop;
+ mVirtualKeys.push_back(virtualKey);
+ }
+}
+
+void TouchInputMapper::dumpVirtualKeys(std::string& dump) {
+ if (!mVirtualKeys.empty()) {
+ dump += INDENT3 "Virtual Keys:\n";
+
+ for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mVirtualKeys[i];
+ dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
+ "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+ i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft,
+ virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+ }
+ }
+}
+
+void TouchInputMapper::parseCalibration() {
+ const PropertyMap& in = getDevice()->getConfiguration();
+ Calibration& out = mCalibration;
+
+ // Size
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+ String8 sizeCalibrationString;
+ if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+ if (sizeCalibrationString == "none") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ } else if (sizeCalibrationString == "geometric") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ } else if (sizeCalibrationString == "diameter") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
+ } else if (sizeCalibrationString == "box") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
+ } else if (sizeCalibrationString == "area") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
+ } else if (sizeCalibrationString != "default") {
+ ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
+ }
+ }
+
+ out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale);
+ out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias);
+ out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
+
+ // Pressure
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+ String8 pressureCalibrationString;
+ if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+ if (pressureCalibrationString == "none") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ } else if (pressureCalibrationString == "physical") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ } else if (pressureCalibrationString == "amplitude") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ } else if (pressureCalibrationString != "default") {
+ ALOGW("Invalid value for touch.pressure.calibration: '%s'",
+ pressureCalibrationString.string());
+ }
+ }
+
+ out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
+
+ // Orientation
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+ String8 orientationCalibrationString;
+ if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+ if (orientationCalibrationString == "none") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ } else if (orientationCalibrationString == "interpolated") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ } else if (orientationCalibrationString == "vector") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
+ } else if (orientationCalibrationString != "default") {
+ ALOGW("Invalid value for touch.orientation.calibration: '%s'",
+ orientationCalibrationString.string());
+ }
+ }
+
+ // Distance
+ out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+ String8 distanceCalibrationString;
+ if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+ if (distanceCalibrationString == "none") {
+ out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ } else if (distanceCalibrationString == "scaled") {
+ out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ } else if (distanceCalibrationString != "default") {
+ ALOGW("Invalid value for touch.distance.calibration: '%s'",
+ distanceCalibrationString.string());
+ }
+ }
+
+ out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
+
+ out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
+ String8 coverageCalibrationString;
+ if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
+ if (coverageCalibrationString == "none") {
+ out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ } else if (coverageCalibrationString == "box") {
+ out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
+ } else if (coverageCalibrationString != "default") {
+ ALOGW("Invalid value for touch.coverage.calibration: '%s'",
+ coverageCalibrationString.string());
+ }
+ }
+}
+
+void TouchInputMapper::resolveCalibration() {
+ // Size
+ if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
+ if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ }
+ } else {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ }
+
+ // Pressure
+ if (mRawPointerAxes.pressure.valid) {
+ if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ }
+ } else {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ }
+
+ // Orientation
+ if (mRawPointerAxes.orientation.valid) {
+ if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ }
+ } else {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ }
+
+ // Distance
+ if (mRawPointerAxes.distance.valid) {
+ if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
+ mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ }
+ } else {
+ mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ }
+
+ // Coverage
+ if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
+ mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ }
+}
+
+void TouchInputMapper::dumpCalibration(std::string& dump) {
+ dump += INDENT3 "Calibration:\n";
+
+ // Size
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NONE:
+ dump += INDENT4 "touch.size.calibration: none\n";
+ break;
+ case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+ dump += INDENT4 "touch.size.calibration: geometric\n";
+ break;
+ case Calibration::SIZE_CALIBRATION_DIAMETER:
+ dump += INDENT4 "touch.size.calibration: diameter\n";
+ break;
+ case Calibration::SIZE_CALIBRATION_BOX:
+ dump += INDENT4 "touch.size.calibration: box\n";
+ break;
+ case Calibration::SIZE_CALIBRATION_AREA:
+ dump += INDENT4 "touch.size.calibration: area\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ if (mCalibration.haveSizeScale) {
+ dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale);
+ }
+
+ if (mCalibration.haveSizeBias) {
+ dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias);
+ }
+
+ if (mCalibration.haveSizeIsSummed) {
+ dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n",
+ toString(mCalibration.sizeIsSummed));
+ }
+
+ // Pressure
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_NONE:
+ dump += INDENT4 "touch.pressure.calibration: none\n";
+ break;
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ dump += INDENT4 "touch.pressure.calibration: physical\n";
+ break;
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ dump += INDENT4 "touch.pressure.calibration: amplitude\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ if (mCalibration.havePressureScale) {
+ dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale);
+ }
+
+ // Orientation
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_NONE:
+ dump += INDENT4 "touch.orientation.calibration: none\n";
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ dump += INDENT4 "touch.orientation.calibration: interpolated\n";
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_VECTOR:
+ dump += INDENT4 "touch.orientation.calibration: vector\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ // Distance
+ switch (mCalibration.distanceCalibration) {
+ case Calibration::DISTANCE_CALIBRATION_NONE:
+ dump += INDENT4 "touch.distance.calibration: none\n";
+ break;
+ case Calibration::DISTANCE_CALIBRATION_SCALED:
+ dump += INDENT4 "touch.distance.calibration: scaled\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+
+ if (mCalibration.haveDistanceScale) {
+ dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale);
+ }
+
+ switch (mCalibration.coverageCalibration) {
+ case Calibration::COVERAGE_CALIBRATION_NONE:
+ dump += INDENT4 "touch.coverage.calibration: none\n";
+ break;
+ case Calibration::COVERAGE_CALIBRATION_BOX:
+ dump += INDENT4 "touch.coverage.calibration: box\n";
+ break;
+ default:
+ ALOG_ASSERT(false);
+ }
+}
+
+void TouchInputMapper::dumpAffineTransformation(std::string& dump) {
+ dump += INDENT3 "Affine Transformation:\n";
+
+ dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale);
+ dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix);
+ dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset);
+ dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix);
+ dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale);
+ dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset);
+}
+
+void TouchInputMapper::updateAffineTransformation() {
+ mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
+ mSurfaceOrientation);
+}
+
+void TouchInputMapper::reset(nsecs_t when) {
+ mCursorButtonAccumulator.reset(getDevice());
+ mCursorScrollAccumulator.reset(getDevice());
+ mTouchButtonAccumulator.reset(getDevice());
+
+ mPointerVelocityControl.reset();
+ mWheelXVelocityControl.reset();
+ mWheelYVelocityControl.reset();
+
+ mRawStatesPending.clear();
+ mCurrentRawState.clear();
+ mCurrentCookedState.clear();
+ mLastRawState.clear();
+ mLastCookedState.clear();
+ mPointerUsage = POINTER_USAGE_NONE;
+ mSentHoverEnter = false;
+ mHavePointerIds = false;
+ mCurrentMotionAborted = false;
+ mDownTime = 0;
+
+ mCurrentVirtualKey.down = false;
+
+ mPointerGesture.reset();
+ mPointerSimple.reset();
+ resetExternalStylus();
+
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->clearSpots();
+ }
+
+ InputMapper::reset(when);
+}
+
+void TouchInputMapper::resetExternalStylus() {
+ mExternalStylusState.clear();
+ mExternalStylusId = -1;
+ mExternalStylusFusionTimeout = LLONG_MAX;
+ mExternalStylusDataPending = false;
+}
+
+void TouchInputMapper::clearStylusDataPendingFlags() {
+ mExternalStylusDataPending = false;
+ mExternalStylusFusionTimeout = LLONG_MAX;
+}
+
+void TouchInputMapper::process(const RawEvent* rawEvent) {
+ mCursorButtonAccumulator.process(rawEvent);
+ mCursorScrollAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ sync(rawEvent->when);
+ }
+}
+
+void TouchInputMapper::sync(nsecs_t when) {
+ const RawState* last =
+ mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
+
+ // Push a new state.
+ mRawStatesPending.emplace_back();
+
+ RawState* next = &mRawStatesPending.back();
+ next->clear();
+ next->when = when;
+
+ // Sync button state.
+ next->buttonState =
+ mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
+
+ // Sync scroll
+ next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
+ next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
+ mCursorScrollAccumulator.finishSync();
+
+ // Sync touch
+ syncTouch(when, next);
+
+ // Assign pointer ids.
+ if (!mHavePointerIds) {
+ assignPointerIds(last, next);
+ }
+
+#if DEBUG_RAW_EVENTS
+ ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+ "hovering ids 0x%08x -> 0x%08x",
+ last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
+ last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
+ last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
+#endif
+
+ processRawTouches(false /*timeout*/);
+}
+
+void TouchInputMapper::processRawTouches(bool timeout) {
+ if (mDeviceMode == DEVICE_MODE_DISABLED) {
+ // Drop all input if the device is disabled.
+ mCurrentRawState.clear();
+ mRawStatesPending.clear();
+ return;
+ }
+
+ // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
+ // valid and must go through the full cook and dispatch cycle. This ensures that anything
+ // touching the current state will only observe the events that have been dispatched to the
+ // rest of the pipeline.
+ const size_t N = mRawStatesPending.size();
+ size_t count;
+ for (count = 0; count < N; count++) {
+ const RawState& next = mRawStatesPending[count];
+
+ // A failure to assign the stylus id means that we're waiting on stylus data
+ // and so should defer the rest of the pipeline.
+ if (assignExternalStylusId(next, timeout)) {
+ break;
+ }
+
+ // All ready to go.
+ clearStylusDataPendingFlags();
+ mCurrentRawState.copyFrom(next);
+ if (mCurrentRawState.when < mLastRawState.when) {
+ mCurrentRawState.when = mLastRawState.when;
+ }
+ cookAndDispatch(mCurrentRawState.when);
+ }
+ if (count != 0) {
+ mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
+ }
+
+ if (mExternalStylusDataPending) {
+ if (timeout) {
+ nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
+ clearStylusDataPendingFlags();
+ mCurrentRawState.copyFrom(mLastRawState);
+#if DEBUG_STYLUS_FUSION
+ ALOGD("Timeout expired, synthesizing event with new stylus data");
+#endif
+ cookAndDispatch(when);
+ } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
+ mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
+ getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+ }
+ }
+}
+
+void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+ // Always start with a clean state.
+ mCurrentCookedState.clear();
+
+ // Apply stylus buttons to current raw state.
+ applyExternalStylusButtonState(when);
+
+ // Handle policy on initial down or hover events.
+ bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
+ mCurrentRawState.rawPointerData.pointerCount != 0;
+
+ uint32_t policyFlags = 0;
+ bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
+ if (initialDown || buttonsPressed) {
+ // If this is a touch screen, hide the pointer on an initial down.
+ if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ getContext()->fadePointer();
+ }
+
+ if (mParameters.wake) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+ }
+
+ // Consume raw off-screen touches before cooking pointer data.
+ // If touches are consumed, subsequent code will not receive any pointer data.
+ if (consumeRawTouches(when, policyFlags)) {
+ mCurrentRawState.rawPointerData.clear();
+ }
+
+ // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure
+ // with cooked pointer data that has the same ids and indices as the raw data.
+ // The following code can use either the raw or cooked data, as needed.
+ cookPointerData();
+
+ // Apply stylus pressure to current cooked state.
+ applyExternalStylusTouchState(when);
+
+ // Synthesize key down from raw buttons if needed.
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
+ mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+ mCurrentCookedState.buttonState);
+
+ // Dispatch the touches either directly or by translation through a pointer on screen.
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(id);
+ if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+ pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ mCurrentCookedState.stylusIdBits.markBit(id);
+ } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
+ pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ mCurrentCookedState.fingerIdBits.markBit(id);
+ } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
+ mCurrentCookedState.mouseIdBits.markBit(id);
+ }
+ }
+ for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(id);
+ if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+ pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ mCurrentCookedState.stylusIdBits.markBit(id);
+ }
+ }
+
+ // Stylus takes precedence over all tools, then mouse, then finger.
+ PointerUsage pointerUsage = mPointerUsage;
+ if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+ mCurrentCookedState.mouseIdBits.clear();
+ mCurrentCookedState.fingerIdBits.clear();
+ pointerUsage = POINTER_USAGE_STYLUS;
+ } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+ mCurrentCookedState.fingerIdBits.clear();
+ pointerUsage = POINTER_USAGE_MOUSE;
+ } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
+ isPointerDown(mCurrentRawState.buttonState)) {
+ pointerUsage = POINTER_USAGE_GESTURES;
+ }
+
+ dispatchPointerUsage(when, policyFlags, pointerUsage);
+ } else {
+ if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
+ mPointerController != nullptr) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+
+ mPointerController->setButtonState(mCurrentRawState.buttonState);
+ mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mViewport.displayId);
+ }
+
+ if (!mCurrentMotionAborted) {
+ dispatchButtonRelease(when, policyFlags);
+ dispatchHoverExit(when, policyFlags);
+ dispatchTouches(when, policyFlags);
+ dispatchHoverEnterAndMove(when, policyFlags);
+ dispatchButtonPress(when, policyFlags);
+ }
+
+ if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
+ mCurrentMotionAborted = false;
+ }
+ }
+
+ // Synthesize key up from raw buttons if needed.
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+ mCurrentCookedState.buttonState);
+
+ // Clear some transient state.
+ mCurrentRawState.rawVScroll = 0;
+ mCurrentRawState.rawHScroll = 0;
+
+ // Copy current touch to last touch in preparation for the next cycle.
+ mLastRawState.copyFrom(mCurrentRawState);
+ mLastCookedState.copyFrom(mCurrentCookedState);
+}
+
+void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
+ if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
+ mCurrentRawState.buttonState |= mExternalStylusState.buttons;
+ }
+}
+
+void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
+ CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
+ const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
+
+ if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
+ float pressure = mExternalStylusState.pressure;
+ if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
+ const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
+ pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ }
+ PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+
+ PointerProperties& properties =
+ currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
+ if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+ properties.toolType = mExternalStylusState.toolType;
+ }
+ }
+}
+
+bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
+ if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+ return false;
+ }
+
+ const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
+ state.rawPointerData.pointerCount != 0;
+ if (initialDown) {
+ if (mExternalStylusState.pressure != 0.0f) {
+#if DEBUG_STYLUS_FUSION
+ ALOGD("Have both stylus and touch data, beginning fusion");
+#endif
+ mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+ } else if (timeout) {
+#if DEBUG_STYLUS_FUSION
+ ALOGD("Timeout expired, assuming touch is not a stylus.");
+#endif
+ resetExternalStylus();
+ } else {
+ if (mExternalStylusFusionTimeout == LLONG_MAX) {
+ mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
+ }
+#if DEBUG_STYLUS_FUSION
+ ALOGD("No stylus data but stylus is connected, requesting timeout "
+ "(%" PRId64 "ms)",
+ mExternalStylusFusionTimeout);
+#endif
+ getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+ return true;
+ }
+ }
+
+ // Check if the stylus pointer has gone up.
+ if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
+#if DEBUG_STYLUS_FUSION
+ ALOGD("Stylus pointer is going up");
+#endif
+ mExternalStylusId = -1;
+ }
+
+ return false;
+}
+
+void TouchInputMapper::timeoutExpired(nsecs_t when) {
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mPointerUsage == POINTER_USAGE_GESTURES) {
+ dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+ }
+ } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ if (mExternalStylusFusionTimeout < when) {
+ processRawTouches(true /*timeout*/);
+ } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
+ getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+ }
+ }
+}
+
+void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+ mExternalStylusState.copyFrom(state);
+ if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
+ // We're either in the middle of a fused stream of data or we're waiting on data before
+ // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
+ // data.
+ mExternalStylusDataPending = true;
+ processRawTouches(false /*timeout*/);
+ }
+}
+
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+ // Check for release of a virtual key.
+ if (mCurrentVirtualKey.down) {
+ if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+ // Pointer went up while virtual key was down.
+ mCurrentVirtualKey.down = false;
+ if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+ ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+ }
+ return true;
+ }
+
+ if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
+ uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(id);
+ const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+ if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
+ // Pointer is still within the space of the virtual key.
+ return true;
+ }
+ }
+
+ // Pointer left virtual key area or another pointer also went down.
+ // Send key cancellation but do not consume the touch yet.
+ // This is useful when the user swipes through from the virtual key area
+ // into the main display surface.
+ mCurrentVirtualKey.down = false;
+ if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+ ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
+ mCurrentVirtualKey.scanCode);
+#endif
+ dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
+ AKEY_EVENT_FLAG_CANCELED);
+ }
+ }
+
+ if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() &&
+ !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+ // Pointer just went down. Check for virtual key press or off-screen touches.
+ uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+ const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
+ if (!isPointInsideSurface(pointer.x, pointer.y)) {
+ // If exactly one pointer went down, check for virtual key hit.
+ // Otherwise we will drop the entire stroke.
+ if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
+ const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+ if (virtualKey) {
+ mCurrentVirtualKey.down = true;
+ mCurrentVirtualKey.downTime = when;
+ mCurrentVirtualKey.keyCode = virtualKey->keyCode;
+ mCurrentVirtualKey.scanCode = virtualKey->scanCode;
+ mCurrentVirtualKey.ignored =
+ mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode,
+ virtualKey->scanCode);
+
+ if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+ ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN,
+ AKEY_EVENT_FLAG_FROM_SYSTEM |
+ AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ // Disable all virtual key touches that happen within a short time interval of the
+ // most recent touch within the screen area. The idea is to filter out stray
+ // virtual key presses when interacting with the touch screen.
+ //
+ // Problems we're trying to solve:
+ //
+ // 1. While scrolling a list or dragging the window shade, the user swipes down into a
+ // virtual key area that is implemented by a separate touch panel and accidentally
+ // triggers a virtual key.
+ //
+ // 2. While typing in the on screen keyboard, the user taps slightly outside the screen
+ // area and accidentally triggers a virtual key. This often happens when virtual keys
+ // are layed out below the screen near to where the on screen keyboard's space bar
+ // is displayed.
+ if (mConfig.virtualKeyQuietTime > 0 &&
+ !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+ mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
+ }
+ return false;
+}
+
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags) {
+ int32_t keyCode = mCurrentVirtualKey.keyCode;
+ int32_t scanCode = mCurrentVirtualKey.scanCode;
+ nsecs_t downTime = mCurrentVirtualKey.downTime;
+ int32_t metaState = mContext->getGlobalMetaState();
+ policyFlags |= POLICY_FLAG_VIRTUAL;
+
+ NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
+ scanCode, metaState, downTime);
+ getListener()->notifyKey(&args);
+}
+
+void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
+ BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
+ if (!currentIdBits.isEmpty()) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mCurrentCookedState.buttonState;
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ mCurrentMotionAborted = true;
+ }
+}
+
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+ BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
+ BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
+ int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mCurrentCookedState.buttonState;
+
+ if (currentIdBits == lastIdBits) {
+ if (!currentIdBits.isEmpty()) {
+ // No pointer id changes so this is a move event.
+ // The listener takes care of batching moves so we don't have to deal with that here.
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+ } else {
+ // There may be pointers going up and pointers going down and pointers moving
+ // all at the same time.
+ BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
+ BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
+ BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
+ BitSet32 dispatchedIdBits(lastIdBits.value);
+
+ // Update last coordinates of pointers that have moved so that we observe the new
+ // pointer positions at the same time as other pointers that have just gone up.
+ bool moveNeeded =
+ updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex, moveIdBits);
+ if (buttonState != mLastCookedState.buttonState) {
+ moveNeeded = true;
+ }
+
+ // Dispatch pointer up events.
+ while (!upIdBits.isEmpty()) {
+ uint32_t upId = upIdBits.clearFirstMarkedBit();
+
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
+ metaState, buttonState, 0,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ dispatchedIdBits.clearBit(upId);
+ }
+
+ // Dispatch move events if any of the remaining pointers moved from their old locations.
+ // Although applications receive new locations as part of individual pointer up
+ // events, they do not generally handle them except when presented in a move event.
+ if (moveNeeded && !moveIdBits.isEmpty()) {
+ ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+
+ // Dispatch pointer down events using the new pointer locations.
+ while (!downIdBits.isEmpty()) {
+ uint32_t downId = downIdBits.clearFirstMarkedBit();
+ dispatchedIdBits.markBit(downId);
+
+ if (dispatchedIdBits.count() == 1) {
+ // First pointer is going down. Set down time.
+ mDownTime = when;
+ }
+
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
+ metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
+ downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+ }
+}
+
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
+ if (mSentHoverEnter &&
+ (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
+ !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+ mLastCookedState.buttonState, 0,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex,
+ mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
+ mOrientedYPrecision, mDownTime);
+ mSentHoverEnter = false;
+ }
+}
+
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
+ if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
+ !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ if (!mSentHoverEnter) {
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
+ metaState, mCurrentRawState.buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ mSentHoverEnter = true;
+ }
+
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ mCurrentRawState.buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+}
+
+void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
+ BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
+ const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
+ const int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mLastCookedState.buttonState;
+ while (!releasedButtons.isEmpty()) {
+ int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
+ buttonState &= ~actionButton;
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ actionButton, 0, metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+}
+
+void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
+ BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
+ const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
+ const int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mLastCookedState.buttonState;
+ while (!pressedButtons.isEmpty()) {
+ int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
+ buttonState |= actionButton;
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
+ 0, metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+ }
+}
+
+const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
+ if (!cookedPointerData.touchingIdBits.isEmpty()) {
+ return cookedPointerData.touchingIdBits;
+ }
+ return cookedPointerData.hoveringIdBits;
+}
+
+void TouchInputMapper::cookPointerData() {
+ uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
+
+ mCurrentCookedState.cookedPointerData.clear();
+ mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
+ mCurrentCookedState.cookedPointerData.hoveringIdBits =
+ mCurrentRawState.rawPointerData.hoveringIdBits;
+ mCurrentCookedState.cookedPointerData.touchingIdBits =
+ mCurrentRawState.rawPointerData.touchingIdBits;
+
+ if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
+ mCurrentCookedState.buttonState = 0;
+ } else {
+ mCurrentCookedState.buttonState = mCurrentRawState.buttonState;
+ }
+
+ // Walk through the the active pointers and map device coordinates onto
+ // surface coordinates and adjust for display orientation.
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
+
+ // Size
+ float touchMajor, touchMinor, toolMajor, toolMinor, size;
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+ case Calibration::SIZE_CALIBRATION_DIAMETER:
+ case Calibration::SIZE_CALIBRATION_BOX:
+ case Calibration::SIZE_CALIBRATION_AREA:
+ if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
+ touchMajor = in.touchMajor;
+ touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+ toolMajor = in.toolMajor;
+ toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.touchMajor.valid) {
+ toolMajor = touchMajor = in.touchMajor;
+ toolMinor = touchMinor =
+ mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+ size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.toolMajor.valid) {
+ touchMajor = toolMajor = in.toolMajor;
+ touchMinor = toolMinor =
+ mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
+ } else {
+ ALOG_ASSERT(false,
+ "No touch or tool axes. "
+ "Size calibration should have been resolved to NONE.");
+ touchMajor = 0;
+ touchMinor = 0;
+ toolMajor = 0;
+ toolMinor = 0;
+ size = 0;
+ }
+
+ if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
+ uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count();
+ if (touchingCount > 1) {
+ touchMajor /= touchingCount;
+ touchMinor /= touchingCount;
+ toolMajor /= touchingCount;
+ toolMinor /= touchingCount;
+ size /= touchingCount;
+ }
+ }
+
+ if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
+ touchMajor *= mGeometricScale;
+ touchMinor *= mGeometricScale;
+ toolMajor *= mGeometricScale;
+ toolMinor *= mGeometricScale;
+ } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
+ touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
+ touchMinor = touchMajor;
+ toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
+ toolMinor = toolMajor;
+ } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
+ touchMinor = touchMajor;
+ toolMinor = toolMajor;
+ }
+
+ mCalibration.applySizeScaleAndBias(&touchMajor);
+ mCalibration.applySizeScaleAndBias(&touchMinor);
+ mCalibration.applySizeScaleAndBias(&toolMajor);
+ mCalibration.applySizeScaleAndBias(&toolMinor);
+ size *= mSizeScale;
+ break;
+ default:
+ touchMajor = 0;
+ touchMinor = 0;
+ toolMajor = 0;
+ toolMinor = 0;
+ size = 0;
+ break;
+ }
+
+ // Pressure
+ float pressure;
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ pressure = in.pressure * mPressureScale;
+ break;
+ default:
+ pressure = in.isHovering ? 0 : 1;
+ break;
+ }
+
+ // Tilt and Orientation
+ float tilt;
+ float orientation;
+ if (mHaveTilt) {
+ float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
+ float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
+ orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
+ tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
+ } else {
+ tilt = 0;
+
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ orientation = in.orientation * mOrientationScale;
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+ int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
+ int32_t c2 = signExtendNybble(in.orientation & 0x0f);
+ if (c1 != 0 || c2 != 0) {
+ orientation = atan2f(c1, c2) * 0.5f;
+ float confidence = hypotf(c1, c2);
+ float scale = 1.0f + confidence / 16.0f;
+ touchMajor *= scale;
+ touchMinor /= scale;
+ toolMajor *= scale;
+ toolMinor /= scale;
+ } else {
+ orientation = 0;
+ }
+ break;
+ }
+ default:
+ orientation = 0;
+ }
+ }
+
+ // Distance
+ float distance;
+ switch (mCalibration.distanceCalibration) {
+ case Calibration::DISTANCE_CALIBRATION_SCALED:
+ distance = in.distance * mDistanceScale;
+ break;
+ default:
+ distance = 0;
+ }
+
+ // Coverage
+ int32_t rawLeft, rawTop, rawRight, rawBottom;
+ switch (mCalibration.coverageCalibration) {
+ case Calibration::COVERAGE_CALIBRATION_BOX:
+ rawLeft = (in.toolMinor & 0xffff0000) >> 16;
+ rawRight = in.toolMinor & 0x0000ffff;
+ rawBottom = in.toolMajor & 0x0000ffff;
+ rawTop = (in.toolMajor & 0xffff0000) >> 16;
+ break;
+ default:
+ rawLeft = rawTop = rawRight = rawBottom = 0;
+ break;
+ }
+
+ // Adjust X,Y coords for device calibration
+ // TODO: Adjust coverage coords?
+ float xTransformed = in.x, yTransformed = in.y;
+ mAffineTransform.applyTo(xTransformed, yTransformed);
+
+ // Adjust X, Y, and coverage coords for surface orientation.
+ float x, y;
+ float left, top, right, bottom;
+
+ switch (mSurfaceOrientation) {
+ case DISPLAY_ORIENTATION_90:
+ x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
+ left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
+ top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
+ orientation -= M_PI_2;
+ if (mOrientedRanges.haveOrientation &&
+ orientation < mOrientedRanges.orientation.min) {
+ orientation +=
+ (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ }
+ break;
+ case DISPLAY_ORIENTATION_180:
+ x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
+ y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
+ left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
+ right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
+ bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
+ top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
+ orientation -= M_PI;
+ if (mOrientedRanges.haveOrientation &&
+ orientation < mOrientedRanges.orientation.min) {
+ orientation +=
+ (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ }
+ break;
+ case DISPLAY_ORIENTATION_270:
+ x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
+ y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
+ right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
+ bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ orientation += M_PI_2;
+ if (mOrientedRanges.haveOrientation &&
+ orientation > mOrientedRanges.orientation.max) {
+ orientation -=
+ (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ }
+ break;
+ default:
+ x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ break;
+ }
+
+ // Write output coords.
+ PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
+ out.clear();
+ out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
+ out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
+ if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
+ out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
+ out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
+ out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
+ } else {
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
+ }
+
+ // Write output properties.
+ PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
+ uint32_t id = in.id;
+ properties.clear();
+ properties.id = id;
+ properties.toolType = in.toolType;
+
+ // Write id index.
+ mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
+ }
+}
+
+void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
+ PointerUsage pointerUsage) {
+ if (pointerUsage != mPointerUsage) {
+ abortPointerUsage(when, policyFlags);
+ mPointerUsage = pointerUsage;
+ }
+
+ switch (mPointerUsage) {
+ case POINTER_USAGE_GESTURES:
+ dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+ break;
+ case POINTER_USAGE_STYLUS:
+ dispatchPointerStylus(when, policyFlags);
+ break;
+ case POINTER_USAGE_MOUSE:
+ dispatchPointerMouse(when, policyFlags);
+ break;
+ default:
+ break;
+ }
+}
+
+void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
+ switch (mPointerUsage) {
+ case POINTER_USAGE_GESTURES:
+ abortPointerGestures(when, policyFlags);
+ break;
+ case POINTER_USAGE_STYLUS:
+ abortPointerStylus(when, policyFlags);
+ break;
+ case POINTER_USAGE_MOUSE:
+ abortPointerMouse(when, policyFlags);
+ break;
+ default:
+ break;
+ }
+
+ mPointerUsage = POINTER_USAGE_NONE;
+}
+
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
+ // Update current gesture coordinates.
+ bool cancelPreviousGesture, finishPreviousGesture;
+ bool sendEvents =
+ preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
+ if (!sendEvents) {
+ return;
+ }
+ if (finishPreviousGesture) {
+ cancelPreviousGesture = false;
+ }
+
+ // Update the pointer presentation and spots.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ if (finishPreviousGesture || cancelPreviousGesture) {
+ mPointerController->clearSpots();
+ }
+
+ if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits,
+ mPointerController->getDisplayId());
+ }
+ } else {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ }
+
+ // Show or hide the pointer if needed.
+ switch (mPointerGesture.currentGestureMode) {
+ case PointerGesture::NEUTRAL:
+ case PointerGesture::QUIET:
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH &&
+ mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
+ // Remind the user of where the pointer is after finishing a gesture with spots.
+ mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
+ }
+ break;
+ case PointerGesture::TAP:
+ case PointerGesture::TAP_DRAG:
+ case PointerGesture::BUTTON_CLICK_OR_DRAG:
+ case PointerGesture::HOVER:
+ case PointerGesture::PRESS:
+ case PointerGesture::SWIPE:
+ // Unfade the pointer when the current gesture manipulates the
+ // area directly under the pointer.
+ mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ break;
+ case PointerGesture::FREEFORM:
+ // Fade the pointer when the current gesture manipulates a different
+ // area and there are spots to guide the user experience.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ } else {
+ mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ }
+ break;
+ }
+
+ // Send events!
+ int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mCurrentCookedState.buttonState;
+
+ // Update last coordinates of pointers that have moved so that we observe the new
+ // pointer positions at the same time as other pointers that have just gone up.
+ bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP ||
+ mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::SWIPE ||
+ mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+ bool moveNeeded = false;
+ if (down && !cancelPreviousGesture && !finishPreviousGesture &&
+ !mPointerGesture.lastGestureIdBits.isEmpty() &&
+ !mPointerGesture.currentGestureIdBits.isEmpty()) {
+ BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value &
+ mPointerGesture.lastGestureIdBits.value);
+ moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.lastGestureProperties,
+ mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex, movedGestureIdBits);
+ if (buttonState != mLastCookedState.buttonState) {
+ moveNeeded = true;
+ }
+ }
+
+ // Send motion events for all pointers that went up or were canceled.
+ BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
+ if (!dispatchedGestureIdBits.isEmpty()) {
+ if (cancelPreviousGesture) {
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
+ mPointerGesture.downTime);
+
+ dispatchedGestureIdBits.clear();
+ } else {
+ BitSet32 upGestureIdBits;
+ if (finishPreviousGesture) {
+ upGestureIdBits = dispatchedGestureIdBits;
+ } else {
+ upGestureIdBits.value =
+ dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value;
+ }
+ while (!upGestureIdBits.isEmpty()) {
+ uint32_t id = upGestureIdBits.clearFirstMarkedBit();
+
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties,
+ mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
+ 0, mPointerGesture.downTime);
+
+ dispatchedGestureIdBits.clearBit(id);
+ }
+ }
+ }
+
+ // Send motion events for all pointers that moved.
+ if (moveNeeded) {
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
+ mPointerGesture.downTime);
+ }
+
+ // Send motion events for all pointers that went down.
+ if (down) {
+ BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value &
+ ~dispatchedGestureIdBits.value);
+ while (!downGestureIdBits.isEmpty()) {
+ uint32_t id = downGestureIdBits.clearFirstMarkedBit();
+ dispatchedGestureIdBits.markBit(id);
+
+ if (dispatchedGestureIdBits.count() == 1) {
+ mPointerGesture.downTime = when;
+ }
+
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
+ metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
+ 0, mPointerGesture.downTime);
+ }
+ }
+
+ // Send motion events for hover.
+ if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
+ } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
+ // Synthesize a hover move event after all pointers go up to indicate that
+ // the pointer is hovering again even if the user is not currently touching
+ // the touch pad. This ensures that a view will receive a fresh hover enter
+ // event after a tap.
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+ const int32_t displayId = mPointerController->getDisplayId();
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ // Update state.
+ mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
+ if (!down) {
+ mPointerGesture.lastGestureIdBits.clear();
+ } else {
+ mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
+ for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ mPointerGesture.lastGestureProperties[index].copyFrom(
+ mPointerGesture.currentGestureProperties[index]);
+ mPointerGesture.lastGestureCoords[index].copyFrom(
+ mPointerGesture.currentGestureCoords[index]);
+ mPointerGesture.lastGestureIdToIndex[id] = index;
+ }
+ }
+}
+
+void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
+ // Cancel previously dispatches pointers.
+ if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t buttonState = mCurrentRawState.buttonState;
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
+ 0, 0, mPointerGesture.downTime);
+ }
+
+ // Reset the current pointer gesture.
+ mPointerGesture.reset();
+ mPointerVelocityControl.reset();
+
+ // Remove any current spots.
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->clearSpots();
+ }
+}
+
+bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
+ bool* outFinishPreviousGesture, bool isTimeout) {
+ *outCancelPreviousGesture = false;
+ *outFinishPreviousGesture = false;
+
+ // Handle TAP timeout.
+ if (isTimeout) {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Processing timeout");
+#endif
+
+ if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
+ // The tap/drag timeout has not yet expired.
+ getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime +
+ mConfig.pointerGestureTapDragInterval);
+ } else {
+ // The tap is finished.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: TAP finished");
+#endif
+ *outFinishPreviousGesture = true;
+
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureIdBits.clear();
+
+ mPointerVelocityControl.reset();
+ return true;
+ }
+ }
+
+ // We did not handle this timeout.
+ return false;
+ }
+
+ const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+ const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
+
+ // Update the velocity tracker.
+ {
+ VelocityTracker::Position positions[MAX_POINTERS];
+ uint32_t count = 0;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(id);
+ positions[count].x = pointer.x * mPointerXMovementScale;
+ positions[count].y = pointer.y * mPointerYMovementScale;
+ }
+ mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
+ positions);
+ }
+
+ // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
+ // to NEUTRAL, then we should not generate tap event.
+ if (mPointerGesture.lastGestureMode != PointerGesture::HOVER &&
+ mPointerGesture.lastGestureMode != PointerGesture::TAP &&
+ mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+ mPointerGesture.resetTap();
+ }
+
+ // Pick a new active touch id if needed.
+ // Choose an arbitrary pointer that just went down, if there is one.
+ // Otherwise choose an arbitrary remaining pointer.
+ // This guarantees we always have an active touch id when there is at least one pointer.
+ // We keep the same active touch id for as long as possible.
+ int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
+ int32_t activeTouchId = lastActiveTouchId;
+ if (activeTouchId < 0) {
+ if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
+ activeTouchId = mPointerGesture.activeTouchId =
+ mCurrentCookedState.fingerIdBits.firstMarkedBit();
+ mPointerGesture.firstTouchTime = when;
+ }
+ } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
+ if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
+ activeTouchId = mPointerGesture.activeTouchId =
+ mCurrentCookedState.fingerIdBits.firstMarkedBit();
+ } else {
+ activeTouchId = mPointerGesture.activeTouchId = -1;
+ }
+ }
+
+ // Determine whether we are in quiet time.
+ bool isQuietTime = false;
+ if (activeTouchId < 0) {
+ mPointerGesture.resetQuietTime();
+ } else {
+ isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
+ if (!isQuietTime) {
+ if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
+ mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
+ mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) &&
+ currentFingerCount < 2) {
+ // Enter quiet time when exiting swipe or freeform state.
+ // This is to prevent accidentally entering the hover state and flinging the
+ // pointer when finishing a swipe and there is still one pointer left onscreen.
+ isQuietTime = true;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG &&
+ currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
+ // Enter quiet time when releasing the button and there are still two or more
+ // fingers down. This may indicate that one finger was used to press the button
+ // but it has not gone up yet.
+ isQuietTime = true;
+ }
+ if (isQuietTime) {
+ mPointerGesture.quietTime = when;
+ }
+ }
+ }
+
+ // Switch states based on button and pointer state.
+ if (isQuietTime) {
+ // Case 1: Quiet time. (QUIET)
+#if DEBUG_GESTURES
+ ALOGD("Gestures: QUIET for next %0.3fms",
+ (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
+#endif
+ if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+ *outFinishPreviousGesture = true;
+ }
+
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+ mPointerGesture.currentGestureIdBits.clear();
+
+ mPointerVelocityControl.reset();
+ } else if (isPointerDown(mCurrentRawState.buttonState)) {
+ // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
+ // The pointer follows the active touch point.
+ // Emit DOWN, MOVE, UP events at the pointer location.
+ //
+ // Only the active touch matters; other fingers are ignored. This policy helps
+ // to handle the case where the user places a second finger on the touch pad
+ // to apply the necessary force to depress an integrated button below the surface.
+ // We don't want the second finger to be delivered to applications.
+ //
+ // For this to work well, we need to make sure to track the pointer that is really
+ // active. If the user first puts one finger down to click then adds another
+ // finger to drag then the active pointer should switch to the finger that is
+ // being dragged.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+ "currentFingerCount=%d",
+ activeTouchId, currentFingerCount);
+#endif
+ // Reset state when just starting.
+ if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+ *outFinishPreviousGesture = true;
+ mPointerGesture.activeGestureId = 0;
+ }
+
+ // Switch pointers if needed.
+ // Find the fastest pointer and follow it.
+ if (activeTouchId >= 0 && currentFingerCount > 1) {
+ int32_t bestId = -1;
+ float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ float vx, vy;
+ if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
+ float speed = hypotf(vx, vy);
+ if (speed > bestSpeed) {
+ bestId = id;
+ bestSpeed = speed;
+ }
+ }
+ }
+ if (bestId >= 0 && bestId != activeTouchId) {
+ mPointerGesture.activeTouchId = activeTouchId = bestId;
+#if DEBUG_GESTURES
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+ "bestId=%d, bestSpeed=%0.3f",
+ bestId, bestSpeed);
+#endif
+ }
+ }
+
+ float deltaX = 0, deltaY = 0;
+ if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
+ const RawPointerData::Pointer& currentPointer =
+ mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
+ const RawPointerData::Pointer& lastPointer =
+ mLastRawState.rawPointerData.pointerForId(activeTouchId);
+ deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
+ deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
+
+ rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+ // Move the pointer using a relative motion.
+ // When using spots, the click will occur at the position of the anchor
+ // spot and all other spots will move there.
+ mPointerController->move(deltaX, deltaY);
+ } else {
+ mPointerVelocityControl.reset();
+ }
+
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ } else if (currentFingerCount == 0) {
+ // Case 3. No fingers down and button is not pressed. (NEUTRAL)
+ if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+ *outFinishPreviousGesture = true;
+ }
+
+ // Watch for taps coming out of HOVER or TAP_DRAG mode.
+ // Checking for taps after TAP_DRAG allows us to detect double-taps.
+ bool tapped = false;
+ if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
+ mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
+ lastFingerCount == 1) {
+ if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+ if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+ fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: TAP");
+#endif
+
+ mPointerGesture.tapUpTime = when;
+ getContext()->requestTimeoutAtTime(when +
+ mConfig.pointerGestureTapDragInterval);
+
+ mPointerGesture.activeGestureId = 0;
+ mPointerGesture.currentGestureMode = PointerGesture::TAP;
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id =
+ mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType =
+ AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.tapX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.tapY);
+ mPointerGesture.currentGestureCoords[0]
+ .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+ tapped = true;
+ } else {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
+ y - mPointerGesture.tapY);
+#endif
+ }
+ } else {
+#if DEBUG_GESTURES
+ if (mPointerGesture.tapDownTime != LLONG_MIN) {
+ ALOGD("Gestures: Not a TAP, %0.3fms since down",
+ (when - mPointerGesture.tapDownTime) * 0.000001f);
+ } else {
+ ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ }
+#endif
+ }
+ }
+
+ mPointerVelocityControl.reset();
+
+ if (!tapped) {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: NEUTRAL");
+#endif
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureIdBits.clear();
+ }
+ } else if (currentFingerCount == 1) {
+ // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
+ // The pointer follows the active touch point.
+ // When in HOVER, emit HOVER_MOVE events at the pointer location.
+ // When in TAP_DRAG, emit MOVE events at the pointer location.
+ ALOG_ASSERT(activeTouchId >= 0);
+
+ mPointerGesture.currentGestureMode = PointerGesture::HOVER;
+ if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+ if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+ fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+ mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ } else {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+ x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+#endif
+ }
+ } else {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+ (when - mPointerGesture.tapUpTime) * 0.000001f);
+#endif
+ }
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
+ mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ }
+
+ float deltaX = 0, deltaY = 0;
+ if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
+ const RawPointerData::Pointer& currentPointer =
+ mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
+ const RawPointerData::Pointer& lastPointer =
+ mLastRawState.rawPointerData.pointerForId(activeTouchId);
+ deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
+ deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
+
+ rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+ // Move the pointer using a relative motion.
+ // When using spots, the hover or drag will occur at the position of the anchor spot.
+ mPointerController->move(deltaX, deltaY);
+ } else {
+ mPointerVelocityControl.reset();
+ }
+
+ bool down;
+ if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: TAP_DRAG");
+#endif
+ down = true;
+ } else {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: HOVER");
+#endif
+ if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+ *outFinishPreviousGesture = true;
+ }
+ mPointerGesture.activeGestureId = 0;
+ down = false;
+ }
+
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ down ? 1.0f : 0.0f);
+
+ if (lastFingerCount == 0 && currentFingerCount != 0) {
+ mPointerGesture.resetTap();
+ mPointerGesture.tapDownTime = when;
+ mPointerGesture.tapX = x;
+ mPointerGesture.tapY = y;
+ }
+ } else {
+ // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
+ // We need to provide feedback for each finger that goes down so we cannot wait
+ // for the fingers to move before deciding what to do.
+ //
+ // The ambiguous case is deciding what to do when there are two fingers down but they
+ // have not moved enough to determine whether they are part of a drag or part of a
+ // freeform gesture, or just a press or long-press at the pointer location.
+ //
+ // When there are two fingers we start with the PRESS hypothesis and we generate a
+ // down at the pointer location.
+ //
+ // When the two fingers move enough or when additional fingers are added, we make
+ // a decision to transition into SWIPE or FREEFORM mode accordingly.
+ ALOG_ASSERT(activeTouchId >= 0);
+
+ bool settled = when >=
+ mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
+ if (mPointerGesture.lastGestureMode != PointerGesture::PRESS &&
+ mPointerGesture.lastGestureMode != PointerGesture::SWIPE &&
+ mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ *outFinishPreviousGesture = true;
+ } else if (!settled && currentFingerCount > lastFingerCount) {
+ // Additional pointers have gone down but not yet settled.
+ // Reset the gesture.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+#endif
+ *outCancelPreviousGesture = true;
+ } else {
+ // Continue previous gesture.
+ mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+ }
+
+ if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
+ mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.activeGestureId = 0;
+ mPointerGesture.referenceIdBits.clear();
+ mPointerVelocityControl.reset();
+
+ // Use the centroid and pointer location as the reference points for the gesture.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+#endif
+ mCurrentRawState.rawPointerData
+ .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
+ &mPointerGesture.referenceTouchY);
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ // Clear the reference deltas for fingers not yet included in the reference calculation.
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
+ ~mPointerGesture.referenceIdBits.value);
+ !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ mPointerGesture.referenceDeltas[id].dx = 0;
+ mPointerGesture.referenceDeltas[id].dy = 0;
+ }
+ mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
+
+ // Add delta for all fingers and calculate a common movement delta.
+ float commonDeltaX = 0, commonDeltaY = 0;
+ BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
+ mCurrentCookedState.fingerIdBits.value);
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
+ const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx += cpd.x - lpd.x;
+ delta.dy += cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaX = delta.dx;
+ commonDeltaY = delta.dy;
+ } else {
+ commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+ commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
+ }
+ }
+
+ // Consider transitions from PRESS to SWIPE or MULTITOUCH.
+ if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ float dist[MAX_POINTER_ID + 1];
+ int32_t distOverThreshold = 0;
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
+ if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
+ distOverThreshold += 1;
+ }
+ }
+
+ // Only transition when at least two pointers have moved further than
+ // the minimum distance threshold.
+ if (distOverThreshold >= 2) {
+ if (currentFingerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else {
+ // There are exactly two pointers.
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ uint32_t id1 = idBits.clearFirstMarkedBit();
+ uint32_t id2 = idBits.firstMarkedBit();
+ const RawPointerData::Pointer& p1 =
+ mCurrentRawState.rawPointerData.pointerForId(id1);
+ const RawPointerData::Pointer& p2 =
+ mCurrentRawState.rawPointerData.pointerForId(id2);
+ float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
+ if (mutualDistance > mPointerGestureMaxSwipeWidth) {
+ // There are two pointers but they are too far apart for a SWIPE,
+ // switch to FREEFORM.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ mutualDistance, mPointerGestureMaxSwipeWidth);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ float dist1 = dist[id1];
+ float dist2 = dist[id2];
+ if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
+ dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
+ // Calculate the dot product of the displacement vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) *
+ // mag(v2).
+ PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+ PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+ float dx1 = delta1.dx * mPointerXZoomScale;
+ float dy1 = delta1.dy * mPointerYZoomScale;
+ float dx2 = delta2.dx * mPointerXZoomScale;
+ float dy2 = delta2.dy * mPointerYZoomScale;
+ float dot = dx1 * dx2 + dy1 * dy2;
+ float cosine = dot / (dist1 * dist2); // denominator always > 0
+ if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+#endif
+ mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ }
+ }
+ }
+ }
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ // Switch from SWIPE to FREEFORM if additional pointers go down.
+ // Cancel previous gesture.
+ if (currentFingerCount > 2) {
+#if DEBUG_GESTURES
+ ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ }
+ }
+
+ // Move the reference points based on the overall group motion of the fingers
+ // except in PRESS mode while waiting for a transition to occur.
+ if (mPointerGesture.currentGestureMode != PointerGesture::PRESS &&
+ (commonDeltaX || commonDeltaY)) {
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx = 0;
+ delta.dy = 0;
+ }
+
+ mPointerGesture.referenceTouchX += commonDeltaX;
+ mPointerGesture.referenceTouchY += commonDeltaY;
+
+ commonDeltaX *= mPointerXMovementScale;
+ commonDeltaY *= mPointerYMovementScale;
+
+ rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
+ mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
+
+ mPointerGesture.referenceGestureX += commonDeltaX;
+ mPointerGesture.referenceGestureY += commonDeltaY;
+ }
+
+ // Report gestures.
+ if (mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ // PRESS or SWIPE mode.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+#endif
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ // FREEFORM mode.
+#if DEBUG_GESTURES
+ ALOGD("Gestures: FREEFORM activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+#endif
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+
+ BitSet32 mappedTouchIdBits;
+ BitSet32 usedGestureIdBits;
+ if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ // Initially, assign the active gesture id to the active touch point
+ // if there is one. No other touch id bits are mapped yet.
+ if (!*outCancelPreviousGesture) {
+ mappedTouchIdBits.markBit(activeTouchId);
+ usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
+ mPointerGesture.activeGestureId;
+ } else {
+ mPointerGesture.activeGestureId = -1;
+ }
+ } else {
+ // Otherwise, assume we mapped all touches from the previous frame.
+ // Reuse all mappings that are still applicable.
+ mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value &
+ mCurrentCookedState.fingerIdBits.value;
+ usedGestureIdBits = mPointerGesture.lastGestureIdBits;
+
+ // Check whether we need to choose a new active gesture id because the
+ // current went went up.
+ for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
+ ~mCurrentCookedState.fingerIdBits.value);
+ !upTouchIdBits.isEmpty();) {
+ uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
+ uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
+ if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
+ mPointerGesture.activeGestureId = -1;
+ break;
+ }
+ }
+ }
+
+#if DEBUG_GESTURES
+ ALOGD("Gestures: FREEFORM follow up "
+ "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value,
+ mPointerGesture.activeGestureId);
+#endif
+
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ for (uint32_t i = 0; i < currentFingerCount; i++) {
+ uint32_t touchId = idBits.clearFirstMarkedBit();
+ uint32_t gestureId;
+ if (!mappedTouchIdBits.hasBit(touchId)) {
+ gestureId = usedGestureIdBits.markFirstUnmarkedBit();
+ mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
+#if DEBUG_GESTURES
+ ALOGD("Gestures: FREEFORM "
+ "new mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+#endif
+ } else {
+ gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
+#if DEBUG_GESTURES
+ ALOGD("Gestures: FREEFORM "
+ "existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+#endif
+ }
+ mPointerGesture.currentGestureIdBits.markBit(gestureId);
+ mPointerGesture.currentGestureIdToIndex[gestureId] = i;
+
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(touchId);
+ float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
+ float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
+ rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+
+ mPointerGesture.currentGestureProperties[i].clear();
+ mPointerGesture.currentGestureProperties[i].id = gestureId;
+ mPointerGesture.currentGestureProperties[i].toolType =
+ AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[i].clear();
+ mPointerGesture.currentGestureCoords[i]
+ .setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX + deltaX);
+ mPointerGesture.currentGestureCoords[i]
+ .setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY + deltaY);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ 1.0f);
+ }
+
+ if (mPointerGesture.activeGestureId < 0) {
+ mPointerGesture.activeGestureId =
+ mPointerGesture.currentGestureIdBits.firstMarkedBit();
+#if DEBUG_GESTURES
+ ALOGD("Gestures: FREEFORM new "
+ "activeGestureId=%d",
+ mPointerGesture.activeGestureId);
+#endif
+ }
+ }
+ }
+
+ mPointerController->setButtonState(mCurrentRawState.buttonState);
+
+#if DEBUG_GESTURES
+ ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+ "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+ "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+ toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+ mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+ mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+ for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+ ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+ for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+ ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+#endif
+ return true;
+}
+
+void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
+ mPointerSimple.currentCoords.clear();
+ mPointerSimple.currentProperties.clear();
+
+ bool down, hovering;
+ if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+ uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
+ uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
+ float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
+ float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
+ mPointerController->setPosition(x, y);
+
+ hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
+ down = !hovering;
+
+ mPointerController->getPosition(&x, &y);
+ mPointerSimple.currentCoords.copyFrom(
+ mCurrentCookedState.cookedPointerData.pointerCoords[index]);
+ mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerSimple.currentProperties.id = 0;
+ mPointerSimple.currentProperties.toolType =
+ mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType;
+ } else {
+ down = false;
+ hovering = false;
+ }
+
+ dispatchPointerSimple(when, policyFlags, down, hovering);
+}
+
+void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
+ abortPointerSimple(when, policyFlags);
+}
+
+void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
+ mPointerSimple.currentCoords.clear();
+ mPointerSimple.currentProperties.clear();
+
+ bool down, hovering;
+ if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+ uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
+ uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+ float deltaX = 0, deltaY = 0;
+ if (mLastCookedState.mouseIdBits.hasBit(id)) {
+ uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+ deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x -
+ mLastRawState.rawPointerData.pointers[lastIndex].x) *
+ mPointerXMovementScale;
+ deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y -
+ mLastRawState.rawPointerData.pointers[lastIndex].y) *
+ mPointerYMovementScale;
+
+ rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+ mPointerController->move(deltaX, deltaY);
+ } else {
+ mPointerVelocityControl.reset();
+ }
+
+ down = isPointerDown(mCurrentRawState.buttonState);
+ hovering = !down;
+
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+ mPointerSimple.currentCoords.copyFrom(
+ mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
+ mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ hovering ? 0.0f : 1.0f);
+ mPointerSimple.currentProperties.id = 0;
+ mPointerSimple.currentProperties.toolType =
+ mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
+ } else {
+ mPointerVelocityControl.reset();
+
+ down = false;
+ hovering = false;
+ }
+
+ dispatchPointerSimple(when, policyFlags, down, hovering);
+}
+
+void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
+ abortPointerSimple(when, policyFlags);
+
+ mPointerVelocityControl.reset();
+}
+
+void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
+ bool hovering) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t displayId = mViewport.displayId;
+
+ if (down || hovering) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ mPointerController->clearSpots();
+ mPointerController->setButtonState(mCurrentRawState.buttonState);
+ mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ }
+ displayId = mPointerController->getDisplayId();
+
+ float xCursorPosition;
+ float yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ if (mPointerSimple.down && !down) {
+ mPointerSimple.down = false;
+
+ // Send up.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+ mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+ xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ if (mPointerSimple.hovering && !hovering) {
+ mPointerSimple.hovering = false;
+
+ // Send hover exit.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+ metaState, mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+ xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ if (down) {
+ if (!mPointerSimple.down) {
+ mPointerSimple.down = true;
+ mPointerSimple.downTime = when;
+
+ // Send down.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ // Send move.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ if (hovering) {
+ if (!mPointerSimple.hovering) {
+ mPointerSimple.hovering = true;
+
+ // Send hover enter.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
+ metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ // Send hover move.
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
+ float vscroll = mCurrentRawState.rawVScroll;
+ float hscroll = mCurrentRawState.rawHScroll;
+ mWheelYVelocityControl.move(when, nullptr, &vscroll);
+ mWheelXVelocityControl.move(when, &hscroll, nullptr);
+
+ // Send scroll.
+ PointerCoords pointerCoords;
+ pointerCoords.copyFrom(mPointerSimple.currentCoords);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+ &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
+ xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {});
+ getListener()->notifyMotion(&args);
+ }
+
+ // Save state.
+ if (down || hovering) {
+ mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
+ mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ } else {
+ mPointerSimple.reset();
+ }
+}
+
+void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
+ mPointerSimple.currentCoords.clear();
+ mPointerSimple.currentProperties.clear();
+
+ dispatchPointerSimple(when, policyFlags, false, false);
+}
+
+void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
+ int32_t action, int32_t actionButton, int32_t flags,
+ int32_t metaState, int32_t buttonState, int32_t edgeFlags,
+ const PointerProperties* properties,
+ const PointerCoords* coords, const uint32_t* idToIndex,
+ BitSet32 idBits, int32_t changedId, float xPrecision,
+ float yPrecision, nsecs_t downTime) {
+ PointerCoords pointerCoords[MAX_POINTERS];
+ PointerProperties pointerProperties[MAX_POINTERS];
+ uint32_t pointerCount = 0;
+ while (!idBits.isEmpty()) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = idToIndex[id];
+ pointerProperties[pointerCount].copyFrom(properties[index]);
+ pointerCoords[pointerCount].copyFrom(coords[index]);
+
+ if (changedId >= 0 && id == uint32_t(changedId)) {
+ action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+
+ pointerCount += 1;
+ }
+
+ ALOG_ASSERT(pointerCount != 0);
+
+ if (changedId >= 0 && pointerCount == 1) {
+ // Replace initial down and final up action.
+ // We can compare the action without masking off the changed pointer index
+ // because we know the index is 0.
+ if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ action = AMOTION_EVENT_ACTION_DOWN;
+ } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
+ action = AMOTION_EVENT_ACTION_UP;
+ } else {
+ // Can't happen.
+ ALOG_ASSERT(false);
+ }
+ }
+ float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+ float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ }
+ const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
+ const int32_t deviceId = getDeviceId();
+ std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+ std::for_each(frames.begin(), frames.end(),
+ [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId,
+ policyFlags, action, actionButton, flags, metaState, buttonState,
+ MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+ pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+ downTime, std::move(frames));
+ getListener()->notifyMotion(&args);
+}
+
+bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
+ const PointerCoords* inCoords,
+ const uint32_t* inIdToIndex,
+ PointerProperties* outProperties,
+ PointerCoords* outCoords, const uint32_t* outIdToIndex,
+ BitSet32 idBits) const {
+ bool changed = false;
+ while (!idBits.isEmpty()) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t inIndex = inIdToIndex[id];
+ uint32_t outIndex = outIdToIndex[id];
+
+ const PointerProperties& curInProperties = inProperties[inIndex];
+ const PointerCoords& curInCoords = inCoords[inIndex];
+ PointerProperties& curOutProperties = outProperties[outIndex];
+ PointerCoords& curOutCoords = outCoords[outIndex];
+
+ if (curInProperties != curOutProperties) {
+ curOutProperties.copyFrom(curInProperties);
+ changed = true;
+ }
+
+ if (curInCoords != curOutCoords) {
+ curOutCoords.copyFrom(curInCoords);
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+void TouchInputMapper::fadePointer() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ }
+}
+
+void TouchInputMapper::cancelTouch(nsecs_t when) {
+ abortPointerUsage(when, 0 /*policyFlags*/);
+ abortTouches(when, 0 /* policyFlags*/);
+}
+
+bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+ const float scaledX = x * mXScale;
+ const float scaledY = y * mYScale;
+ return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
+ scaledX >= mSurfaceLeft && scaledX <= mSurfaceLeft + mSurfaceWidth &&
+ y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
+ scaledY >= mSurfaceTop && scaledY <= mSurfaceTop + mSurfaceHeight;
+}
+
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
+#if DEBUG_VIRTUAL_KEYS
+ ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop,
+ virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+ if (virtualKey.isHit(x, y)) {
+ return &virtualKey;
+ }
+ }
+
+ return nullptr;
+}
+
+void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
+ uint32_t currentPointerCount = current->rawPointerData.pointerCount;
+ uint32_t lastPointerCount = last->rawPointerData.pointerCount;
+
+ current->rawPointerData.clearIdBits();
+
+ if (currentPointerCount == 0) {
+ // No pointers to assign.
+ return;
+ }
+
+ if (lastPointerCount == 0) {
+ // All pointers are new.
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ uint32_t id = i;
+ current->rawPointerData.pointers[i].id = id;
+ current->rawPointerData.idToIndex[id] = i;
+ current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
+ }
+ return;
+ }
+
+ if (currentPointerCount == 1 && lastPointerCount == 1 &&
+ current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) {
+ // Only one pointer and no change in count so it must have the same id as before.
+ uint32_t id = last->rawPointerData.pointers[0].id;
+ current->rawPointerData.pointers[0].id = id;
+ current->rawPointerData.idToIndex[id] = 0;
+ current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
+ return;
+ }
+
+ // General case.
+ // We build a heap of squared euclidean distances between current and last pointers
+ // associated with the current and last pointer indices. Then, we find the best
+ // match (by distance) for each current pointer.
+ // The pointers must have the same tool type but it is possible for them to
+ // transition from hovering to touching or vice-versa while retaining the same id.
+ PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+ uint32_t heapSize = 0;
+ for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+ currentPointerIndex++) {
+ for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+ lastPointerIndex++) {
+ const RawPointerData::Pointer& currentPointer =
+ current->rawPointerData.pointers[currentPointerIndex];
+ const RawPointerData::Pointer& lastPointer =
+ last->rawPointerData.pointers[lastPointerIndex];
+ if (currentPointer.toolType == lastPointer.toolType) {
+ int64_t deltaX = currentPointer.x - lastPointer.x;
+ int64_t deltaY = currentPointer.y - lastPointer.y;
+
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+ // Insert new element into the heap (sift up).
+ heap[heapSize].currentPointerIndex = currentPointerIndex;
+ heap[heapSize].lastPointerIndex = lastPointerIndex;
+ heap[heapSize].distance = distance;
+ heapSize += 1;
+ }
+ }
+ }
+
+ // Heapify
+ for (uint32_t startIndex = heapSize / 2; startIndex != 0;) {
+ startIndex -= 1;
+ for (uint32_t parentIndex = startIndex;;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize &&
+ heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+ heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ }
+#endif
+
+ // Pull matches out by increasing order of distance.
+ // To avoid reassigning pointers that have already been matched, the loop keeps track
+ // of which last and current pointers have been matched using the matchedXXXBits variables.
+ // It also tracks the used pointer id bits.
+ BitSet32 matchedLastBits(0);
+ BitSet32 matchedCurrentBits(0);
+ BitSet32 usedIdBits(0);
+ bool first = true;
+ for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
+ while (heapSize > 0) {
+ if (first) {
+ // The first time through the loop, we just consume the root element of
+ // the heap (the one with smallest distance).
+ first = false;
+ } else {
+ // Previous iterations consumed the root element of the heap.
+ // Pop root element off of the heap (sift down).
+ heap[0] = heap[heapSize];
+ for (uint32_t parentIndex = 0;;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize &&
+ heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+ heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ }
+#endif
+ }
+
+ heapSize -= 1;
+
+ uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+ if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+ uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+ if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+ matchedCurrentBits.markBit(currentPointerIndex);
+ matchedLastBits.markBit(lastPointerIndex);
+
+ uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
+ current->rawPointerData.pointers[currentPointerIndex].id = id;
+ current->rawPointerData.idToIndex[id] = currentPointerIndex;
+ current->rawPointerData.markIdBit(id,
+ current->rawPointerData.isHovering(
+ currentPointerIndex));
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+ ", distance=%" PRIu64,
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+ break;
+ }
+ }
+
+ // Assign fresh ids to pointers that were not matched in the process.
+ for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
+ uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
+ uint32_t id = usedIdBits.markFirstUnmarkedBit();
+
+ current->rawPointerData.pointers[currentPointerIndex].id = id;
+ current->rawPointerData.idToIndex[id] = currentPointerIndex;
+ current->rawPointerData.markIdBit(id,
+ current->rawPointerData.isHovering(currentPointerIndex));
+
+#if DEBUG_POINTER_ASSIGNMENT
+ ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
+#endif
+ }
+}
+
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
+ if (virtualKey.keyCode == keyCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
+ if (virtualKey.scanCode == scanCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
+ for (size_t i = 0; i < numCodes; i++) {
+ if (virtualKey.keyCode == keyCodes[i]) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+
+ return true;
+}
+
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ return std::make_optional(mViewport.displayId);
+ }
+ }
+ return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
new file mode 100644
index 0000000..89c017d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "EventHub.h"
+#include "InputMapper.h"
+#include "InputReaderBase.h"
+#include "TouchButtonAccumulator.h"
+
+#include <stdint.h>
+
+namespace android {
+
+/* Raw axis information from the driver. */
+struct RawPointerAxes {
+ RawAbsoluteAxisInfo x;
+ RawAbsoluteAxisInfo y;
+ RawAbsoluteAxisInfo pressure;
+ RawAbsoluteAxisInfo touchMajor;
+ RawAbsoluteAxisInfo touchMinor;
+ RawAbsoluteAxisInfo toolMajor;
+ RawAbsoluteAxisInfo toolMinor;
+ RawAbsoluteAxisInfo orientation;
+ RawAbsoluteAxisInfo distance;
+ RawAbsoluteAxisInfo tiltX;
+ RawAbsoluteAxisInfo tiltY;
+ RawAbsoluteAxisInfo trackingId;
+ RawAbsoluteAxisInfo slot;
+
+ RawPointerAxes();
+ inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+ inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
+ void clear();
+};
+
+/* Raw data for a collection of pointers including a pointer id mapping table. */
+struct RawPointerData {
+ struct Pointer {
+ uint32_t id;
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ int32_t touchMajor;
+ int32_t touchMinor;
+ int32_t toolMajor;
+ int32_t toolMinor;
+ int32_t orientation;
+ int32_t distance;
+ int32_t tiltX;
+ int32_t tiltY;
+ int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
+ bool isHovering;
+ };
+
+ uint32_t pointerCount;
+ Pointer pointers[MAX_POINTERS];
+ BitSet32 hoveringIdBits, touchingIdBits;
+ uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+ RawPointerData();
+ void clear();
+ void copyFrom(const RawPointerData& other);
+ void getCentroidOfTouchingPointers(float* outX, float* outY) const;
+
+ inline void markIdBit(uint32_t id, bool isHovering) {
+ if (isHovering) {
+ hoveringIdBits.markBit(id);
+ } else {
+ touchingIdBits.markBit(id);
+ }
+ }
+
+ inline void clearIdBits() {
+ hoveringIdBits.clear();
+ touchingIdBits.clear();
+ }
+
+ inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; }
+
+ inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; }
+};
+
+/* Cooked data for a collection of pointers including a pointer id mapping table. */
+struct CookedPointerData {
+ uint32_t pointerCount;
+ PointerProperties pointerProperties[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+ BitSet32 hoveringIdBits, touchingIdBits;
+ uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+ CookedPointerData();
+ void clear();
+ void copyFrom(const CookedPointerData& other);
+
+ inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
+ return pointerCoords[idToIndex[id]];
+ }
+
+ inline PointerCoords& editPointerCoordsWithId(uint32_t id) {
+ return pointerCoords[idToIndex[id]];
+ }
+
+ inline PointerProperties& editPointerPropertiesWithId(uint32_t id) {
+ return pointerProperties[idToIndex[id]];
+ }
+
+ inline bool isHovering(uint32_t pointerIndex) const {
+ return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
+ }
+
+ inline bool isTouching(uint32_t pointerIndex) const {
+ return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
+ }
+};
+
+class TouchInputMapper : public InputMapper {
+public:
+ explicit TouchInputMapper(InputDevice* device);
+ virtual ~TouchInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void dump(std::string& dump) override;
+ virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ virtual void reset(nsecs_t when) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) override;
+
+ virtual void fadePointer() override;
+ virtual void cancelTouch(nsecs_t when) override;
+ virtual void timeoutExpired(nsecs_t when) override;
+ virtual void updateExternalStylusState(const StylusState& state) override;
+ virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+protected:
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ CursorScrollAccumulator mCursorScrollAccumulator;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+
+ struct VirtualKey {
+ int32_t keyCode;
+ int32_t scanCode;
+ uint32_t flags;
+
+ // computed hit box, specified in touch screen coords based on known display size
+ int32_t hitLeft;
+ int32_t hitTop;
+ int32_t hitRight;
+ int32_t hitBottom;
+
+ inline bool isHit(int32_t x, int32_t y) const {
+ return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+ }
+ };
+
+ // Input sources and device mode.
+ uint32_t mSource;
+
+ enum DeviceMode {
+ DEVICE_MODE_DISABLED, // input is disabled
+ DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
+ DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
+ DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
+ DEVICE_MODE_POINTER, // pointer mapping (pointer)
+ };
+ DeviceMode mDeviceMode;
+
+ // The reader's configuration.
+ InputReaderConfiguration mConfig;
+
+ // Immutable configuration parameters.
+ struct Parameters {
+ enum DeviceType {
+ DEVICE_TYPE_TOUCH_SCREEN,
+ DEVICE_TYPE_TOUCH_PAD,
+ DEVICE_TYPE_TOUCH_NAVIGATION,
+ DEVICE_TYPE_POINTER,
+ };
+
+ DeviceType deviceType;
+ bool hasAssociatedDisplay;
+ bool associatedDisplayIsExternal;
+ bool orientationAware;
+ bool hasButtonUnderPad;
+ std::string uniqueDisplayId;
+
+ enum GestureMode {
+ GESTURE_MODE_SINGLE_TOUCH,
+ GESTURE_MODE_MULTI_TOUCH,
+ };
+ GestureMode gestureMode;
+
+ bool wake;
+ } mParameters;
+
+ // Immutable calibration parameters in parsed form.
+ struct Calibration {
+ // Size
+ enum SizeCalibration {
+ SIZE_CALIBRATION_DEFAULT,
+ SIZE_CALIBRATION_NONE,
+ SIZE_CALIBRATION_GEOMETRIC,
+ SIZE_CALIBRATION_DIAMETER,
+ SIZE_CALIBRATION_BOX,
+ SIZE_CALIBRATION_AREA,
+ };
+
+ SizeCalibration sizeCalibration;
+
+ bool haveSizeScale;
+ float sizeScale;
+ bool haveSizeBias;
+ float sizeBias;
+ bool haveSizeIsSummed;
+ bool sizeIsSummed;
+
+ // Pressure
+ enum PressureCalibration {
+ PRESSURE_CALIBRATION_DEFAULT,
+ PRESSURE_CALIBRATION_NONE,
+ PRESSURE_CALIBRATION_PHYSICAL,
+ PRESSURE_CALIBRATION_AMPLITUDE,
+ };
+
+ PressureCalibration pressureCalibration;
+ bool havePressureScale;
+ float pressureScale;
+
+ // Orientation
+ enum OrientationCalibration {
+ ORIENTATION_CALIBRATION_DEFAULT,
+ ORIENTATION_CALIBRATION_NONE,
+ ORIENTATION_CALIBRATION_INTERPOLATED,
+ ORIENTATION_CALIBRATION_VECTOR,
+ };
+
+ OrientationCalibration orientationCalibration;
+
+ // Distance
+ enum DistanceCalibration {
+ DISTANCE_CALIBRATION_DEFAULT,
+ DISTANCE_CALIBRATION_NONE,
+ DISTANCE_CALIBRATION_SCALED,
+ };
+
+ DistanceCalibration distanceCalibration;
+ bool haveDistanceScale;
+ float distanceScale;
+
+ enum CoverageCalibration {
+ COVERAGE_CALIBRATION_DEFAULT,
+ COVERAGE_CALIBRATION_NONE,
+ COVERAGE_CALIBRATION_BOX,
+ };
+
+ CoverageCalibration coverageCalibration;
+
+ inline void applySizeScaleAndBias(float* outSize) const {
+ if (haveSizeScale) {
+ *outSize *= sizeScale;
+ }
+ if (haveSizeBias) {
+ *outSize += sizeBias;
+ }
+ if (*outSize < 0) {
+ *outSize = 0;
+ }
+ }
+ } mCalibration;
+
+ // Affine location transformation/calibration
+ struct TouchAffineTransformation mAffineTransform;
+
+ RawPointerAxes mRawPointerAxes;
+
+ struct RawState {
+ nsecs_t when;
+
+ // Raw pointer sample data.
+ RawPointerData rawPointerData;
+
+ int32_t buttonState;
+
+ // Scroll state.
+ int32_t rawVScroll;
+ int32_t rawHScroll;
+
+ void copyFrom(const RawState& other) {
+ when = other.when;
+ rawPointerData.copyFrom(other.rawPointerData);
+ buttonState = other.buttonState;
+ rawVScroll = other.rawVScroll;
+ rawHScroll = other.rawHScroll;
+ }
+
+ void clear() {
+ when = 0;
+ rawPointerData.clear();
+ buttonState = 0;
+ rawVScroll = 0;
+ rawHScroll = 0;
+ }
+ };
+
+ struct CookedState {
+ // Cooked pointer sample data.
+ CookedPointerData cookedPointerData;
+
+ // Id bits used to differentiate fingers, stylus and mouse tools.
+ BitSet32 fingerIdBits;
+ BitSet32 stylusIdBits;
+ BitSet32 mouseIdBits;
+
+ int32_t buttonState;
+
+ void copyFrom(const CookedState& other) {
+ cookedPointerData.copyFrom(other.cookedPointerData);
+ fingerIdBits = other.fingerIdBits;
+ stylusIdBits = other.stylusIdBits;
+ mouseIdBits = other.mouseIdBits;
+ buttonState = other.buttonState;
+ }
+
+ void clear() {
+ cookedPointerData.clear();
+ fingerIdBits.clear();
+ stylusIdBits.clear();
+ mouseIdBits.clear();
+ buttonState = 0;
+ }
+ };
+
+ std::vector<RawState> mRawStatesPending;
+ RawState mCurrentRawState;
+ CookedState mCurrentCookedState;
+ RawState mLastRawState;
+ CookedState mLastCookedState;
+
+ // State provided by an external stylus
+ StylusState mExternalStylusState;
+ int64_t mExternalStylusId;
+ nsecs_t mExternalStylusFusionTimeout;
+ bool mExternalStylusDataPending;
+
+ // True if we sent a HOVER_ENTER event.
+ bool mSentHoverEnter;
+
+ // Have we assigned pointer IDs for this stream
+ bool mHavePointerIds;
+
+ // Is the current stream of direct touch events aborted
+ bool mCurrentMotionAborted;
+
+ // The time the primary pointer last went down.
+ nsecs_t mDownTime;
+
+ // The pointer controller, or null if the device is not a pointer.
+ sp<PointerControllerInterface> mPointerController;
+
+ std::vector<VirtualKey> mVirtualKeys;
+
+ virtual void configureParameters();
+ virtual void dumpParameters(std::string& dump);
+ virtual void configureRawPointerAxes();
+ virtual void dumpRawPointerAxes(std::string& dump);
+ virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
+ virtual void dumpSurface(std::string& dump);
+ virtual void configureVirtualKeys();
+ virtual void dumpVirtualKeys(std::string& dump);
+ virtual void parseCalibration();
+ virtual void resolveCalibration();
+ virtual void dumpCalibration(std::string& dump);
+ virtual void updateAffineTransformation();
+ virtual void dumpAffineTransformation(std::string& dump);
+ virtual void resolveExternalStylusPresence();
+ virtual bool hasStylus() const = 0;
+ virtual bool hasExternalStylus() const;
+
+ virtual void syncTouch(nsecs_t when, RawState* outState) = 0;
+
+private:
+ // The current viewport.
+ // The components of the viewport are specified in the display's rotated orientation.
+ DisplayViewport mViewport;
+
+ // The surface orientation, width and height set by configureSurface().
+ // The width and height are derived from the viewport but are specified
+ // in the natural orientation.
+ // The surface origin specifies how the surface coordinates should be translated
+ // to align with the logical display coordinate space.
+ int32_t mSurfaceWidth;
+ int32_t mSurfaceHeight;
+ int32_t mSurfaceLeft;
+ int32_t mSurfaceTop;
+
+ // Similar to the surface coordinates, but in the raw display coordinate space rather than in
+ // the logical coordinate space.
+ int32_t mPhysicalWidth;
+ int32_t mPhysicalHeight;
+ int32_t mPhysicalLeft;
+ int32_t mPhysicalTop;
+
+ // The orientation may be different from the viewport orientation as it specifies
+ // the rotation of the surface coordinates required to produce the viewport's
+ // requested orientation, so it will depend on whether the device is orientation aware.
+ int32_t mSurfaceOrientation;
+
+ // Translation and scaling factors, orientation-independent.
+ float mXTranslate;
+ float mXScale;
+ float mXPrecision;
+
+ float mYTranslate;
+ float mYScale;
+ float mYPrecision;
+
+ float mGeometricScale;
+
+ float mPressureScale;
+
+ float mSizeScale;
+
+ float mOrientationScale;
+
+ float mDistanceScale;
+
+ bool mHaveTilt;
+ float mTiltXCenter;
+ float mTiltXScale;
+ float mTiltYCenter;
+ float mTiltYScale;
+
+ bool mExternalStylusConnected;
+
+ // Oriented motion ranges for input device info.
+ struct OrientedRanges {
+ InputDeviceInfo::MotionRange x;
+ InputDeviceInfo::MotionRange y;
+ InputDeviceInfo::MotionRange pressure;
+
+ bool haveSize;
+ InputDeviceInfo::MotionRange size;
+
+ bool haveTouchSize;
+ InputDeviceInfo::MotionRange touchMajor;
+ InputDeviceInfo::MotionRange touchMinor;
+
+ bool haveToolSize;
+ InputDeviceInfo::MotionRange toolMajor;
+ InputDeviceInfo::MotionRange toolMinor;
+
+ bool haveOrientation;
+ InputDeviceInfo::MotionRange orientation;
+
+ bool haveDistance;
+ InputDeviceInfo::MotionRange distance;
+
+ bool haveTilt;
+ InputDeviceInfo::MotionRange tilt;
+
+ OrientedRanges() { clear(); }
+
+ void clear() {
+ haveSize = false;
+ haveTouchSize = false;
+ haveToolSize = false;
+ haveOrientation = false;
+ haveDistance = false;
+ haveTilt = false;
+ }
+ } mOrientedRanges;
+
+ // Oriented dimensions and precision.
+ float mOrientedXPrecision;
+ float mOrientedYPrecision;
+
+ struct CurrentVirtualKeyState {
+ bool down;
+ bool ignored;
+ nsecs_t downTime;
+ int32_t keyCode;
+ int32_t scanCode;
+ } mCurrentVirtualKey;
+
+ // Scale factor for gesture or mouse based pointer movements.
+ float mPointerXMovementScale;
+ float mPointerYMovementScale;
+
+ // Scale factor for gesture based zooming and other freeform motions.
+ float mPointerXZoomScale;
+ float mPointerYZoomScale;
+
+ // The maximum swipe width.
+ float mPointerGestureMaxSwipeWidth;
+
+ struct PointerDistanceHeapElement {
+ uint32_t currentPointerIndex : 8;
+ uint32_t lastPointerIndex : 8;
+ uint64_t distance : 48; // squared distance
+ };
+
+ enum PointerUsage {
+ POINTER_USAGE_NONE,
+ POINTER_USAGE_GESTURES,
+ POINTER_USAGE_STYLUS,
+ POINTER_USAGE_MOUSE,
+ };
+ PointerUsage mPointerUsage;
+
+ struct PointerGesture {
+ enum Mode {
+ // No fingers, button is not pressed.
+ // Nothing happening.
+ NEUTRAL,
+
+ // No fingers, button is not pressed.
+ // Tap detected.
+ // Emits DOWN and UP events at the pointer location.
+ TAP,
+
+ // Exactly one finger dragging following a tap.
+ // Pointer follows the active finger.
+ // Emits DOWN, MOVE and UP events at the pointer location.
+ //
+ // Detect double-taps when the finger goes up while in TAP_DRAG mode.
+ TAP_DRAG,
+
+ // Button is pressed.
+ // Pointer follows the active finger if there is one. Other fingers are ignored.
+ // Emits DOWN, MOVE and UP events at the pointer location.
+ BUTTON_CLICK_OR_DRAG,
+
+ // Exactly one finger, button is not pressed.
+ // Pointer follows the active finger.
+ // Emits HOVER_MOVE events at the pointer location.
+ //
+ // Detect taps when the finger goes up while in HOVER mode.
+ HOVER,
+
+ // Exactly two fingers but neither have moved enough to clearly indicate
+ // whether a swipe or freeform gesture was intended. We consider the
+ // pointer to be pressed so this enables clicking or long-pressing on buttons.
+ // Pointer does not move.
+ // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
+ PRESS,
+
+ // Exactly two fingers moving in the same direction, button is not pressed.
+ // Pointer does not move.
+ // Emits DOWN, MOVE and UP events with a single pointer coordinate that
+ // follows the midpoint between both fingers.
+ SWIPE,
+
+ // Two or more fingers moving in arbitrary directions, button is not pressed.
+ // Pointer does not move.
+ // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
+ // each finger individually relative to the initial centroid of the finger.
+ FREEFORM,
+
+ // Waiting for quiet time to end before starting the next gesture.
+ QUIET,
+ };
+
+ // Time the first finger went down.
+ nsecs_t firstTouchTime;
+
+ // The active pointer id from the raw touch data.
+ int32_t activeTouchId; // -1 if none
+
+ // The active pointer id from the gesture last delivered to the application.
+ int32_t activeGestureId; // -1 if none
+
+ // Pointer coords and ids for the current and previous pointer gesture.
+ Mode currentGestureMode;
+ BitSet32 currentGestureIdBits;
+ uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
+ PointerProperties currentGestureProperties[MAX_POINTERS];
+ PointerCoords currentGestureCoords[MAX_POINTERS];
+
+ Mode lastGestureMode;
+ BitSet32 lastGestureIdBits;
+ uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
+ PointerProperties lastGestureProperties[MAX_POINTERS];
+ PointerCoords lastGestureCoords[MAX_POINTERS];
+
+ // Time the pointer gesture last went down.
+ nsecs_t downTime;
+
+ // Time when the pointer went down for a TAP.
+ nsecs_t tapDownTime;
+
+ // Time when the pointer went up for a TAP.
+ nsecs_t tapUpTime;
+
+ // Location of initial tap.
+ float tapX, tapY;
+
+ // Time we started waiting for quiescence.
+ nsecs_t quietTime;
+
+ // Reference points for multitouch gestures.
+ float referenceTouchX; // reference touch X/Y coordinates in surface units
+ float referenceTouchY;
+ float referenceGestureX; // reference gesture X/Y coordinates in pixels
+ float referenceGestureY;
+
+ // Distance that each pointer has traveled which has not yet been
+ // subsumed into the reference gesture position.
+ BitSet32 referenceIdBits;
+ struct Delta {
+ float dx, dy;
+ };
+ Delta referenceDeltas[MAX_POINTER_ID + 1];
+
+ // Describes how touch ids are mapped to gesture ids for freeform gestures.
+ uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
+
+ // A velocity tracker for determining whether to switch active pointers during drags.
+ VelocityTracker velocityTracker;
+
+ void reset() {
+ firstTouchTime = LLONG_MIN;
+ activeTouchId = -1;
+ activeGestureId = -1;
+ currentGestureMode = NEUTRAL;
+ currentGestureIdBits.clear();
+ lastGestureMode = NEUTRAL;
+ lastGestureIdBits.clear();
+ downTime = 0;
+ velocityTracker.clear();
+ resetTap();
+ resetQuietTime();
+ }
+
+ void resetTap() {
+ tapDownTime = LLONG_MIN;
+ tapUpTime = LLONG_MIN;
+ }
+
+ void resetQuietTime() { quietTime = LLONG_MIN; }
+ } mPointerGesture;
+
+ struct PointerSimple {
+ PointerCoords currentCoords;
+ PointerProperties currentProperties;
+ PointerCoords lastCoords;
+ PointerProperties lastProperties;
+
+ // True if the pointer is down.
+ bool down;
+
+ // True if the pointer is hovering.
+ bool hovering;
+
+ // Time the pointer last went down.
+ nsecs_t downTime;
+
+ void reset() {
+ currentCoords.clear();
+ currentProperties.clear();
+ lastCoords.clear();
+ lastProperties.clear();
+ down = false;
+ hovering = false;
+ downTime = 0;
+ }
+ } mPointerSimple;
+
+ // The pointer and scroll velocity controls.
+ VelocityControl mPointerVelocityControl;
+ VelocityControl mWheelXVelocityControl;
+ VelocityControl mWheelYVelocityControl;
+
+ std::optional<DisplayViewport> findViewport();
+
+ void resetExternalStylus();
+ void clearStylusDataPendingFlags();
+
+ void sync(nsecs_t when);
+
+ bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
+ void processRawTouches(bool timeout);
+ void cookAndDispatch(nsecs_t when);
+ void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction,
+ int32_t keyEventFlags);
+
+ void dispatchTouches(nsecs_t when, uint32_t policyFlags);
+ void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
+ void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
+ void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags);
+ void dispatchButtonPress(nsecs_t when, uint32_t policyFlags);
+ const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
+ void cookPointerData();
+ void abortTouches(nsecs_t when, uint32_t policyFlags);
+
+ void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
+ void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
+
+ void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
+ void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
+ bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
+ bool* outFinishPreviousGesture, bool isTimeout);
+
+ void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
+ void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
+
+ void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
+ void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
+
+ void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering);
+ void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+
+ bool assignExternalStylusId(const RawState& state, bool timeout);
+ void applyExternalStylusButtonState(nsecs_t when);
+ void applyExternalStylusTouchState(nsecs_t when);
+
+ // Dispatches a motion event.
+ // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
+ // method will take care of setting the index and transmuting the action to DOWN or UP
+ // it is the first / last pointer to go down / up.
+ void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+ int32_t edgeFlags, const PointerProperties* properties,
+ const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+ int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+
+ // Updates pointer coords and properties for pointers with specified ids that have moved.
+ // Returns true if any of them changed.
+ bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords,
+ const uint32_t* inIdToIndex, PointerProperties* outProperties,
+ PointerCoords* outCoords, const uint32_t* outIdToIndex,
+ BitSet32 idBits) const;
+
+ bool isPointInsideSurface(int32_t x, int32_t y);
+ const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
+
+ static void assignPointerIds(const RawState* last, RawState* current);
+
+ const char* modeToString(DeviceMode deviceMode);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
new file mode 100644
index 0000000..a27fab4
--- /dev/null
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "VibratorInputMapper.h"
+
+namespace android {
+
+VibratorInputMapper::VibratorInputMapper(InputDevice* device)
+ : InputMapper(device), mVibrating(false) {}
+
+VibratorInputMapper::~VibratorInputMapper() {}
+
+uint32_t VibratorInputMapper::getSources() {
+ return 0;
+}
+
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setVibrator(true);
+}
+
+void VibratorInputMapper::process(const RawEvent* rawEvent) {
+ // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+}
+
+void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+#if DEBUG_VIBRATOR
+ std::string patternStr;
+ for (size_t i = 0; i < patternSize; i++) {
+ if (i != 0) {
+ patternStr += ", ";
+ }
+ patternStr += StringPrintf("%" PRId64, pattern[i]);
+ }
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
+ patternStr.c_str(), repeat, token);
+#endif
+
+ mVibrating = true;
+ memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
+ mPatternSize = patternSize;
+ mRepeat = repeat;
+ mToken = token;
+ mIndex = -1;
+
+ nextStep();
+}
+
+void VibratorInputMapper::cancelVibrate(int32_t token) {
+#if DEBUG_VIBRATOR
+ ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+#endif
+
+ if (mVibrating && mToken == token) {
+ stopVibrating();
+ }
+}
+
+void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+ if (mVibrating) {
+ if (when >= mNextStepTime) {
+ nextStep();
+ } else {
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+ }
+ }
+}
+
+void VibratorInputMapper::nextStep() {
+ mIndex += 1;
+ if (size_t(mIndex) >= mPatternSize) {
+ if (mRepeat < 0) {
+ // We are done.
+ stopVibrating();
+ return;
+ }
+ mIndex = mRepeat;
+ }
+
+ bool vibratorOn = mIndex & 1;
+ nsecs_t duration = mPattern[mIndex];
+ if (vibratorOn) {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+#endif
+ getEventHub()->vibrate(getDeviceId(), duration);
+ } else {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+ }
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mNextStepTime = now + duration;
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+#endif
+}
+
+void VibratorInputMapper::stopVibrating() {
+ mVibrating = false;
+#if DEBUG_VIBRATOR
+ ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+}
+
+void VibratorInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Vibrator Input Mapper:\n";
+ dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
new file mode 100644
index 0000000..dc67890
--- /dev/null
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class VibratorInputMapper : public InputMapper {
+public:
+ explicit VibratorInputMapper(InputDevice* device);
+ virtual ~VibratorInputMapper();
+
+ virtual uint32_t getSources() override;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ virtual void process(const RawEvent* rawEvent) override;
+
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) override;
+ virtual void cancelVibrate(int32_t token) override;
+ virtual void timeoutExpired(nsecs_t when) override;
+ virtual void dump(std::string& dump) override;
+
+private:
+ bool mVibrating;
+ nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
+ size_t mPatternSize;
+ ssize_t mRepeat;
+ int32_t mToken;
+ ssize_t mIndex;
+ nsecs_t mNextStepTime;
+
+ void nextStep();
+ void stopVibrating();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
new file mode 100644
index 0000000..0337d51
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CursorButtonAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+CursorButtonAccumulator::CursorButtonAccumulator() {
+ clearButtons();
+}
+
+void CursorButtonAccumulator::reset(InputDevice* device) {
+ mBtnLeft = device->isKeyPressed(BTN_LEFT);
+ mBtnRight = device->isKeyPressed(BTN_RIGHT);
+ mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
+ mBtnBack = device->isKeyPressed(BTN_BACK);
+ mBtnSide = device->isKeyPressed(BTN_SIDE);
+ mBtnForward = device->isKeyPressed(BTN_FORWARD);
+ mBtnExtra = device->isKeyPressed(BTN_EXTRA);
+ mBtnTask = device->isKeyPressed(BTN_TASK);
+}
+
+void CursorButtonAccumulator::clearButtons() {
+ mBtnLeft = 0;
+ mBtnRight = 0;
+ mBtnMiddle = 0;
+ mBtnBack = 0;
+ mBtnSide = 0;
+ mBtnForward = 0;
+ mBtnExtra = 0;
+ mBtnTask = 0;
+}
+
+void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_KEY) {
+ switch (rawEvent->code) {
+ case BTN_LEFT:
+ mBtnLeft = rawEvent->value;
+ break;
+ case BTN_RIGHT:
+ mBtnRight = rawEvent->value;
+ break;
+ case BTN_MIDDLE:
+ mBtnMiddle = rawEvent->value;
+ break;
+ case BTN_BACK:
+ mBtnBack = rawEvent->value;
+ break;
+ case BTN_SIDE:
+ mBtnSide = rawEvent->value;
+ break;
+ case BTN_FORWARD:
+ mBtnForward = rawEvent->value;
+ break;
+ case BTN_EXTRA:
+ mBtnExtra = rawEvent->value;
+ break;
+ case BTN_TASK:
+ mBtnTask = rawEvent->value;
+ break;
+ }
+ }
+}
+
+uint32_t CursorButtonAccumulator::getButtonState() const {
+ uint32_t result = 0;
+ if (mBtnLeft) {
+ result |= AMOTION_EVENT_BUTTON_PRIMARY;
+ }
+ if (mBtnRight) {
+ result |= AMOTION_EVENT_BUTTON_SECONDARY;
+ }
+ if (mBtnMiddle) {
+ result |= AMOTION_EVENT_BUTTON_TERTIARY;
+ }
+ if (mBtnBack || mBtnSide) {
+ result |= AMOTION_EVENT_BUTTON_BACK;
+ }
+ if (mBtnForward || mBtnExtra) {
+ result |= AMOTION_EVENT_BUTTON_FORWARD;
+ }
+ return result;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
new file mode 100644
index 0000000..d912310
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of mouse or touch pad buttons. */
+class CursorButtonAccumulator {
+public:
+ CursorButtonAccumulator();
+ void reset(InputDevice* device);
+
+ void process(const RawEvent* rawEvent);
+
+ uint32_t getButtonState() const;
+
+private:
+ bool mBtnLeft;
+ bool mBtnRight;
+ bool mBtnMiddle;
+ bool mBtnBack;
+ bool mBtnSide;
+ bool mBtnForward;
+ bool mBtnExtra;
+ bool mBtnTask;
+
+ void clearButtons();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
new file mode 100644
index 0000000..d744096
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CursorScrollAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) {
+ clearRelativeAxes();
+}
+
+void CursorScrollAccumulator::configure(InputDevice* device) {
+ mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
+ mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
+}
+
+void CursorScrollAccumulator::reset(InputDevice* device) {
+ clearRelativeAxes();
+}
+
+void CursorScrollAccumulator::clearRelativeAxes() {
+ mRelWheel = 0;
+ mRelHWheel = 0;
+}
+
+void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_REL) {
+ switch (rawEvent->code) {
+ case REL_WHEEL:
+ mRelWheel = rawEvent->value;
+ break;
+ case REL_HWHEEL:
+ mRelHWheel = rawEvent->value;
+ break;
+ }
+ }
+}
+
+void CursorScrollAccumulator::finishSync() {
+ clearRelativeAxes();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
new file mode 100644
index 0000000..85f331f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of cursor scrolling motions. */
+
+class CursorScrollAccumulator {
+public:
+ CursorScrollAccumulator();
+ void configure(InputDevice* device);
+ void reset(InputDevice* device);
+
+ void process(const RawEvent* rawEvent);
+ void finishSync();
+
+ inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
+ inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
+
+ inline int32_t getRelativeX() const { return mRelX; }
+ inline int32_t getRelativeY() const { return mRelY; }
+ inline int32_t getRelativeVWheel() const { return mRelWheel; }
+ inline int32_t getRelativeHWheel() const { return mRelHWheel; }
+
+private:
+ bool mHaveRelWheel;
+ bool mHaveRelHWheel;
+
+ int32_t mRelX;
+ int32_t mRelY;
+ int32_t mRelWheel;
+ int32_t mRelHWheel;
+
+ void clearRelativeAxes();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
new file mode 100644
index 0000000..e9ba727
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SingleTouchMotionAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
+ clearAbsoluteAxes();
+}
+
+void SingleTouchMotionAccumulator::reset(InputDevice* device) {
+ mAbsX = device->getAbsoluteAxisValue(ABS_X);
+ mAbsY = device->getAbsoluteAxisValue(ABS_Y);
+ mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
+ mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
+ mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
+ mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
+ mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
+}
+
+void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
+ mAbsX = 0;
+ mAbsY = 0;
+ mAbsPressure = 0;
+ mAbsToolWidth = 0;
+ mAbsDistance = 0;
+ mAbsTiltX = 0;
+ mAbsTiltY = 0;
+}
+
+void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_ABS) {
+ switch (rawEvent->code) {
+ case ABS_X:
+ mAbsX = rawEvent->value;
+ break;
+ case ABS_Y:
+ mAbsY = rawEvent->value;
+ break;
+ case ABS_PRESSURE:
+ mAbsPressure = rawEvent->value;
+ break;
+ case ABS_TOOL_WIDTH:
+ mAbsToolWidth = rawEvent->value;
+ break;
+ case ABS_DISTANCE:
+ mAbsDistance = rawEvent->value;
+ break;
+ case ABS_TILT_X:
+ mAbsTiltX = rawEvent->value;
+ break;
+ case ABS_TILT_Y:
+ mAbsTiltY = rawEvent->value;
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
new file mode 100644
index 0000000..75f8a96
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of single-touch protocol. */
+class SingleTouchMotionAccumulator {
+public:
+ SingleTouchMotionAccumulator();
+
+ void process(const RawEvent* rawEvent);
+ void reset(InputDevice* device);
+
+ inline int32_t getAbsoluteX() const { return mAbsX; }
+ inline int32_t getAbsoluteY() const { return mAbsY; }
+ inline int32_t getAbsolutePressure() const { return mAbsPressure; }
+ inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; }
+ inline int32_t getAbsoluteDistance() const { return mAbsDistance; }
+ inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; }
+ inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; }
+
+private:
+ int32_t mAbsX;
+ int32_t mAbsY;
+ int32_t mAbsPressure;
+ int32_t mAbsToolWidth;
+ int32_t mAbsDistance;
+ int32_t mAbsTiltX;
+ int32_t mAbsTiltY;
+
+ void clearAbsoluteAxes();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
new file mode 100644
index 0000000..d2f06c8
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TouchButtonAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
+ clearButtons();
+}
+
+void TouchButtonAccumulator::configure(InputDevice* device) {
+ mHaveBtnTouch = device->hasKey(BTN_TOUCH);
+ mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) ||
+ device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) ||
+ device->hasKey(BTN_TOOL_AIRBRUSH);
+}
+
+void TouchButtonAccumulator::reset(InputDevice* device) {
+ mBtnTouch = device->isKeyPressed(BTN_TOUCH);
+ mBtnStylus = device->isKeyPressed(BTN_STYLUS);
+ // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
+ mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
+ mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
+ mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
+ mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
+ mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
+ mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
+ mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
+ mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
+ mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
+ mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
+ mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
+ mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
+}
+
+void TouchButtonAccumulator::clearButtons() {
+ mBtnTouch = 0;
+ mBtnStylus = 0;
+ mBtnStylus2 = 0;
+ mBtnToolFinger = 0;
+ mBtnToolPen = 0;
+ mBtnToolRubber = 0;
+ mBtnToolBrush = 0;
+ mBtnToolPencil = 0;
+ mBtnToolAirbrush = 0;
+ mBtnToolMouse = 0;
+ mBtnToolLens = 0;
+ mBtnToolDoubleTap = 0;
+ mBtnToolTripleTap = 0;
+ mBtnToolQuadTap = 0;
+}
+
+void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_KEY) {
+ switch (rawEvent->code) {
+ case BTN_TOUCH:
+ mBtnTouch = rawEvent->value;
+ break;
+ case BTN_STYLUS:
+ mBtnStylus = rawEvent->value;
+ break;
+ case BTN_STYLUS2:
+ case BTN_0: // BTN_0 is what gets mapped for the HID usage
+ // Digitizers.SecondaryBarrelSwitch
+ mBtnStylus2 = rawEvent->value;
+ break;
+ case BTN_TOOL_FINGER:
+ mBtnToolFinger = rawEvent->value;
+ break;
+ case BTN_TOOL_PEN:
+ mBtnToolPen = rawEvent->value;
+ break;
+ case BTN_TOOL_RUBBER:
+ mBtnToolRubber = rawEvent->value;
+ break;
+ case BTN_TOOL_BRUSH:
+ mBtnToolBrush = rawEvent->value;
+ break;
+ case BTN_TOOL_PENCIL:
+ mBtnToolPencil = rawEvent->value;
+ break;
+ case BTN_TOOL_AIRBRUSH:
+ mBtnToolAirbrush = rawEvent->value;
+ break;
+ case BTN_TOOL_MOUSE:
+ mBtnToolMouse = rawEvent->value;
+ break;
+ case BTN_TOOL_LENS:
+ mBtnToolLens = rawEvent->value;
+ break;
+ case BTN_TOOL_DOUBLETAP:
+ mBtnToolDoubleTap = rawEvent->value;
+ break;
+ case BTN_TOOL_TRIPLETAP:
+ mBtnToolTripleTap = rawEvent->value;
+ break;
+ case BTN_TOOL_QUADTAP:
+ mBtnToolQuadTap = rawEvent->value;
+ break;
+ }
+ }
+}
+
+uint32_t TouchButtonAccumulator::getButtonState() const {
+ uint32_t result = 0;
+ if (mBtnStylus) {
+ result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
+ }
+ if (mBtnStylus2) {
+ result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY;
+ }
+ return result;
+}
+
+int32_t TouchButtonAccumulator::getToolType() const {
+ if (mBtnToolMouse || mBtnToolLens) {
+ return AMOTION_EVENT_TOOL_TYPE_MOUSE;
+ }
+ if (mBtnToolRubber) {
+ return AMOTION_EVENT_TOOL_TYPE_ERASER;
+ }
+ if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
+ return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ }
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+ return AMOTION_EVENT_TOOL_TYPE_FINGER;
+ }
+ return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+}
+
+bool TouchButtonAccumulator::isToolActive() const {
+ return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
+ mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
+ mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+}
+
+bool TouchButtonAccumulator::isHovering() const {
+ return mHaveBtnTouch && !mBtnTouch;
+}
+
+bool TouchButtonAccumulator::hasStylus() const {
+ return mHaveStylus;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
new file mode 100644
index 0000000..65b6bdc
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of touch, stylus and tool buttons. */
+class TouchButtonAccumulator {
+public:
+ TouchButtonAccumulator();
+ void configure(InputDevice* device);
+ void reset(InputDevice* device);
+
+ void process(const RawEvent* rawEvent);
+
+ uint32_t getButtonState() const;
+ int32_t getToolType() const;
+ bool isToolActive() const;
+ bool isHovering() const;
+ bool hasStylus() const;
+
+private:
+ bool mHaveBtnTouch;
+ bool mHaveStylus;
+
+ bool mBtnTouch;
+ bool mBtnStylus;
+ bool mBtnStylus2;
+ bool mBtnToolFinger;
+ bool mBtnToolPen;
+ bool mBtnToolRubber;
+ bool mBtnToolBrush;
+ bool mBtnToolPencil;
+ bool mBtnToolAirbrush;
+ bool mBtnToolMouse;
+ bool mBtnToolLens;
+ bool mBtnToolDoubleTap;
+ bool mBtnToolTripleTap;
+ bool mBtnToolQuadTap;
+
+ void clearButtons();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
new file mode 100644
index 0000000..5956fb0
--- /dev/null
+++ b/services/inputflinger/reporter/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2019 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.
+
+cc_library_headers {
+ name: "libinputreporter_headers",
+ export_include_dirs: ["."],
+}
+
+cc_library_shared {
+ name: "libinputreporter",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "InputReporter.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libinputflinger_headers",
+ ],
+}
+
diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/reporter/InputReporter.cpp
similarity index 93%
rename from services/inputflinger/InputReporter.cpp
rename to services/inputflinger/reporter/InputReporter.cpp
index 8d3153c..b591d3f 100644
--- a/services/inputflinger/InputReporter.cpp
+++ b/services/inputflinger/reporter/InputReporter.cpp
@@ -27,15 +27,15 @@
};
void InputReporter::reportUnhandledKey(uint32_t sequenceNum) {
- // do nothing
+ // do nothing
}
void InputReporter::reportDroppedKey(uint32_t sequenceNum) {
- // do nothing
+ // do nothing
}
sp<InputReporterInterface> createInputReporter() {
- return new InputReporter();
+ return new InputReporter();
}
} // namespace android
diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h
similarity index 97%
rename from services/inputflinger/include/InputReporterInterface.h
rename to services/inputflinger/reporter/InputReporterInterface.h
index 906d7f2..e5d3606 100644
--- a/services/inputflinger/include/InputReporterInterface.h
+++ b/services/inputflinger/reporter/InputReporterInterface.h
@@ -27,7 +27,7 @@
*/
class InputReporterInterface : public virtual RefBase {
protected:
- virtual ~InputReporterInterface() { }
+ virtual ~InputReporterInterface() {}
public:
// Report a key that was not handled by the system or apps.
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 9054316..c4f8626 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -32,4 +32,7 @@
"libinputflinger_base",
"libinputservice",
],
+ header_libs: [
+ "libinputreader_headers",
+ ],
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index a86dcbc..aa98ef7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include "../InputDispatcher.h"
+#include "../dispatcher/InputDispatcher.h"
+
+#include <InputDispatcherThread.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
#include <linux/input.h>
-namespace android {
+namespace android::inputdispatcher {
// An arbitrary time value.
static const nsecs_t ARBITRARY_TIME = 1234;
@@ -1082,4 +1084,4 @@
mFakePolicy->assertOnPointerDownEquals(nullptr);
}
-} // namespace android
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 09df2a7..31b1652 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,8 +14,16 @@
* limitations under the License.
*/
-#include "../InputReader.h"
-#include "TestInputListener.h"
+#include <CursorInputMapper.h>
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <KeyboardInputMapper.h>
+#include <MultiTouchInputMapper.h>
+#include <SingleTouchInputMapper.h>
+#include <SwitchInputMapper.h>
+#include <TestInputListener.h>
+#include <TouchInputMapper.h>
#include <gtest/gtest.h>
#include <inttypes.h>
@@ -194,6 +202,20 @@
mConfig.setDisplayViewports(mViewports);
}
+ bool updateViewport(const DisplayViewport& viewport) {
+ size_t count = mViewports.size();
+ for (size_t i = 0; i < count; i++) {
+ const DisplayViewport& currentViewport = mViewports[i];
+ if (currentViewport.displayId == viewport.displayId) {
+ mViewports[i] = viewport;
+ mConfig.setDisplayViewports(mViewports);
+ return true;
+ }
+ }
+ // no viewport found.
+ return false;
+ }
+
void addExcludedDeviceName(const std::string& deviceName) {
mConfig.excludedDeviceNames.push_back(deviceName);
}
@@ -6585,4 +6607,30 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
}
+/**
+ * Test touch should not work if outside of surface.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ // Let surface be different from physical display.
+ std::optional<DisplayViewport> internalViewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport->logicalLeft = internalViewport->physicalTop + 20;
+ internalViewport->logicalTop = internalViewport->physicalRight + 20;
+ internalViewport->logicalRight = internalViewport->physicalRight - 20;
+ internalViewport->logicalBottom = internalViewport->physicalBottom - 20;
+ mFakePolicy->updateViewport(internalViewport.value());
+
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ int32_t rawX = 10;
+ int32_t rawY = 10;
+ processPosition(mapper, rawX, rawY);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b404836..213a62e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -29,6 +29,7 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
"libbase",
@@ -92,6 +93,7 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"android.hardware.power@1.3",
"libhidlbase",
],
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fba235d..b500ad3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -58,7 +58,7 @@
BufferLayer::BufferLayer(const LayerCreationArgs& args)
: Layer(args),
- mTextureName(args.flinger->getNewTexture()),
+ mTextureName(args.textureName),
mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
compositionengine::LayerCreationArgs{this})} {
ALOGV("Creating Layer %s", args.name.string());
@@ -70,7 +70,13 @@
}
BufferLayer::~BufferLayer() {
- mFlinger->deleteTextureAsync(mTextureName);
+ if (!isClone()) {
+ // The original layer and the clone layer share the same texture. Therefore, only one of
+ // the layers, in this case the original layer, needs to handle the deletion. The original
+ // layer and the clone should be removed at the same time so there shouldn't be any issue
+ // with the clone layer trying to use the deleted texture.
+ mFlinger->deleteTextureAsync(mTextureName);
+ }
const int32_t layerID = getSequence();
mFlinger->mTimeStats->onDestroy(layerID);
mFlinger->mFrameTracer->onDestroy(layerID);
@@ -289,7 +295,7 @@
const CompositorTiming& compositorTiming) {
// mFrameLatencyNeeded is true when a new frame was latched for the
// composition.
- if (!mFrameLatencyNeeded) return false;
+ if (!mBufferInfo.mFrameLatencyNeeded) return false;
// Update mFrameEventHistory.
{
@@ -331,7 +337,7 @@
}
mFrameTracker.advanceFrame();
- mFrameLatencyNeeded = false;
+ mBufferInfo.mFrameLatencyNeeded = false;
return true;
}
@@ -395,7 +401,7 @@
gatherBufferInfo();
mRefreshPending = true;
- mFrameLatencyNeeded = true;
+ mBufferInfo.mFrameLatencyNeeded = true;
if (oldBufferInfo.mBuffer == nullptr) {
// the first time we receive a buffer, we need to trigger a
// geometry invalidation.
@@ -722,6 +728,44 @@
mBufferInfo.mTransform, filteringEnabled);
}
+void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+ Layer::setInitialValuesForClone(clonedFrom);
+
+ sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get());
+ mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
+ mPotentialCursor = bufferClonedFrom->mPotentialCursor;
+ mProtectedByApp = bufferClonedFrom->mProtectedByApp;
+
+ updateCloneBufferInfo();
+}
+
+void BufferLayer::updateCloneBufferInfo() {
+ if (!isClone() || !isClonedFromAlive()) {
+ return;
+ }
+
+ sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get());
+ mBufferInfo = clonedFrom->mBufferInfo;
+ mSidebandStream = clonedFrom->mSidebandStream;
+ surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+ mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+ mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+ // After buffer info is updated, the drawingState from the real layer needs to be copied into
+ // the cloned. This is because some properties of drawingState can change when latchBuffer is
+ // called. However, copying the drawingState would also overwrite the cloned layer's relatives.
+ // Therefore, temporarily store the relatives so they can be set in the cloned drawingState
+ // again.
+ wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+ SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ mDrawingState = clonedFrom->mDrawingState;
+ // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
+ // InputWindows per client token yet.
+ mDrawingState.inputInfo.token = nullptr;
+ mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+ mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index f0a30e3..656ba12 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -165,6 +165,8 @@
sp<GraphicBuffer> mBuffer;
int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+ bool mFrameLatencyNeeded{false};
};
BufferInfo mBufferInfo;
@@ -194,6 +196,9 @@
bool mRefreshPending{false};
ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+ void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ void updateCloneBufferInfo() override;
+ uint64_t mPreviousFrameNumber = 0;
private:
// Returns true if this layer requires filtering
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index fcabedf..d80a70e 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -461,12 +461,7 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer, true);
mProducer = new MonitoredProducer(producer, mFlinger, this);
- {
- // Grab the SF state lock during this since it's the only safe way to access RenderEngine
- Mutex::Autolock lock(mFlinger->mStateLock);
- mConsumer =
- new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
- }
+ mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mConsumer->setContentsChangedListener(this);
mConsumer->setName(mName);
@@ -476,7 +471,7 @@
mProducer->setMaxDequeuedBufferCount(2);
}
- if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+ if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
updateTransformHint(display);
}
}
@@ -531,4 +526,15 @@
mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
}
+sp<Layer> BufferQueueLayer::createClone() {
+ const String8 name = mName + " (Mirror)";
+ LayerCreationArgs args =
+ LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata());
+ args.textureName = mTextureName;
+ sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
+ layer->setInitialValuesForClone(this);
+
+ return layer;
+}
+
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 9374741..f3e8a19 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -31,6 +31,7 @@
*/
class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
public:
+ // Only call while mStateLock is held
explicit BufferQueueLayer(const LayerCreationArgs&);
~BufferQueueLayer() override;
@@ -81,6 +82,7 @@
status_t updateFrameNumber(nsecs_t latchTime) override;
void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ sp<Layer> createClone() override;
// -----------------------------------------------------------------------
// Interface implementation for BufferLayerConsumer::ContentsChangedListener
@@ -109,8 +111,6 @@
PixelFormat mFormat{PIXEL_FORMAT_NONE};
- // Only accessed on the main thread.
- uint64_t mPreviousFrameNumber{0};
bool mUpdateTexImageFailed{false};
uint64_t mPreviousBufferId = 0;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index ad05bc8..75fc0e9 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -48,10 +48,17 @@
: BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
+ if (const auto display = args.displayDevice) {
+ updateTransformHint(display);
+ }
}
BufferStateLayer::~BufferStateLayer() {
- if (mBufferInfo.mBuffer != nullptr) {
+ // The original layer and the clone layer share the same texture and buffer. Therefore, only
+ // one of the layers, in this case the original layer, needs to handle the deletion. The
+ // original layer and the clone should be removed at the same time so there shouldn't be any
+ // issue with the clone layer trying to use the texture.
+ if (mBufferInfo.mBuffer != nullptr && !isClone()) {
// Ensure that mBuffer is uncached from RenderEngine here, as
// RenderEngine may have been using the buffer as an external texture
// after the client uncached the buffer.
@@ -118,9 +125,10 @@
bool BufferStateLayer::willPresentCurrentTransaction() const {
// Returns true if the most recent Transaction applied to CurrentState will be presented.
- return getSidebandStreamChanged() || getAutoRefresh() ||
+ return (getSidebandStreamChanged() || getAutoRefresh() ||
(mCurrentState.modified &&
- (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr));
+ (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr))) &&
+ !mLayerDetached;
}
void BufferStateLayer::pushPendingState() {
@@ -545,14 +553,6 @@
mFrameNumber++;
}
-void BufferStateLayer::onFirstRef() {
- BufferLayer::onFirstRef();
-
- if (const auto display = mFlinger->getDefaultDisplayDevice()) {
- updateTransformHint(display);
- }
-}
-
void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
std::lock_guard lock(mMutex);
if (!clientCacheId.isValid()) {
@@ -668,4 +668,14 @@
return s.crop;
}
+sp<Layer> BufferStateLayer::createClone() {
+ const String8 name = mName + " (Mirror)";
+ LayerCreationArgs args =
+ LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata());
+ args.textureName = mTextureName;
+ sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+ layer->mHwcSlotGenerator = mHwcSlotGenerator;
+ layer->setInitialValuesForClone(this);
+ return layer;
+}
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 52063fc..3dfe76c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -124,13 +124,13 @@
status_t updateFrameNumber(nsecs_t latchTime) override;
void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ sp<Layer> createClone() override;
// Crop that applies to the buffer
Rect computeCrop(const State& s);
private:
friend class SlotGenerationTest;
- void onFirstRef() override;
bool willPresentCurrentTransaction() const;
static const std::array<float, 16> IDENTITY_MATRIX;
@@ -143,7 +143,6 @@
sp<Fence> mPreviousReleaseFence;
uint64_t mPreviousBufferId = 0;
- uint64_t mPreviousFrameNumber = 0;
uint64_t mPreviousReleasedFrameNumber = 0;
mutable bool mCurrentStateModified = false;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 6bfd302..c7ed9b0 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -106,6 +106,10 @@
nullptr, layer);
}
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
+ return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
+}
+
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
sp<Layer> layer = getLayerUser(handle);
if (layer == nullptr) {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 74e4818..7d7cef8 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -62,6 +62,8 @@
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp);
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
+
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 99805d9..172d445 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -107,6 +107,14 @@
return mDrawingState.dataspace;
}
+sp<Layer> ColorLayer::createClone() {
+ String8 name = mName + " (Mirror)";
+ sp<ColorLayer> layer = mFlinger->getFactory().createColorLayer(
+ LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata()));
+ layer->setInitialValuesForClone(this);
+ return layer;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 16921df..634a800 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -50,6 +50,8 @@
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+
+ sp<Layer> createClone() override;
};
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index fcb94fd..738a2a4 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -3,6 +3,7 @@
defaults: ["surfaceflinger_defaults"],
cflags: [
"-DLOG_TAG=\"CompositionEngine\"",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
],
shared_libs: [
"android.frameworks.vr.composer@2.0",
@@ -10,6 +11,7 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
"libbase",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index a8e05cb..8687d0c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -46,8 +46,8 @@
virtual ~CompositionEngine();
// Create a composition Display
- virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
- virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0;
+ virtual std::shared_ptr<Display> createDisplay(const DisplayCreationArgs&) = 0;
+ virtual std::shared_ptr<Layer> createLayer(const LayerCreationArgs&) = 0;
virtual HWComposer& getHwComposer() const = 0;
virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
@@ -66,6 +66,9 @@
// TODO(b/121291683): These will become private/internal
virtual void preComposition(CompositionRefreshArgs&) = 0;
+
+ // Debugging
+ virtual void dump(std::string&) const = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index dbcd3bd..9193dc0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -47,10 +47,10 @@
virtual void disconnect() = 0;
// Creates a render color mode for the display
- virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+ virtual void createDisplayColorProfile(const DisplayColorProfileCreationArgs&) = 0;
// Creates a render surface for the display
- virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+ virtual void createRenderSurface(const RenderSurfaceCreationArgs&) = 0;
protected:
~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 0778936..ced4227 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -30,9 +30,6 @@
* A parameter object for creating Display instances
*/
struct DisplayCreationArgs {
- // True if this display is secure
- bool isSecure = false;
-
// True if this display is a virtual display
bool isVirtual = false;
@@ -54,17 +51,13 @@
*
* Prefer:
*
- * DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ * DisplayCreationArgsBuilder().setIsVirtual(false)
* .setDisplayId(displayId).build();
*/
class DisplayCreationArgsBuilder {
public:
DisplayCreationArgs build() { return std::move(mArgs); }
- DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
- mArgs.isSecure = isSecure;
- return *this;
- }
DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
mArgs.isVirtual = isVirtual;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d374baa..5f42ea1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -17,9 +17,12 @@
#pragma once
#include <cstdint>
+#include <iterator>
#include <optional>
#include <string>
+#include <type_traits>
#include <unordered_map>
+#include <utility>
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
@@ -55,10 +58,67 @@
*/
class Output {
public:
- using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
using ReleasedLayers = std::vector<wp<LayerFE>>;
using UniqueFELayerStateMap = std::unordered_map<LayerFE*, LayerFECompositionState*>;
+ // A helper class for enumerating the output layers using a C++11 ranged-based for loop
+ template <typename T>
+ class OutputLayersEnumerator {
+ public:
+ // TODO(lpique): Consider turning this into a C++20 view when possible.
+ template <bool IsConstIter>
+ class IteratorImpl {
+ public:
+ // Required definitions to be considered an iterator
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = decltype(std::declval<T>().getOutputLayerOrderedByZByIndex(0));
+ using difference_type = std::ptrdiff_t;
+ using pointer = std::conditional_t<IsConstIter, const value_type*, value_type*>;
+ using reference = std::conditional_t<IsConstIter, const value_type&, value_type&>;
+
+ IteratorImpl() = default;
+ IteratorImpl(const T* output, size_t index) : mOutput(output), mIndex(index) {}
+
+ value_type operator*() const {
+ return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+ }
+ value_type operator->() const {
+ return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+ }
+
+ bool operator==(const IteratorImpl& other) const {
+ return mOutput == other.mOutput && mIndex == other.mIndex;
+ }
+ bool operator!=(const IteratorImpl& other) const { return !operator==(other); }
+
+ IteratorImpl& operator++() {
+ ++mIndex;
+ return *this;
+ }
+ IteratorImpl operator++(int) {
+ auto prev = *this;
+ ++mIndex;
+ return prev;
+ }
+
+ private:
+ const T* mOutput{nullptr};
+ size_t mIndex{0};
+ };
+
+ using iterator = IteratorImpl<false>;
+ using const_iterator = IteratorImpl<true>;
+
+ explicit OutputLayersEnumerator(const T& output) : mOutput(output) {}
+ auto begin() const { return iterator(&mOutput, 0); }
+ auto end() const { return iterator(&mOutput, mOutput.getOutputLayerCount()); }
+ auto cbegin() const { return const_iterator(&mOutput, 0); }
+ auto cend() const { return const_iterator(&mOutput, mOutput.getOutputLayerCount()); }
+
+ private:
+ const T& mOutput;
+ };
+
struct FrameFences {
sp<Fence> presentFence{Fence::NO_FENCE};
sp<Fence> clientTargetAcquireFence{Fence::NO_FENCE};
@@ -95,6 +155,9 @@
// constructor.
virtual bool isValid() const = 0;
+ // Returns the DisplayId the output represents, if it has one
+ virtual std::optional<DisplayId> getDisplayId() const = 0;
+
// Enables (or disables) composition on this output
virtual void setCompositionEnabled(bool) = 0;
@@ -152,34 +215,32 @@
virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
// Determines if a layer belongs to the output.
- virtual bool belongsInOutput(const compositionengine::Layer*) const = 0;
+ virtual bool belongsInOutput(const Layer*) const = 0;
// Returns a pointer to the output layer corresponding to the given layer on
// this output, or nullptr if the layer does not have one
virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
- // Creates an OutputLayer instance for this output
- virtual std::unique_ptr<OutputLayer> createOutputLayer(const std::shared_ptr<Layer>&,
- const sp<LayerFE>&) const = 0;
+ // Immediately clears all layers from the output.
+ virtual void clearOutputLayers() = 0;
- // Gets the OutputLayer corresponding to the input Layer instance from the
- // current ordered set of output layers. If there is no such layer, a new
- // one is created and returned.
- virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::shared_ptr<Layer>,
- sp<LayerFE>) = 0;
+ // For tests use only. Creates and appends an OutputLayer into the output.
+ virtual OutputLayer* injectOutputLayerForTest(const std::shared_ptr<Layer>&,
+ const sp<LayerFE>&) = 0;
- // Sets the new ordered set of output layers for this output
- virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0;
+ // Gets the count of output layers managed by this output
+ virtual size_t getOutputLayerCount() const = 0;
- // Gets the ordered set of output layers for this output
- virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
+ // Gets an output layer in Z order given its index
+ virtual OutputLayer* getOutputLayerOrderedByZByIndex(size_t) const = 0;
+
+ // A helper function for enumerating all the output layers in Z order using
+ // a C++11 range-based for loop.
+ auto getOutputLayersOrderedByZ() const { return OutputLayersEnumerator(*this); }
// Sets the new set of layers being released this frame
virtual void setReleasedLayers(ReleasedLayers&&) = 0;
- // Takes (moves) the set of layers being released this frame.
- virtual ReleasedLayers takeReleasedLayers() = 0;
-
// Prepare the output, updating the OutputLayers used in the output
virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
@@ -193,12 +254,10 @@
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
- virtual void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&,
- LayerFESet&) = 0;
+ virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
- virtual std::unique_ptr<OutputLayer> getOutputLayerIfVisible(
- std::shared_ptr<compositionengine::Layer>, CoverageState&) = 0;
- virtual void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) = 0;
+ virtual void ensureOutputLayerIfVisible(std::shared_ptr<Layer>, CoverageState&) = 0;
+ virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 389b605..a9a95cd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -74,7 +74,7 @@
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
- virtual void updateCompositionState(bool includeGeometry) = 0;
+ virtual void updateCompositionState(bool includeGeometry, bool forceClientComposition) = 0;
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 6859846..5ce2fdc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -78,7 +78,7 @@
// Queues the drawn buffer for consumption by HWC. readyFence is the fence
// which will fire when the buffer is ready for consumption.
- virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
+ virtual void queueBuffer(base::unique_fd readyFence) = 0;
// Called after the HWC calls are made to present the display
virtual void onPresentDisplayCompleted() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 6340b14..f416c9c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -26,9 +26,9 @@
~CompositionEngine() override;
std::shared_ptr<compositionengine::Display> createDisplay(
- compositionengine::DisplayCreationArgs&&) override;
+ const compositionengine::DisplayCreationArgs&) override;
std::shared_ptr<compositionengine::Layer> createLayer(
- compositionengine::LayerCreationArgs&&) override;
+ const compositionengine::LayerCreationArgs&) override;
HWComposer& getHwComposer() const override;
void setHwComposer(std::unique_ptr<HWComposer>) override;
@@ -45,6 +45,9 @@
void preComposition(CompositionRefreshArgs&) override;
+ // Debugging
+ void dump(std::string&) const override;
+
void updateLayerStateFromFE(CompositionRefreshArgs& args);
// Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index b5d8325..ace876c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -17,6 +17,7 @@
#pragma once
#include <compositionengine/Display.h>
+#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/impl/Output.h>
#include <memory>
@@ -29,36 +30,36 @@
class CompositionEngine;
-struct DisplayCreationArgs;
-
namespace impl {
-class Display : public compositionengine::impl::Output, public compositionengine::Display {
+// The implementation class contains the common implementation, but does not
+// actually contain the final display state.
+class Display : public compositionengine::impl::Output, public virtual compositionengine::Display {
public:
- Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+ explicit Display(const compositionengine::DisplayCreationArgs&);
virtual ~Display();
// compositionengine::Output overrides
+ std::optional<DisplayId> getDisplayId() const override;
void dump(std::string&) const override;
- std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
- const std::shared_ptr<Layer>&, const sp<LayerFE>&) const override;
using compositionengine::impl::Output::setReleasedLayers;
- void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
- void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
+ void setReleasedLayers(const CompositionRefreshArgs&) override;
+ void setColorTransform(const CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
void chooseCompositionStrategy() override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
void setExpensiveRenderingExpected(bool) override;
- void finishFrame(const compositionengine::CompositionRefreshArgs&) override;
+ void finishFrame(const CompositionRefreshArgs&) override;
// compositionengine::Display overrides
const std::optional<DisplayId>& getId() const override;
bool isSecure() const override;
bool isVirtual() const override;
void disconnect() override;
- void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
- void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+ void createDisplayColorProfile(
+ const compositionengine::DisplayColorProfileCreationArgs&) override;
+ void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
@@ -70,14 +71,28 @@
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
+ // Internal
+ std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const;
+
private:
const bool mIsVirtual;
std::optional<DisplayId> mId;
Hwc2::PowerAdvisor* const mPowerAdvisor{nullptr};
};
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseDisplay, typename CompositionEngine, typename DisplayCreationArgs>
+std::shared_ptr<BaseDisplay> createDisplayTemplated(const CompositionEngine& compositionEngine,
+ const DisplayCreationArgs& args) {
+ return createOutputTemplated<BaseDisplay>(compositionEngine, args);
+}
+
std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&,
- compositionengine::DisplayCreationArgs&&);
+ const compositionengine::DisplayCreationArgs&);
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index e84a36e..9bc0e68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -33,7 +33,7 @@
class DisplayColorProfile : public compositionengine::DisplayColorProfile {
public:
- DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+ DisplayColorProfile(const DisplayColorProfileCreationArgs&);
~DisplayColorProfile() override;
bool isValid() const override;
@@ -91,7 +91,7 @@
};
std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
- DisplayColorProfileCreationArgs&&);
+ const DisplayColorProfileCreationArgs&);
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
index d441c9c..46489fb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
@@ -19,43 +19,66 @@
#include <memory>
#include <compositionengine/Layer.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <utils/RefBase.h>
+#include <compositionengine/LayerCreationArgs.h>
#include <utils/StrongPointer.h>
namespace android::compositionengine {
-class CompositionEngine;
-class LayerFE;
-
struct LayerCreationArgs;
namespace impl {
-class Display;
-
-class Layer : public compositionengine::Layer {
+// The implementation class contains the common implementation, but does not
+// actually contain the final layer state.
+class Layer : public virtual compositionengine::Layer {
public:
- Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&);
~Layer() override;
- sp<LayerFE> getLayerFE() const override;
+ // compositionengine::Layer overrides
+ void dump(std::string&) const override;
- const LayerFECompositionState& getFEState() const override;
- LayerFECompositionState& editFEState() override;
-
- void dump(std::string& result) const override;
-
-private:
- const compositionengine::CompositionEngine& mCompositionEngine;
- const wp<LayerFE> mLayerFE;
-
- // State obtained from calls to LayerFE::getCompositionState
- LayerFECompositionState mFrontEndState;
+protected:
+ // Implemented by the final implementation for the final state it uses.
+ virtual void dumpFEState(std::string&) const = 0;
};
-std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
- compositionengine::LayerCreationArgs&&);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseLayer, typename LayerCreationArgs>
+std::shared_ptr<BaseLayer> createLayerTemplated(const LayerCreationArgs& args) {
+ class Layer final : public BaseLayer {
+ public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+ using LayerFE = std::remove_pointer_t<decltype(
+ std::declval<decltype(std::declval<LayerCreationArgs>().layerFE)>().unsafe_get())>;
+ using LayerFECompositionState = std::remove_const_t<
+ std::remove_reference_t<decltype(std::declval<BaseLayer>().getFEState())>>;
+#pragma clang diagnostic pop
+
+ explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {}
+ ~Layer() override = default;
+
+ private:
+ // compositionengine::Layer overrides
+ sp<compositionengine::LayerFE> getLayerFE() const override { return mLayerFE.promote(); }
+ const LayerFECompositionState& getFEState() const override { return mFrontEndState; }
+ LayerFECompositionState& editFEState() override { return mFrontEndState; }
+
+ // compositionengine::impl::Layer overrides
+ void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); }
+
+ const wp<LayerFE> mLayerFE;
+ LayerFECompositionState mFrontEndState;
+ };
+
+ return std::make_shared<Layer>(args);
+}
+
+std::shared_ptr<Layer> createLayer(const LayerCreationArgs&);
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index fa6cd27..a2342ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,28 +16,25 @@
#pragma once
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
#include <memory>
#include <utility>
#include <vector>
-#include <compositionengine/Output.h>
-#include <compositionengine/impl/OutputCompositionState.h>
+namespace android::compositionengine::impl {
-namespace android::compositionengine {
-
-class CompositionEngine;
-class Layer;
-class OutputLayer;
-
-namespace impl {
-
+// The implementation class contains the common implementation, but does not
+// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
- Output(const CompositionEngine&);
~Output() override;
+ // compositionengine::Output overrides
bool isValid() const override;
-
+ std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
@@ -58,34 +55,23 @@
compositionengine::RenderSurface* getRenderSurface() const override;
void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
- const OutputCompositionState& getState() const override;
- OutputCompositionState& editState() override;
-
Region getDirtyRegion(bool repaintEverything) const override;
bool belongsInOutput(std::optional<uint32_t>, bool) const override;
bool belongsInOutput(const compositionengine::Layer*) const override;
compositionengine::OutputLayer* getOutputLayerForLayer(
compositionengine::Layer*) const override;
- std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
- const std::shared_ptr<Layer>&, const sp<LayerFE>&) const override;
- std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer(
- std::shared_ptr<compositionengine::Layer>, sp<LayerFE>) override;
- void setOutputLayersOrderedByZ(OutputLayers&&) override;
- const OutputLayers& getOutputLayersOrderedByZ() const override;
void setReleasedLayers(ReleasedLayers&&) override;
- ReleasedLayers takeReleasedLayers() override;
- void prepare(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
- void present(const compositionengine::CompositionRefreshArgs&) override;
+ void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
+ void present(const CompositionRefreshArgs&) override;
- void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
- void collectVisibleLayers(const compositionengine::CompositionRefreshArgs&,
+ void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
+ void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
- std::unique_ptr<compositionengine::OutputLayer> getOutputLayerIfVisible(
- std::shared_ptr<compositionengine::Layer>,
- compositionengine::Output::CoverageState&) override;
+ void ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer>,
+ compositionengine::Output::CoverageState&) override;
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
@@ -93,8 +79,8 @@
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
- void devOptRepaintFlash(const compositionengine::CompositionRefreshArgs&) override;
- void finishFrame(const compositionengine::CompositionRefreshArgs&) override;
+ void devOptRepaintFlash(const CompositionRefreshArgs&) override;
+ void finishFrame(const CompositionRefreshArgs&) override;
std::optional<base::unique_fd> composeSurfaces(const Region&) override;
void postFramebuffer() override;
@@ -104,9 +90,9 @@
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
protected:
- const CompositionEngine& getCompositionEngine() const;
- std::unique_ptr<compositionengine::OutputLayer> takeOutputLayerForLayer(
- compositionengine::Layer*);
+ std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const;
+ std::optional<size_t> findCurrentOutputLayerForLayer(compositionengine::Layer*) const;
void chooseCompositionStrategy() override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
@@ -117,24 +103,128 @@
void setExpensiveRenderingExpected(bool enabled) override;
void dumpBase(std::string&) const;
+ // Implemented by the final implementation for the final state it uses.
+ virtual compositionengine::OutputLayer* ensureOutputLayer(
+ std::optional<size_t>, const std::shared_ptr<compositionengine::Layer>&,
+ const sp<LayerFE>&) = 0;
+ virtual compositionengine::OutputLayer* injectOutputLayerForTest(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) = 0;
+ virtual void finalizePendingOutputLayers() = 0;
+ virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
+ virtual void dumpState(std::string& out) const = 0;
+
private:
void dirtyEntireOutput();
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
- const CompositionEngine& mCompositionEngine;
-
std::string mName;
- OutputCompositionState mState;
-
std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
- OutputLayers mOutputLayersOrderedByZ;
ReleasedLayers mReleasedLayers;
};
-} // namespace impl
-} // namespace android::compositionengine
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutput, typename CompositionEngine, typename... Args>
+std::shared_ptr<BaseOutput> createOutputTemplated(const CompositionEngine& compositionEngine,
+ Args... args) {
+ class Output final : public BaseOutput {
+ public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+
+ using OutputCompositionState = std::remove_const_t<
+ std::remove_reference_t<decltype(std::declval<BaseOutput>().getState())>>;
+ using OutputLayer = std::remove_pointer_t<decltype(
+ std::declval<BaseOutput>().getOutputLayerOrderedByZByIndex(0))>;
+
+#pragma clang diagnostic pop
+
+ explicit Output(const CompositionEngine& compositionEngine, Args... args)
+ : BaseOutput(std::forward<Args>(args)...), mCompositionEngine(compositionEngine) {}
+ ~Output() override = default;
+
+ private:
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+
+ OutputCompositionState& editState() override { return mState; }
+
+ size_t getOutputLayerCount() const override {
+ return mCurrentOutputLayersOrderedByZ.size();
+ }
+
+ OutputLayer* getOutputLayerOrderedByZByIndex(size_t index) const override {
+ if (index >= mCurrentOutputLayersOrderedByZ.size()) {
+ return nullptr;
+ }
+ return mCurrentOutputLayersOrderedByZ[index].get();
+ }
+
+ // compositionengine::impl::Output overrides
+ const CompositionEngine& getCompositionEngine() const override {
+ return mCompositionEngine;
+ };
+
+ OutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex,
+ const std::shared_ptr<compositionengine::Layer>& layer,
+ const sp<LayerFE>& layerFE) {
+ auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())
+ ? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex])
+ : BaseOutput::createOutputLayer(layer, layerFE);
+ auto result = outputLayer.get();
+ mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+ return result;
+ }
+
+ void finalizePendingOutputLayers() override {
+ // The pending layers are added in reverse order. Reverse them to
+ // get the back-to-front ordered list of layers.
+ std::reverse(mPendingOutputLayersOrderedByZ.begin(),
+ mPendingOutputLayersOrderedByZ.end());
+
+ mCurrentOutputLayersOrderedByZ = std::move(mPendingOutputLayersOrderedByZ);
+ }
+
+ void dumpState(std::string& out) const override { mState.dump(out); }
+
+ OutputLayer* injectOutputLayerForTest(
+ const std::shared_ptr<compositionengine::Layer>& layer,
+ const sp<LayerFE>& layerFE) override {
+ auto outputLayer = BaseOutput::createOutputLayer(layer, layerFE);
+ auto result = outputLayer.get();
+ mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+ return result;
+ }
+
+ // Note: This is declared as a private virtual non-override so it can be
+ // an override implementation in the unit tests, but otherwise is not an
+ // accessible override for the normal implementation.
+ virtual void injectOutputLayerForTest(std::unique_ptr<OutputLayer> outputLayer) {
+ mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+ }
+
+ void clearOutputLayers() override {
+ mCurrentOutputLayersOrderedByZ.clear();
+ mPendingOutputLayersOrderedByZ.clear();
+ }
+
+ const CompositionEngine& mCompositionEngine;
+ OutputCompositionState mState;
+ std::vector<std::unique_ptr<OutputLayer>> mCurrentOutputLayersOrderedByZ;
+ std::vector<std::unique_ptr<OutputLayer>> mPendingOutputLayersOrderedByZ;
+ };
+
+ return std::make_shared<Output>(compositionEngine, std::forward<Args>(args)...);
+}
+
+std::shared_ptr<Output> createOutput(const compositionengine::CompositionEngine&);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 1199fea..95c8afb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,7 +20,6 @@
#include <string>
#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -32,22 +31,15 @@
namespace impl {
-class OutputLayer : public compositionengine::OutputLayer {
+// The implementation class contains the common implementation, but does not
+// actually contain the final layer state.
+class OutputLayer : public virtual compositionengine::OutputLayer {
public:
- OutputLayer(const compositionengine::Output&, const std::shared_ptr<compositionengine::Layer>&,
- const sp<compositionengine::LayerFE>&);
~OutputLayer() override;
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
- const compositionengine::Output& getOutput() const override;
- compositionengine::Layer& getLayer() const override;
- compositionengine::LayerFE& getLayerFE() const override;
-
- const OutputLayerCompositionState& getState() const override;
- OutputLayerCompositionState& editState() override;
-
- void updateCompositionState(bool) override;
+ void updateCompositionState(bool includeGeometry, bool forceClientComposition) override;
void writeStateToHWC(bool) override;
void writeCursorPositionToHWC() const override;
@@ -59,12 +51,16 @@
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
- void dump(std::string& result) const override;
+ void dump(std::string&) const override;
virtual FloatRect calculateOutputSourceCrop() const;
virtual Rect calculateOutputDisplayFrame() const;
virtual uint32_t calculateOutputRelativeBufferTransform() const;
+protected:
+ // Implemented by the final implementation for the final state it uses.
+ virtual void dumpState(std::string&) const = 0;
+
private:
Rect calculateInitialCrop() const;
void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
@@ -77,17 +73,60 @@
void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
Hwc2::IComposerClient::Composition to) const;
-
- const compositionengine::Output& mOutput;
- std::shared_ptr<compositionengine::Layer> mLayer;
- sp<compositionengine::LayerFE> mLayerFE;
-
- OutputLayerCompositionState mState;
};
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
- const compositionengine::Output&, const std::shared_ptr<compositionengine::Layer>&,
- const sp<compositionengine::LayerFE>&);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutputLayer>
+std::unique_ptr<BaseOutputLayer> createOutputLayerTemplated(const Output& output,
+ std::shared_ptr<Layer> layer,
+ sp<LayerFE> layerFE) {
+ class OutputLayer final : public BaseOutputLayer {
+ public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+
+ using OutputLayerCompositionState = std::remove_const_t<
+ std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getState())>>;
+ using Output = std::remove_const_t<
+ std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getOutput())>>;
+ using Layer = std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayer())>;
+ using LayerFE =
+ std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayerFE())>;
+
+#pragma clang diagnostic pop
+
+ OutputLayer(const Output& output, const std::shared_ptr<Layer>& layer,
+ const sp<LayerFE>& layerFE)
+ : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+ ~OutputLayer() override = default;
+
+ private:
+ // compositionengine::OutputLayer overrides
+ const Output& getOutput() const override { return mOutput; }
+ Layer& getLayer() const override { return *mLayer; }
+ LayerFE& getLayerFE() const override { return *mLayerFE; }
+ const OutputLayerCompositionState& getState() const override { return mState; }
+ OutputLayerCompositionState& editState() override { return mState; }
+
+ // compositionengine::impl::OutputLayer overrides
+ void dumpState(std::string& out) const override { mState.dump(out); }
+
+ const Output& mOutput;
+ const std::shared_ptr<Layer> mLayer;
+ const sp<LayerFE> mLayerFE;
+ OutputLayerCompositionState mState;
+ };
+
+ return std::make_unique<OutputLayer>(output, layer, layerFE);
+}
+
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output&,
+ const std::shared_ptr<compositionengine::Layer>&,
+ const sp<LayerFE>&);
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 0a04462..692d78d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -39,7 +39,7 @@
class RenderSurface : public compositionengine::RenderSurface {
public:
RenderSurface(const CompositionEngine&, compositionengine::Display&,
- compositionengine::RenderSurfaceCreationArgs&&);
+ const compositionengine::RenderSurfaceCreationArgs&);
~RenderSurface() override;
bool isValid() const override;
@@ -54,7 +54,7 @@
status_t beginFrame(bool mustRecompose) override;
void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
- void queueBuffer(base::unique_fd&& readyFence) override;
+ void queueBuffer(base::unique_fd readyFence) override;
void onPresentDisplayCompleted() override;
void flip() override;
@@ -84,7 +84,7 @@
std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
const compositionengine::CompositionEngine&, compositionengine::Display&,
- compositionengine::RenderSurfaceCreationArgs&&);
+ const compositionengine::RenderSurfaceCreationArgs&);
} // namespace impl
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index e3254ac..8e6f2e2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -32,8 +32,8 @@
CompositionEngine();
~CompositionEngine() override;
- MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
- MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&));
+ MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(const DisplayCreationArgs&));
+ MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(const LayerCreationArgs&));
MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
@@ -48,6 +48,8 @@
MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index d763aa6..57f33ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -38,8 +38,8 @@
MOCK_METHOD0(disconnect, void());
- MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
- MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+ MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
+ MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 286a20f..02e68fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -34,6 +34,7 @@
virtual ~Output();
MOCK_CONST_METHOD0(isValid, bool());
+ MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD6(setProjection,
@@ -64,20 +65,14 @@
MOCK_CONST_METHOD1(getOutputLayerForLayer,
compositionengine::OutputLayer*(compositionengine::Layer*));
- MOCK_CONST_METHOD2(createOutputLayer,
- std::unique_ptr<compositionengine::OutputLayer>(
- const std::shared_ptr<compositionengine::Layer>&,
- const sp<compositionengine::LayerFE>&));
- MOCK_METHOD2(getOrCreateOutputLayer,
- std::unique_ptr<compositionengine::OutputLayer>(
- std::shared_ptr<compositionengine::Layer>,
- sp<compositionengine::LayerFE>));
-
- MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
- MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(const std::shared_ptr<compositionengine::Layer>&,
+ const sp<compositionengine::LayerFE>&));
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
- MOCK_METHOD0(takeReleasedLayers, ReleasedLayers());
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
@@ -87,10 +82,9 @@
MOCK_METHOD2(collectVisibleLayers,
void(const compositionengine::CompositionRefreshArgs&,
compositionengine::Output::CoverageState&));
- MOCK_METHOD2(getOutputLayerIfVisible,
- std::unique_ptr<compositionengine::OutputLayer>(
- std::shared_ptr<compositionengine::Layer>,
- compositionengine::Output::CoverageState&));
+ MOCK_METHOD2(ensureOutputLayerIfVisible,
+ void(std::shared_ptr<compositionengine::Layer>,
+ compositionengine::Output::CoverageState&));
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 4f2afac..631760a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -40,7 +40,7 @@
MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
- MOCK_METHOD1(updateCompositionState, void(bool));
+ MOCK_METHOD2(updateCompositionState, void(bool, bool));
MOCK_METHOD1(writeStateToHWC, void(bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ba6746a..ed4d492 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,7 +39,7 @@
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD2(prepareFrame, void(bool, bool));
MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
- MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+ MOCK_METHOD1(queueBuffer, void(base::unique_fd));
MOCK_METHOD0(onPresentDisplayCompleted, void());
MOCK_METHOD0(flip, void());
MOCK_CONST_METHOD1(dump, void(std::string& result));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 8391458..be8646c 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -39,12 +39,13 @@
CompositionEngine::~CompositionEngine() = default;
std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
- DisplayCreationArgs&& args) {
- return compositionengine::impl::createDisplay(*this, std::move(args));
+ const DisplayCreationArgs& args) {
+ return compositionengine::impl::createDisplay(*this, args);
}
-std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) {
- return compositionengine::impl::createLayer(*this, std::move(args));
+std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(
+ const LayerCreationArgs& args) {
+ return compositionengine::impl::createLayer(args);
}
HWComposer& CompositionEngine::getHwComposer() const {
@@ -100,7 +101,7 @@
uniqueVisibleLayers;
for (const auto& output : args.outputs) {
- for (auto& layer : output->getOutputLayersOrderedByZ()) {
+ for (auto* layer : output->getOutputLayersOrderedByZ()) {
if (layer->isHardwareCursor()) {
// Latch the cursor composition state from each front-end layer.
layer->getLayerFE().latchCursorCompositionState(layer->getLayer().editFEState());
@@ -129,6 +130,10 @@
mNeedsAnotherUpdate = needsAnotherUpdate;
}
+void CompositionEngine::dump(std::string&) const {
+ // The base class has no state to dump, but derived classes might.
+}
+
void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) {
mNeedsAnotherUpdate = value;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index fe8fa21..e885629 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -34,17 +34,12 @@
std::shared_ptr<Display> createDisplay(
const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::DisplayCreationArgs&& args) {
- return std::make_shared<Display>(compositionEngine, std::move(args));
+ const compositionengine::DisplayCreationArgs& args) {
+ return createDisplayTemplated<Display>(compositionEngine, args);
}
-Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
- : compositionengine::impl::Output(compositionEngine),
- mIsVirtual(args.isVirtual),
- mId(args.displayId),
- mPowerAdvisor(args.powerAdvisor) {
- editState().isSecure = args.isSecure;
-}
+Display::Display(const DisplayCreationArgs& args)
+ : mIsVirtual(args.isVirtual), mId(args.displayId), mPowerAdvisor(args.powerAdvisor) {}
Display::~Display() = default;
@@ -60,6 +55,10 @@
return mIsVirtual;
}
+std::optional<DisplayId> Display::getDisplayId() const {
+ return mId;
+}
+
void Display::disconnect() {
if (!mId) {
return;
@@ -125,19 +124,19 @@
Output::dumpBase(out);
}
-void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
- setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+void Display::createDisplayColorProfile(const DisplayColorProfileCreationArgs& args) {
+ setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(args));
}
-void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
- setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
- std::move(args)));
+void Display::createRenderSurface(const RenderSurfaceCreationArgs& args) {
+ setRenderSurface(
+ compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, args));
}
std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
const std::shared_ptr<compositionengine::Layer>& layer,
const sp<compositionengine::LayerFE>& layerFE) const {
- auto result = Output::createOutputLayer(layer, layerFE);
+ auto result = impl::createOutputLayer(*this, layer, layerFE);
if (result && mId) {
auto& hwc = getCompositionEngine().getHwComposer();
@@ -172,7 +171,7 @@
// Any non-null entries in the current list of layers are layers that are no
// longer going to be visible
- for (auto& layer : getOutputLayersOrderedByZ()) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
if (!layer) {
continue;
}
@@ -235,14 +234,14 @@
}
bool Display::anyLayersRequireClientComposition() const {
- const auto& layers = getOutputLayersOrderedByZ();
- return std::any_of(layers.cbegin(), layers.cend(),
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::any_of(layers.begin(), layers.end(),
[](const auto& layer) { return layer->requiresClientComposition(); });
}
bool Display::allLayersRequireClientComposition() const {
- const auto& layers = getOutputLayersOrderedByZ();
- return std::all_of(layers.cbegin(), layers.cend(),
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::all_of(layers.begin(), layers.end(),
[](const auto& layer) { return layer->requiresClientComposition(); });
}
@@ -251,7 +250,7 @@
return;
}
- for (auto& layer : getOutputLayersOrderedByZ()) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
auto hwcLayer = layer->getHwcLayer();
if (!hwcLayer) {
continue;
@@ -272,7 +271,7 @@
}
void Display::applyLayerRequestsToLayers(const LayerRequests& layerRequests) {
- for (auto& layer : getOutputLayersOrderedByZ()) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
layer->prepareForDeviceLayerRequests();
auto hwcLayer = layer->getHwcLayer();
@@ -300,7 +299,7 @@
result.presentFence = hwc.getPresentFence(*mId);
// TODO(b/121291683): Change HWComposer call to return entire map
- for (const auto& layer : getOutputLayersOrderedByZ()) {
+ for (const auto* layer : getOutputLayersOrderedByZ()) {
auto hwcLayer = layer->getHwcLayer();
if (!hwcLayer) {
continue;
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 5ef9097..a7c4512 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -184,11 +184,11 @@
} // anonymous namespace
std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
- DisplayColorProfileCreationArgs&& args) {
- return std::make_unique<DisplayColorProfile>(std::move(args));
+ const DisplayColorProfileCreationArgs& args) {
+ return std::make_unique<DisplayColorProfile>(args);
}
-DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs& args)
: mHasWideColorGamut(args.hasWideColorGamut),
mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
populateColorModes(args.hwcColorModes);
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
index 8a1cbe4..ecacaee 100644
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -15,9 +15,8 @@
*/
#include <android-base/stringprintf.h>
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerCreationArgs.h>
#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Layer.h>
namespace android::compositionengine {
@@ -26,38 +25,18 @@
namespace impl {
-std::shared_ptr<compositionengine::Layer> createLayer(
- const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::LayerCreationArgs&& args) {
- return std::make_shared<Layer>(compositionEngine, std::move(args));
-}
-
-Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args)
- : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) {
- static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning
+std::shared_ptr<Layer> createLayer(const LayerCreationArgs& args) {
+ return compositionengine::impl::createLayerTemplated<Layer>(args);
}
Layer::~Layer() = default;
-sp<LayerFE> Layer::getLayerFE() const {
- return mLayerFE.promote();
-}
-
-const compositionengine::LayerFECompositionState& Layer::getFEState() const {
- return mFrontEndState;
-}
-
-compositionengine::LayerFECompositionState& Layer::editFEState() {
- return mFrontEndState;
-}
-
void Layer::dump(std::string& out) const {
auto layerFE = getLayerFE();
android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
layerFE ? layerFE->getDebugName() : "<unknown>");
-
out.append(" frontend:\n");
- mFrontEndState.dump(out);
+ dumpFEState(out);
}
} // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 02ebc1f..aa638b7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -25,7 +25,9 @@
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
#include <ui/DebugUtils.h>
@@ -61,20 +63,22 @@
} // namespace
-Output::Output(const CompositionEngine& compositionEngine)
- : mCompositionEngine(compositionEngine) {}
+std::shared_ptr<Output> createOutput(
+ const compositionengine::CompositionEngine& compositionEngine) {
+ return createOutputTemplated<Output>(compositionEngine);
+}
Output::~Output() = default;
-const CompositionEngine& Output::getCompositionEngine() const {
- return mCompositionEngine;
-}
-
bool Output::isValid() const {
return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
mRenderSurface->isValid();
}
+std::optional<DisplayId> Output::getDisplayId() const {
+ return {};
+}
+
const std::string& Output::getName() const {
return mName;
}
@@ -84,22 +88,24 @@
}
void Output::setCompositionEnabled(bool enabled) {
- if (mState.isEnabled == enabled) {
+ auto& outputState = editState();
+ if (outputState.isEnabled == enabled) {
return;
}
- mState.isEnabled = enabled;
+ outputState.isEnabled = enabled;
dirtyEntireOutput();
}
void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) {
- mState.transform = transform;
- mState.orientation = orientation;
- mState.scissor = scissor;
- mState.frame = frame;
- mState.viewport = viewport;
- mState.needsFiltering = needsFiltering;
+ auto& outputState = editState();
+ outputState.transform = transform;
+ outputState.orientation = orientation;
+ outputState.scissor = scissor;
+ outputState.frame = frame;
+ outputState.viewport = viewport;
+ outputState.needsFiltering = needsFiltering;
dirtyEntireOutput();
}
@@ -107,44 +113,48 @@
// TODO(b/121291683): Rename setSize() once more is moved.
void Output::setBounds(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- // TODO(b/121291683): Rename mState.size once more is moved.
- mState.bounds = Rect(mRenderSurface->getSize());
+ // TODO(b/121291683): Rename outputState.size once more is moved.
+ editState().bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
- mState.layerStackId = layerStackId;
- mState.layerStackInternal = isInternal;
+ auto& outputState = editState();
+ outputState.layerStackId = layerStackId;
+ outputState.layerStackInternal = isInternal;
dirtyEntireOutput();
}
void Output::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
- if (!args.colorTransformMatrix || mState.colorTransformMatrix == *args.colorTransformMatrix) {
+ auto& colorTransformMatrix = editState().colorTransformMatrix;
+ if (!args.colorTransformMatrix || colorTransformMatrix == args.colorTransformMatrix) {
return;
}
- mState.colorTransformMatrix = *args.colorTransformMatrix;
+ colorTransformMatrix = *args.colorTransformMatrix;
dirtyEntireOutput();
}
void Output::setColorProfile(const ColorProfile& colorProfile) {
- const ui::Dataspace targetDataspace =
+ ui::Dataspace targetDataspace =
getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
colorProfile.colorSpaceAgnosticDataspace);
- if (mState.colorMode == colorProfile.mode && mState.dataspace == colorProfile.dataspace &&
- mState.renderIntent == colorProfile.renderIntent &&
- mState.targetDataspace == targetDataspace) {
+ auto& outputState = editState();
+ if (outputState.colorMode == colorProfile.mode &&
+ outputState.dataspace == colorProfile.dataspace &&
+ outputState.renderIntent == colorProfile.renderIntent &&
+ outputState.targetDataspace == targetDataspace) {
return;
}
- mState.colorMode = colorProfile.mode;
- mState.dataspace = colorProfile.dataspace;
- mState.renderIntent = colorProfile.renderIntent;
- mState.targetDataspace = targetDataspace;
+ outputState.colorMode = colorProfile.mode;
+ outputState.dataspace = colorProfile.dataspace;
+ outputState.renderIntent = colorProfile.renderIntent;
+ outputState.targetDataspace = targetDataspace;
mRenderSurface->setBufferDataspace(colorProfile.dataspace);
@@ -166,7 +176,7 @@
}
void Output::dumpBase(std::string& out) const {
- mState.dump(out);
+ dumpState(out);
if (mDisplayColorProfile) {
mDisplayColorProfile->dump(out);
@@ -180,8 +190,8 @@
out.append(" No render surface!\n");
}
- android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size());
- for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+ android::base::StringAppendF(&out, "\n %zu Layers\n", getOutputLayerCount());
+ for (const auto* outputLayer : getOutputLayersOrderedByZ()) {
if (!outputLayer) {
continue;
}
@@ -212,7 +222,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- mState.bounds = Rect(mRenderSurface->getSize());
+ editState().bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -221,18 +231,11 @@
mRenderSurface = std::move(surface);
}
-const OutputCompositionState& Output::getState() const {
- return mState;
-}
-
-OutputCompositionState& Output::editState() {
- return mState;
-}
-
Region Output::getDirtyRegion(bool repaintEverything) const {
- Region dirty(mState.viewport);
+ const auto& outputState = getState();
+ Region dirty(outputState.viewport);
if (!repaintEverything) {
- dirty.andSelf(mState.dirtyRegion);
+ dirty.andSelf(outputState.dirtyRegion);
}
return dirty;
}
@@ -240,8 +243,9 @@
bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
// The layerStackId's must match, and also the layer must not be internal
// only when not on an internal output.
- return layerStackId && (*layerStackId == mState.layerStackId) &&
- (!internalOnly || mState.layerStackInternal);
+ const auto& outputState = getState();
+ return layerStackId && (*layerStackId == outputState.layerStackId) &&
+ (!internalOnly || outputState.layerStackInternal);
}
bool Output::belongsInOutput(const compositionengine::Layer* layer) const {
@@ -253,58 +257,32 @@
return belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly);
}
-compositionengine::OutputLayer* Output::getOutputLayerForLayer(
- compositionengine::Layer* layer) const {
- for (const auto& outputLayer : mOutputLayersOrderedByZ) {
- if (outputLayer && &outputLayer->getLayer() == layer) {
- return outputLayer.get();
- }
- }
- return nullptr;
-}
-
-std::unique_ptr<compositionengine::OutputLayer> Output::takeOutputLayerForLayer(
- compositionengine::Layer* layer) {
- // Removes the outputLayer from mOutputLayersorderedByZ and transfers ownership to the caller.
- for (auto& outputLayer : mOutputLayersOrderedByZ) {
- if (outputLayer && &outputLayer->getLayer() == layer) {
- return std::move(outputLayer);
- }
- }
- return nullptr;
-}
-
-std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
- std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
- auto result = takeOutputLayerForLayer(layer.get());
- if (!result) {
- result = createOutputLayer(layer, layerFE);
- }
- return result;
-}
-
std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
- const std::shared_ptr<compositionengine::Layer>& layer,
- const sp<compositionengine::LayerFE>& layerFE) const {
+ const std::shared_ptr<compositionengine::Layer>& layer, const sp<LayerFE>& layerFE) const {
return impl::createOutputLayer(*this, layer, layerFE);
}
-void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
- mOutputLayersOrderedByZ = std::move(layers);
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(
+ compositionengine::Layer* layer) const {
+ auto index = findCurrentOutputLayerForLayer(layer);
+ return index ? getOutputLayerOrderedByZByIndex(*index) : nullptr;
}
-const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
- return mOutputLayersOrderedByZ;
+std::optional<size_t> Output::findCurrentOutputLayerForLayer(
+ compositionengine::Layer* layer) const {
+ for (size_t i = 0; i < getOutputLayerCount(); i++) {
+ auto outputLayer = getOutputLayerOrderedByZByIndex(i);
+ if (outputLayer && &outputLayer->getLayer() == layer) {
+ return i;
+ }
+ }
+ return std::nullopt;
}
void Output::setReleasedLayers(Output::ReleasedLayers&& layers) {
mReleasedLayers = std::move(layers);
}
-Output::ReleasedLayers Output::takeReleasedLayers() {
- return std::move(mReleasedLayers);
-}
-
void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& geomSnapshots) {
ATRACE_CALL();
@@ -332,8 +310,10 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
+ auto& outputState = editState();
+
// Do nothing if this output is not enabled or there is no need to perform this update
- if (!mState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
+ if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
return;
}
@@ -342,57 +322,44 @@
collectVisibleLayers(refreshArgs, coverage);
// Compute the resulting coverage for this output, and store it for later
- const ui::Transform& tr = mState.transform;
- Region undefinedRegion{mState.bounds};
+ const ui::Transform& tr = outputState.transform;
+ Region undefinedRegion{outputState.bounds};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
- mState.undefinedRegion = undefinedRegion;
- mState.dirtyRegion.orSelf(coverage.dirtyRegion);
+ outputState.undefinedRegion = undefinedRegion;
+ outputState.dirtyRegion.orSelf(coverage.dirtyRegion);
}
void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
compositionengine::Output::CoverageState& coverage) {
- // We build up a list of all layers that are going to be visible in the new
- // frame.
- compositionengine::Output::OutputLayers newLayersSortedByZ;
-
// Evaluate the layers from front to back to determine what is visible. This
// also incrementally calculates the coverage information for each layer as
// well as the entire output.
for (auto& layer : reversed(refreshArgs.layers)) {
- // Incrementally process the coverage for each layer, obtaining an
- // optional outputLayer if the layer is visible.
- auto outputLayer = getOutputLayerIfVisible(layer, coverage);
- if (outputLayer) {
- newLayersSortedByZ.emplace_back(std::move(outputLayer));
- }
+ // Incrementally process the coverage for each layer
+ ensureOutputLayerIfVisible(layer, coverage);
// TODO(b/121291683): Stop early if the output is completely covered and
// no more layers could even be visible underneath the ones on top.
}
- // Since we walked the layers in reverse order, we need to reverse
- // newLayersSortedByZ to get the back-to-front ordered list of layers.
- std::reverse(newLayersSortedByZ.begin(), newLayersSortedByZ.end());
+ setReleasedLayers(refreshArgs);
+
+ finalizePendingOutputLayers();
// Generate a simple Z-order values to each visible output layer
uint32_t zOrder = 0;
- for (auto& outputLayer : newLayersSortedByZ) {
+ for (auto* outputLayer : getOutputLayersOrderedByZ()) {
outputLayer->editState().z = zOrder++;
}
-
- setReleasedLayers(refreshArgs);
-
- mOutputLayersOrderedByZ = std::move(newLayersSortedByZ);
}
-std::unique_ptr<compositionengine::OutputLayer> Output::getOutputLayerIfVisible(
- std::shared_ptr<compositionengine::Layer> layer,
- compositionengine::Output::CoverageState& coverage) {
+void Output::ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer> layer,
+ compositionengine::Output::CoverageState& coverage) {
// Note: Converts a wp<LayerFE> to a sp<LayerFE>
auto layerFE = layer->getLayerFE();
if (layerFE == nullptr) {
- return nullptr;
+ return;
}
// Ensure we have a snapshot of the basic geometry layer state. Limit the
@@ -409,7 +376,7 @@
// Only consider the layers on the given layer stack
if (!belongsInOutput(layer.get())) {
- return nullptr;
+ return;
}
/*
@@ -443,7 +410,7 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_UNLIKELY(!layerFEState.isVisible)) {
- return nullptr;
+ return;
}
const ui::Transform& tr = layerFEState.geomLayerTransform;
@@ -454,7 +421,7 @@
visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
if (visibleRegion.isEmpty()) {
- return nullptr;
+ return;
}
// Remove the transparent area from the visible region
@@ -490,12 +457,14 @@
visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
if (visibleRegion.isEmpty()) {
- return nullptr;
+ return;
}
// Get coverage information for the layer as previously displayed,
// also taking over ownership from mOutputLayersorderedByZ.
- auto prevOutputLayer = takeOutputLayerForLayer(layer.get());
+ auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layer.get());
+ auto prevOutputLayer =
+ prevOutputLayerIndex ? getOutputLayerOrderedByZByIndex(*prevOutputLayerIndex) : nullptr;
// Get coverage information for the layer as previously displayed
// TODO(b/121291683): Define kEmptyRegion as a constant in Region.h
@@ -543,16 +512,16 @@
// Peform the final check to see if this layer is visible on this output
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
- Region drawRegion(mState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(mState.bounds);
+ const auto& outputState = getState();
+ Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
+ drawRegion.andSelf(outputState.bounds);
if (drawRegion.isEmpty()) {
- return nullptr;
+ return;
}
// The layer is visible. Either reuse the existing outputLayer if we have
// one, or create a new one if we do not.
- std::unique_ptr<compositionengine::OutputLayer> result =
- prevOutputLayer ? std::move(prevOutputLayer) : createOutputLayer(layer, layerFE);
+ auto result = ensureOutputLayer(prevOutputLayerIndex, layer, layerFE);
// Store the layer coverage information into the layer state as some of it
// is useful later.
@@ -560,10 +529,8 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion =
- mState.transform.transform(outputLayerState.visibleRegion.intersect(mState.viewport));
-
- return result;
+ outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
+ outputLayerState.visibleRegion.intersect(outputState.viewport));
}
void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
@@ -571,7 +538,7 @@
}
void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
- for (auto& layer : mOutputLayersOrderedByZ) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
layer->getLayerFE().latchCompositionState(layer->getLayer().editFEState(),
args.updatingGeometryThisFrame
? LayerFE::StateSubset::GeometryAndContent
@@ -584,12 +551,9 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- for (auto& layer : mOutputLayersOrderedByZ) {
- if (refreshArgs.devOptForceClientComposition) {
- layer->editState().forceClientComposition = true;
- }
-
- layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame);
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
+ refreshArgs.devOptForceClientComposition);
// Send the updated state to the HWC, if appropriate.
layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
@@ -614,7 +578,7 @@
ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
*outHdrDataSpace = ui::Dataspace::UNKNOWN;
- for (const auto& layer : mOutputLayersOrderedByZ) {
+ for (const auto* layer : getOutputLayersOrderedByZ()) {
switch (layer->getLayer().getFEState().dataspace) {
case ui::Dataspace::V0_SCRGB:
case ui::Dataspace::V0_SCRGB_LINEAR:
@@ -706,9 +670,10 @@
}
void Output::beginFrame() {
+ auto& outputState = editState();
const bool dirty = !getDirtyRegion(false).isEmpty();
- const bool empty = mOutputLayersOrderedByZ.empty();
- const bool wasEmpty = !mState.lastCompositionHadVisibleLayers;
+ const bool empty = getOutputLayerCount() == 0;
+ const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers;
// If nothing has changed (!dirty), don't recompose.
// If something changed, but we don't currently have any visible layers,
@@ -729,7 +694,7 @@
mRenderSurface->beginFrame(mustRecompose);
if (mustRecompose) {
- mState.lastCompositionHadVisibleLayers = !empty;
+ outputState.lastCompositionHadVisibleLayers = !empty;
}
}
@@ -737,13 +702,15 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- if (!mState.isEnabled) {
+ const auto& outputState = getState();
+ if (!outputState.isEnabled) {
return;
}
chooseCompositionStrategy();
- mRenderSurface->prepareFrame(mState.usesClientComposition, mState.usesDeviceComposition);
+ mRenderSurface->prepareFrame(outputState.usesClientComposition,
+ outputState.usesDeviceComposition);
}
void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -751,7 +718,7 @@
return;
}
- if (mState.isEnabled) {
+ if (getState().isEnabled) {
// transform the dirty region into this screen's coordinate space
const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
if (!dirtyRegion.isEmpty()) {
@@ -774,7 +741,7 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- if (!mState.isEnabled) {
+ if (!getState().isEnabled) {
return;
}
@@ -793,32 +760,33 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
+ const auto& outputState = getState();
const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
- mState.usesClientComposition};
+ outputState.usesClientComposition};
base::unique_fd readyFence;
-
if (!hasClientComposition) {
return readyFence;
}
ALOGV("hasClientComposition");
- auto& renderEngine = mCompositionEngine.getRenderEngine();
+ auto& renderEngine = getCompositionEngine().getRenderEngine();
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = mState.scissor;
- clientCompositionDisplay.clip = mState.scissor;
- clientCompositionDisplay.globalTransform = mState.transform.asMatrix4();
- clientCompositionDisplay.orientation = mState.orientation;
- clientCompositionDisplay.outputDataspace =
- mDisplayColorProfile->hasWideColorGamut() ? mState.dataspace : ui::Dataspace::UNKNOWN;
+ clientCompositionDisplay.physicalDisplay = outputState.scissor;
+ clientCompositionDisplay.clip = outputState.scissor;
+ clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4();
+ clientCompositionDisplay.orientation = outputState.orientation;
+ clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
+ ? outputState.dataspace
+ : ui::Dataspace::UNKNOWN;
clientCompositionDisplay.maxLuminance =
mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
// Compute the global color transform matrix.
- if (!mState.usesDeviceComposition && !getSkipColorTransform()) {
- clientCompositionDisplay.colorTransform = mState.colorTransformMatrix;
+ if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
+ clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
}
// Note: Updated by generateClientCompositionRequests
@@ -833,12 +801,11 @@
// If we the display is secure, protected content support is enabled, and at
// least one layer has protected content, we need to use a secure back
// buffer.
- if (mState.isSecure && supportsProtectedContent) {
- bool needsProtected =
- std::any_of(mOutputLayersOrderedByZ.begin(), mOutputLayersOrderedByZ.end(),
- [](auto& layer) {
- return layer->getLayer().getFEState().hasProtectedContent;
- });
+ if (outputState.isSecure && supportsProtectedContent) {
+ auto layers = getOutputLayersOrderedByZ();
+ bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
+ return layer->getLayer().getFEState().hasProtectedContent;
+ });
if (needsProtected != renderEngine.isProtected()) {
renderEngine.useProtectedContext(needsProtected);
}
@@ -883,13 +850,14 @@
std::vector<renderengine::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
- const Region viewportRegion(mState.viewport);
+ const auto& outputState = getState();
+ const Region viewportRegion(outputState.viewport);
const bool useIdentityTransform = false;
bool firstLayer = true;
// Used when a layer clears part of the buffer.
Region dummyRegion;
- for (auto& layer : mOutputLayersOrderedByZ) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
const auto& layerState = layer->getState();
const auto& layerFEState = layer->getLayer().getFEState();
auto& layerFE = layer->getLayerFE();
@@ -918,8 +886,8 @@
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
useIdentityTransform,
- layer->needsFiltering() || mState.needsFiltering,
- mState.isSecure,
+ layer->needsFiltering() || outputState.needsFiltering,
+ outputState.isSecure,
supportsProtectedContent,
clientComposition ? clearRegion : dummyRegion,
};
@@ -972,14 +940,15 @@
return;
}
- mState.dirtyRegion.clear();
+ auto& outputState = editState();
+ outputState.dirtyRegion.clear();
mRenderSurface->flip();
auto frame = presentAndGetFrameFences();
mRenderSurface->onPresentDisplayCompleted();
- for (auto& layer : getOutputLayersOrderedByZ()) {
+ for (auto* layer : getOutputLayersOrderedByZ()) {
// The layer buffer from the previous frame (if any) is released
// by HWC only when the release fence from this frame (if any) is
// signaled. Always get the release fence from HWC first.
@@ -997,7 +966,7 @@
// client target acquire fence when it is available, even though
// this is suboptimal.
// TODO(b/121291683): Track previous frame client target acquire fence.
- if (mState.usesClientComposition) {
+ if (outputState.usesClientComposition) {
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
@@ -1006,7 +975,7 @@
}
// We've got a list of layers needing fences, that are disjoint with
- // getOutputLayersOrderedByZ. The best we can do is to
+ // OutputLayersOrderedByZ. The best we can do is to
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (auto layer = weakLayer.promote(); layer != nullptr) {
@@ -1019,13 +988,15 @@
}
void Output::dirtyEntireOutput() {
- mState.dirtyRegion.set(mState.bounds);
+ auto& outputState = editState();
+ outputState.dirtyRegion.set(outputState.bounds);
}
void Output::chooseCompositionStrategy() {
// The base output implementation can only do client composition
- mState.usesClientComposition = true;
- mState.usesDeviceComposition = false;
+ auto& outputState = editState();
+ outputState.usesClientComposition = true;
+ outputState.usesDeviceComposition = false;
}
bool Output::getSkipColorTransform() const {
@@ -1034,7 +1005,7 @@
compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
compositionengine::Output::FrameFences result;
- if (mState.usesClientComposition) {
+ if (getState().usesClientComposition) {
result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
}
return result;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 2e45953..721e953 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -44,49 +44,26 @@
} // namespace
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+std::unique_ptr<OutputLayer> createOutputLayer(
const compositionengine::Output& output,
const std::shared_ptr<compositionengine::Layer>& layer,
const sp<compositionengine::LayerFE>& layerFE) {
- return std::make_unique<OutputLayer>(output, layer, layerFE);
+ return createOutputLayerTemplated<OutputLayer>(output, layer, layerFE);
}
-OutputLayer::OutputLayer(const Output& output, const std::shared_ptr<Layer>& layer,
- const sp<LayerFE>& layerFE)
- : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
-
OutputLayer::~OutputLayer() = default;
void OutputLayer::setHwcLayer(std::shared_ptr<HWC2::Layer> hwcLayer) {
+ auto& state = editState();
if (hwcLayer) {
- mState.hwc.emplace(hwcLayer);
+ state.hwc.emplace(std::move(hwcLayer));
} else {
- mState.hwc.reset();
+ state.hwc.reset();
}
}
-const compositionengine::Output& OutputLayer::getOutput() const {
- return mOutput;
-}
-
-compositionengine::Layer& OutputLayer::getLayer() const {
- return *mLayer;
-}
-
-compositionengine::LayerFE& OutputLayer::getLayerFE() const {
- return *mLayerFE;
-}
-
-const OutputLayerCompositionState& OutputLayer::getState() const {
- return mState;
-}
-
-OutputLayerCompositionState& OutputLayer::editState() {
- return mState;
-}
-
Rect OutputLayer::calculateInitialCrop() const {
- const auto& layerState = mLayer->getFEState();
+ const auto& layerState = getLayer().getFEState();
// apply the projection's clipping to the window crop in
// layerstack space, and convert-back to layer space.
@@ -96,7 +73,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = mOutput.getState().viewport;
+ const Rect& viewport = getOutput().getState().viewport;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -119,8 +96,8 @@
}
FloatRect OutputLayer::calculateOutputSourceCrop() const {
- const auto& layerState = mLayer->getFEState();
- const auto& outputState = mOutput.getState();
+ const auto& layerState = getLayer().getFEState();
+ const auto& outputState = getOutput().getState();
if (!layerState.geomUsesSourceCrop) {
return {};
@@ -196,8 +173,8 @@
}
Rect OutputLayer::calculateOutputDisplayFrame() const {
- const auto& layerState = mLayer->getFEState();
- const auto& outputState = mOutput.getState();
+ const auto& layerState = getLayer().getFEState();
+ const auto& outputState = getOutput().getState();
// apply the layer's transform, followed by the display's global transform
// here we're guaranteed that the layer's transform preserves rects
@@ -243,8 +220,8 @@
}
uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
- const auto& layerState = mLayer->getFEState();
- const auto& outputState = mOutput.getState();
+ const auto& layerState = getLayer().getFEState();
+ const auto& outputState = getOutput().getState();
/*
* Transformations are applied in this order:
@@ -282,52 +259,62 @@
return transform.getOrientation();
} // namespace impl
-void OutputLayer::updateCompositionState(bool includeGeometry) {
- const auto& layerFEState = mLayer->getFEState();
- const auto& outputState = mOutput.getState();
- const auto& profile = *mOutput.getDisplayColorProfile();
+void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) {
+ const auto& layerFEState = getLayer().getFEState();
+ const auto& outputState = getOutput().getState();
+ const auto& profile = *getOutput().getDisplayColorProfile();
+ auto& state = editState();
if (includeGeometry) {
- mState.displayFrame = calculateOutputDisplayFrame();
- mState.sourceCrop = calculateOutputSourceCrop();
- mState.bufferTransform =
+ // Clear the forceClientComposition flag before it is set for any
+ // reason. Note that since it can be set by some checks below when
+ // updating the geometry state, we only clear it when updating the
+ // geometry since those conditions for forcing client composition won't
+ // go away otherwise.
+ state.forceClientComposition = false;
+
+ state.displayFrame = calculateOutputDisplayFrame();
+ state.sourceCrop = calculateOutputSourceCrop();
+ state.bufferTransform =
static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
if ((layerFEState.isSecure && !outputState.isSecure) ||
- (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
- mState.forceClientComposition = true;
+ (state.bufferTransform & ui::Transform::ROT_INVALID)) {
+ state.forceClientComposition = true;
}
}
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
- mState.dataspace = layerFEState.isColorspaceAgnostic &&
+ state.dataspace = layerFEState.isColorspaceAgnostic &&
outputState.targetDataspace != ui::Dataspace::UNKNOWN
? outputState.targetDataspace
: layerFEState.dataspace;
// These are evaluated every frame as they can potentially change at any
// time.
- if (layerFEState.forceClientComposition || !profile.isDataspaceSupported(mState.dataspace)) {
- mState.forceClientComposition = true;
+ if (layerFEState.forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
+ forceClientComposition) {
+ state.forceClientComposition = true;
}
}
void OutputLayer::writeStateToHWC(bool includeGeometry) {
+ const auto& state = getState();
// Skip doing this if there is no HWC interface
- if (!mState.hwc) {
+ if (!state.hwc) {
return;
}
- auto& hwcLayer = (*mState.hwc).hwcLayer;
+ auto& hwcLayer = (*state.hwc).hwcLayer;
if (!hwcLayer) {
ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
- mLayerFE->getDebugName(), mOutput.getName().c_str());
+ getLayerFE().getDebugName(), getOutput().getName().c_str());
return;
}
- const auto& outputIndependentState = mLayer->getFEState();
+ const auto& outputIndependentState = getLayer().getFEState();
auto requestedCompositionType = outputIndependentState.compositionType;
if (includeGeometry) {
@@ -348,7 +335,7 @@
if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
error != HWC2::Error::None) {
ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
- mLayerFE->getDebugName(), outputDependentState.displayFrame.left,
+ getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
outputDependentState.displayFrame.bottom, to_string(error).c_str(),
static_cast<int32_t>(error));
@@ -358,15 +345,15 @@
error != HWC2::Error::None) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
"%s (%d)",
- mLayerFE->getDebugName(), outputDependentState.sourceCrop.left,
+ getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), outputDependentState.z,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
+ outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
}
// Solid-color layers should always use an identity transform.
@@ -376,7 +363,7 @@
: static_cast<Hwc2::Transform>(0);
if (auto error = hwcLayer->setTransform(static_cast<HWC2::Transform>(bufferTransform));
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set transform %s: %s (%d)", getLayerFE().getDebugName(),
toString(outputDependentState.bufferTransform).c_str(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
@@ -387,21 +374,21 @@
if (auto error = hwcLayer->setBlendMode(
static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), to_string(error).c_str(),
- static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -412,14 +399,14 @@
// state and should not change every frame.
if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set visible region: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
}
if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
outputDependentState.dataspace, to_string(error).c_str(),
static_cast<int32_t>(error));
}
@@ -434,13 +421,13 @@
editState().forceClientComposition = true;
break;
default:
- ALOGE("[%s] Failed to set color transform: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set color transform: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set surface damage: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputIndependentState.surfaceDamage.dump(LOG_TAG);
}
@@ -472,7 +459,7 @@
255};
if (auto error = hwcLayer->setColor(color); error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set color: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -481,7 +468,7 @@
const LayerFECompositionState& outputIndependentState) {
if (auto error = hwcLayer->setSidebandStream(outputIndependentState.sidebandStream->handle());
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", getLayerFE().getDebugName(),
outputIndependentState.sidebandStream->handle(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
@@ -490,11 +477,11 @@
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
auto supportedPerFrameMetadata =
- mOutput.getDisplayColorProfile()->getSupportedPerFrameMetadata();
+ getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata();
if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata,
outputIndependentState.hdrMetadata);
error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
- ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
@@ -508,7 +495,7 @@
if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
outputIndependentState.buffer->handle, to_string(error).c_str(),
static_cast<int32_t>(error));
}
@@ -530,7 +517,7 @@
if (auto error = hwcLayer->setCompositionType(
static_cast<HWC2::Composition>(requestedCompositionType));
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set composition type %s: %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
toString(requestedCompositionType).c_str(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
@@ -544,8 +531,8 @@
return;
}
- const auto& layerFEState = mLayer->getFEState();
- const auto& outputState = mOutput.getState();
+ const auto& layerFEState = getLayer().getFEState();
+ const auto& outputState = getOutput().getState();
Rect frame = layerFEState.cursorFrame;
frame.intersect(outputState.viewport, &frame);
@@ -553,23 +540,26 @@
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)", mLayerFE->getDebugName(),
- position.left, position.top, to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)",
+ getLayerFE().getDebugName(), position.left, position.top, to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
HWC2::Layer* OutputLayer::getHwcLayer() const {
- return mState.hwc ? mState.hwc->hwcLayer.get() : nullptr;
+ const auto& state = getState();
+ return state.hwc ? state.hwc->hwcLayer.get() : nullptr;
}
bool OutputLayer::requiresClientComposition() const {
- return !mState.hwc ||
- mState.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
+ const auto& state = getState();
+ return !state.hwc ||
+ state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
}
bool OutputLayer::isHardwareCursor() const {
- return mState.hwc &&
- mState.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CURSOR;
+ const auto& state = getState();
+ return state.hwc && state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CURSOR;
}
void OutputLayer::detectDisallowedCompositionTypeChange(
@@ -595,15 +585,16 @@
if (!result) {
ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
- mLayerFE->getDebugName(), toString(from).c_str(), static_cast<int>(from),
+ getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
toString(to).c_str(), static_cast<int>(to));
}
}
void OutputLayer::applyDeviceCompositionTypeChange(
Hwc2::IComposerClient::Composition compositionType) {
- LOG_FATAL_IF(!mState.hwc);
- auto& hwcState = *mState.hwc;
+ auto& state = editState();
+ LOG_FATAL_IF(!state.hwc);
+ auto& hwcState = *state.hwc;
detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
@@ -611,25 +602,28 @@
}
void OutputLayer::prepareForDeviceLayerRequests() {
- mState.clearClientTarget = false;
+ auto& state = editState();
+ state.clearClientTarget = false;
}
void OutputLayer::applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) {
+ auto& state = editState();
switch (request) {
case Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET:
- mState.clearClientTarget = true;
+ state.clearClientTarget = true;
break;
default:
- ALOGE("[%s] Unknown device layer request %s (%d)", mLayerFE->getDebugName(),
+ ALOGE("[%s] Unknown device layer request %s (%d)", getLayerFE().getDebugName(),
toString(request).c_str(), static_cast<int>(request));
break;
}
}
bool OutputLayer::needsFiltering() const {
- const auto& displayFrame = mState.displayFrame;
- const auto& sourceCrop = mState.sourceCrop;
+ const auto& state = getState();
+ const auto& displayFrame = state.displayFrame;
+ const auto& sourceCrop = state.sourceCrop;
return sourceCrop.getHeight() != displayFrame.getHeight() ||
sourceCrop.getWidth() != displayFrame.getWidth();
}
@@ -637,9 +631,9 @@
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
- StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(),
- mLayerFE->getDebugName());
- mState.dump(out);
+ StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, &getLayer(),
+ getLayerFE().getDebugName());
+ dumpState(out);
}
} // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 1ce6b4c..5ed21fc 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -43,12 +43,13 @@
std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
- return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+ compositionengine::Display& display,
+ const compositionengine::RenderSurfaceCreationArgs& args) {
+ return std::make_unique<RenderSurface>(compositionEngine, display, args);
}
RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
- RenderSurfaceCreationArgs&& args)
+ const RenderSurfaceCreationArgs& args)
: mCompositionEngine(compositionEngine),
mDisplay(display),
mNativeWindow(args.nativeWindow),
@@ -156,7 +157,7 @@
return mGraphicBuffer;
}
-void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
+void RenderSurface::queueBuffer(base::unique_fd readyFence) {
auto& state = mDisplay.getState();
if (state.usesClientComposition || state.flipClientTarget) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 74b3ada..4d71d43 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -48,17 +48,33 @@
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
struct DisplayTest : public testing::Test {
+ class Display : public impl::Display {
+ public:
+ explicit Display(const compositionengine::DisplayCreationArgs& args)
+ : impl::Display(args) {}
+
+ using impl::Display::injectOutputLayerForTest;
+ virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+ };
+
+ static std::shared_ptr<Display> createDisplay(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::DisplayCreationArgs&& args) {
+ return impl::createDisplayTemplated<Display>(compositionEngine, args);
+ }
+
DisplayTest() {
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
EXPECT_CALL(*mLayer1, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer1));
EXPECT_CALL(*mLayer2, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer2));
EXPECT_CALL(*mLayer3, getHwcLayer()).WillRepeatedly(Return(nullptr));
- std::vector<std::unique_ptr<OutputLayer>> layers;
- layers.emplace_back(mLayer1);
- layers.emplace_back(mLayer2);
- layers.emplace_back(mLayer3);
- mDisplay.setOutputLayersOrderedByZ(std::move(layers));
+ mDisplay->injectOutputLayerForTest(
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer1));
+ mDisplay->injectOutputLayerForTest(
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer2));
+ mDisplay->injectOutputLayerForTest(
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer3));
}
StrictMock<android::mock::HWComposer> mHwComposer;
@@ -71,11 +87,11 @@
mock::OutputLayer* mLayer1 = new StrictMock<mock::OutputLayer>();
mock::OutputLayer* mLayer2 = new StrictMock<mock::OutputLayer>();
mock::OutputLayer* mLayer3 = new StrictMock<mock::OutputLayer>();
- impl::Display mDisplay{mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setDisplayId(DEFAULT_DISPLAY_ID)
- .setPowerAdvisor(&mPowerAdvisor)
- .build()};
+ std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setDisplayId(DEFAULT_DISPLAY_ID)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
};
/*
@@ -95,12 +111,10 @@
{
constexpr DisplayId display2 = DisplayId{546u};
- auto display = impl::createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setIsSecure(true)
- .setDisplayId(display2)
- .build());
- EXPECT_TRUE(display->isSecure());
+ auto display =
+ impl::createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder().setDisplayId(display2).build());
+ EXPECT_FALSE(display->isSecure());
EXPECT_FALSE(display->isVirtual());
EXPECT_EQ(display2, display->getId());
}
@@ -126,13 +140,13 @@
// The first call to disconnect will disconnect the display with the HWC and
// set mHwcId to -1.
EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
- mDisplay.disconnect();
- EXPECT_FALSE(mDisplay.getId());
+ mDisplay->disconnect();
+ EXPECT_FALSE(mDisplay->getId());
// Subsequent calls will do nothing,
EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
- mDisplay.disconnect();
- EXPECT_FALSE(mDisplay.getId());
+ mDisplay->disconnect();
+ EXPECT_FALSE(mDisplay->getId());
}
/*
@@ -143,7 +157,7 @@
// No change does nothing
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = std::nullopt;
- mDisplay.setColorTransform(refreshArgs);
+ mDisplay->setColorTransform(refreshArgs);
// Identity matrix sets an identity state value
const mat4 kIdentity;
@@ -151,7 +165,7 @@
EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
refreshArgs.colorTransformMatrix = kIdentity;
- mDisplay.setColorTransform(refreshArgs);
+ mDisplay->setColorTransform(refreshArgs);
// Non-identity matrix sets a non-identity state value
const mat4 kNonIdentity = mat4() * 2;
@@ -159,7 +173,7 @@
EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
refreshArgs.colorTransformMatrix = kNonIdentity;
- mDisplay.setColorTransform(refreshArgs);
+ mDisplay->setColorTransform(refreshArgs);
}
/*
@@ -170,27 +184,27 @@
using ColorProfile = Output::ColorProfile;
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
- mDisplay.setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
+ mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
.WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
// These values are expected to be the initial state.
- ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
- ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
- ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
+ ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+ ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+ ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
- // If the set values are unchanged, nothing happens
- mDisplay.setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ // Otherwise if the values are unchanged, nothing happens
+ mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
- EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
- EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
+ EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are different, updates happen
EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -199,24 +213,24 @@
ui::RenderIntent::TONE_MAP_COLORIMETRIC))
.Times(1);
- mDisplay.setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+ ui::Dataspace::UNKNOWN});
- EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
- EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
}
TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
using ColorProfile = Output::ColorProfile;
- impl::Display virtualDisplay{mCompositionEngine,
- DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+ std::shared_ptr<impl::Display> virtualDisplay{
+ impl::createDisplay(mCompositionEngine, DisplayCreationArgs{true, DEFAULT_DISPLAY_ID})};
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
- virtualDisplay.setDisplayColorProfileForTest(
+ virtualDisplay->setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(colorProfile));
EXPECT_CALL(*colorProfile,
@@ -224,14 +238,14 @@
ui::Dataspace::UNKNOWN))
.WillOnce(Return(ui::Dataspace::UNKNOWN));
- virtualDisplay.setColorProfile(
+ virtualDisplay->setColorProfile(
ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
- EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
- EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
+ EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
}
/*
@@ -239,11 +253,11 @@
*/
TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
- EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
- mDisplay.createDisplayColorProfile(
+ EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr);
+ mDisplay->createDisplayColorProfile(
DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
DisplayColorProfileCreationArgs::HwcColorModes()});
- EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+ EXPECT_TRUE(mDisplay->getDisplayColorProfile() != nullptr);
}
/*
@@ -252,9 +266,9 @@
TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
- EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
- mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
- EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+ EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
+ mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
+ EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr);
}
/*
@@ -268,7 +282,7 @@
EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
- auto outputLayer = mDisplay.createOutputLayer(layer, layerFE);
+ auto outputLayer = mDisplay->createOutputLayer(layer, layerFE);
EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
@@ -308,13 +322,13 @@
{
Output::ReleasedLayers releasedLayers;
releasedLayers.emplace_back(layerXLayerFE);
- mDisplay.setReleasedLayers(std::move(releasedLayers));
+ mDisplay->setReleasedLayers(std::move(releasedLayers));
}
CompositionRefreshArgs refreshArgs;
- mDisplay.setReleasedLayers(refreshArgs);
+ mDisplay->setReleasedLayers(refreshArgs);
- const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+ const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
ASSERT_EQ(1, releasedLayers.size());
}
@@ -341,9 +355,9 @@
refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
refreshArgs.layersWithQueuedFrames.push_back(nullptr);
- mDisplay.setReleasedLayers(refreshArgs);
+ mDisplay->setReleasedLayers(refreshArgs);
- const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+ const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
ASSERT_EQ(2, releasedLayers.size());
ASSERT_EQ(layer1LayerFE.get(), releasedLayers[0].promote().get());
ASSERT_EQ(layer2LayerFE.get(), releasedLayers[1].promote().get());
@@ -356,8 +370,8 @@
struct DisplayChooseCompositionStrategyTest : public testing::Test {
struct DisplayPartialMock : public impl::Display {
DisplayPartialMock(const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::DisplayCreationArgs&& args)
- : impl::Display(compositionEngine, std::move(args)) {}
+ const compositionengine::DisplayCreationArgs& args)
+ : impl::Display(args), mCompositionEngine(compositionEngine) {}
// Sets up the helper functions called by chooseCompositionStrategy to
// use a mock implementations.
@@ -366,6 +380,34 @@
MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // compositionengine::impl::Output overrides
+ const CompositionEngine& getCompositionEngine() const override {
+ return mCompositionEngine;
+ };
+
+ // These need implementations though are not expected to be called.
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+ compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+ const compositionengine::CompositionEngine& mCompositionEngine;
+ impl::OutputCompositionState mState;
};
DisplayChooseCompositionStrategyTest() {
@@ -380,12 +422,13 @@
};
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
- impl::Display nonHwcDisplay{mCompositionEngine, DisplayCreationArgsBuilder().build()};
- EXPECT_FALSE(nonHwcDisplay.getId());
+ std::shared_ptr<impl::Display> nonHwcDisplay{
+ impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+ EXPECT_FALSE(nonHwcDisplay->getId());
- nonHwcDisplay.chooseCompositionStrategy();
+ nonHwcDisplay->chooseCompositionStrategy();
- auto& state = nonHwcDisplay.getState();
+ auto& state = nonHwcDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
EXPECT_FALSE(state.usesDeviceComposition);
}
@@ -466,7 +509,7 @@
hasDisplayCapability(std::make_optional(DEFAULT_DISPLAY_ID),
HWC2::DisplayCapability::SkipClientColorTransform))
.WillOnce(Return(true));
- EXPECT_TRUE(mDisplay.getSkipColorTransform());
+ EXPECT_TRUE(mDisplay->getSkipColorTransform());
}
/*
@@ -478,14 +521,14 @@
EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_FALSE(mDisplay.anyLayersRequireClientComposition());
+ EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
}
TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) {
EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true));
- EXPECT_TRUE(mDisplay.anyLayersRequireClientComposition());
+ EXPECT_TRUE(mDisplay->anyLayersRequireClientComposition());
}
/*
@@ -497,14 +540,14 @@
EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true));
EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(true));
- EXPECT_TRUE(mDisplay.allLayersRequireClientComposition());
+ EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
}
TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) {
EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(true));
EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_FALSE(mDisplay.allLayersRequireClientComposition());
+ EXPECT_FALSE(mDisplay->allLayersRequireClientComposition());
}
/*
@@ -512,7 +555,7 @@
*/
TEST_F(DisplayTest, applyChangedTypesToLayersTakesEarlyOutIfNoChangedLayers) {
- mDisplay.applyChangedTypesToLayers(impl::Display::ChangedTypes());
+ mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes());
}
TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) {
@@ -523,7 +566,7 @@
applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
.Times(1);
- mDisplay.applyChangedTypesToLayers(impl::Display::ChangedTypes{
+ mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
{&mHWC2Layer1, HWC2::Composition::Client},
{&mHWC2Layer2, HWC2::Composition::Device},
{&mHWC2LayerUnknown, HWC2::Composition::SolidColor},
@@ -535,30 +578,30 @@
*/
TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesNoRequests) {
- mDisplay.applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0));
+ mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0));
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_FALSE(state.flipClientTarget);
}
TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesFlipClientTarget) {
- mDisplay.applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget);
+ mDisplay->applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget);
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_TRUE(state.flipClientTarget);
}
TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesWriteClientTargetToOutput) {
- mDisplay.applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput);
+ mDisplay->applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput);
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_FALSE(state.flipClientTarget);
}
TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesAllRequestFlagsSet) {
- mDisplay.applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0));
+ mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0));
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_TRUE(state.flipClientTarget);
}
@@ -571,7 +614,7 @@
EXPECT_CALL(*mLayer2, prepareForDeviceLayerRequests()).Times(1);
EXPECT_CALL(*mLayer3, prepareForDeviceLayerRequests()).Times(1);
- mDisplay.applyLayerRequestsToLayers(impl::Display::LayerRequests());
+ mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
}
TEST_F(DisplayTest, applyLayerRequestsToLayers2) {
@@ -583,7 +626,7 @@
applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET))
.Times(1);
- mDisplay.applyLayerRequestsToLayers(impl::Display::LayerRequests{
+ mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests{
{&mHWC2Layer1, HWC2::LayerRequest::ClearClientTarget},
{&mHWC2LayerUnknown, HWC2::LayerRequest::ClearClientTarget},
});
@@ -617,7 +660,7 @@
.WillOnce(Return(layer2Fence));
EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
- auto result = mDisplay.presentAndGetFrameFences();
+ auto result = mDisplay->presentAndGetFrameFences();
EXPECT_EQ(presentFence, result.presentFence);
@@ -634,10 +677,10 @@
TEST_F(DisplayTest, setExpensiveRenderingExpectedForwardsToPowerAdvisor) {
EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1);
- mDisplay.setExpensiveRenderingExpected(true);
+ mDisplay->setExpensiveRenderingExpected(true);
EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false)).Times(1);
- mDisplay.setExpensiveRenderingExpected(false);
+ mDisplay->setExpensiveRenderingExpected(false);
}
/*
@@ -646,20 +689,20 @@
TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect no calls to queueBuffer if composition was skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
- mDisplay.editState().isEnabled = true;
- mDisplay.editState().usesClientComposition = false;
- mDisplay.editState().viewport = Rect(0, 0, 1, 1);
- mDisplay.editState().dirtyRegion = Region::INVALID_REGION;
+ mDisplay->editState().isEnabled = true;
+ mDisplay->editState().usesClientComposition = false;
+ mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
refreshArgs.repaintEverything = false;
- mDisplay.finishFrame(refreshArgs);
+ mDisplay->finishFrame(refreshArgs);
}
TEST_F(DisplayTest, finishFrameSkipsCompositionIfNotDirty) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
index 26115a3..787f973 100644
--- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Layer.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/LayerFE.h>
@@ -26,13 +27,28 @@
using testing::StrictMock;
-class LayerTest : public testing::Test {
-public:
+struct LayerTest : public testing::Test {
+ struct Layer final : public impl::Layer {
+ explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {}
+ ~Layer() override = default;
+
+ // compositionengine::Layer overrides
+ sp<LayerFE> getLayerFE() const { return mLayerFE.promote(); }
+ const LayerFECompositionState& getFEState() const override { return mFrontEndState; }
+ LayerFECompositionState& editFEState() override { return mFrontEndState; }
+
+ // compositionengine::impl::Layer overrides
+ void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); }
+
+ const wp<LayerFE> mLayerFE;
+ LayerFECompositionState mFrontEndState;
+ };
+
~LayerTest() override = default;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
- impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}};
+ Layer mLayer{LayerCreationArgs{mLayerFE}};
};
/* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 88cedfa..a338784 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -15,6 +15,7 @@
*/
#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
#include <compositionengine/mock/Layer.h>
@@ -55,6 +56,29 @@
}
struct OutputLayerTest : public testing::Test {
+ struct OutputLayer final : public impl::OutputLayer {
+ OutputLayer(const compositionengine::Output& output,
+ std::shared_ptr<compositionengine::Layer> layer,
+ sp<compositionengine::LayerFE> layerFE)
+ : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+ ~OutputLayer() override = default;
+
+ // compositionengine::OutputLayer overrides
+ const compositionengine::Output& getOutput() const override { return mOutput; }
+ compositionengine::Layer& getLayer() const override { return *mLayer; }
+ compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+ const impl::OutputLayerCompositionState& getState() const override { return mState; }
+ impl::OutputLayerCompositionState& editState() override { return mState; }
+
+ // compositionengine::impl::OutputLayer overrides
+ void dumpState(std::string& out) const override { mState.dump(out); }
+
+ const compositionengine::Output& mOutput;
+ std::shared_ptr<compositionengine::Layer> mLayer;
+ sp<compositionengine::LayerFE> mLayerFE;
+ impl::OutputLayerCompositionState mState;
+ };
+
OutputLayerTest() {
EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
@@ -68,7 +92,7 @@
new StrictMock<compositionengine::mock::Layer>()};
sp<compositionengine::mock::LayerFE> mLayerFE{
new StrictMock<compositionengine::mock::LayerFE>()};
- impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+ OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
LayerFECompositionState mLayerFEState;
impl::OutputCompositionState mOutputState;
@@ -413,11 +437,26 @@
OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
std::shared_ptr<compositionengine::Layer> layer,
sp<compositionengine::LayerFE> layerFE)
- : impl::OutputLayer(output, layer, layerFE) {}
+ : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
// Mock everything called by updateCompositionState to simplify testing it.
MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t());
+
+ // compositionengine::OutputLayer overrides
+ const compositionengine::Output& getOutput() const override { return mOutput; }
+ compositionengine::Layer& getLayer() const override { return *mLayer; }
+ compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+ const impl::OutputLayerCompositionState& getState() const override { return mState; }
+ impl::OutputLayerCompositionState& editState() override { return mState; }
+
+ // These need implementations though are not expected to be called.
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+
+ const compositionengine::Output& mOutput;
+ std::shared_ptr<compositionengine::Layer> mLayer;
+ sp<compositionengine::LayerFE> mLayerFE;
+ impl::OutputLayerCompositionState mState;
};
struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
@@ -458,10 +497,11 @@
TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
mLayerFEState.isSecure = true;
mOutputState.isSecure = true;
+ mOutputLayer.editState().forceClientComposition = true;
setupGeometryChildCallValues();
- mOutputLayer.updateCompositionState(true);
+ mOutputLayer.updateCompositionState(true, false);
validateComputedGeometryState();
@@ -475,7 +515,7 @@
setupGeometryChildCallValues();
- mOutputLayer.updateCompositionState(true);
+ mOutputLayer.updateCompositionState(true, false);
validateComputedGeometryState();
@@ -491,7 +531,7 @@
setupGeometryChildCallValues();
- mOutputLayer.updateCompositionState(true);
+ mOutputLayer.updateCompositionState(true, false);
validateComputedGeometryState();
@@ -506,7 +546,7 @@
// should use the layers requested colorspace.
mLayerFEState.isColorspaceAgnostic = false;
- mOutputLayer.updateCompositionState(false);
+ mOutputLayer.updateCompositionState(false, false);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
@@ -514,30 +554,60 @@
// should use the colorspace chosen for the whole output.
mLayerFEState.isColorspaceAgnostic = true;
- mOutputLayer.updateCompositionState(false);
+ mOutputLayer.updateCompositionState(false, false);
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
- mOutputLayer.updateCompositionState(false);
+ mOutputLayer.editState().forceClientComposition = false;
+
+ mOutputLayer.updateCompositionState(false, false);
EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
}
+TEST_F(OutputLayerUpdateCompositionStateTest,
+ doesNotClearForceClientCompositionIfNotDoingGeometry) {
+ mOutputLayer.editState().forceClientComposition = true;
+
+ mOutputLayer.updateCompositionState(false, false);
+
+ EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) {
mLayerFEState.forceClientComposition = true;
+ mOutputLayer.editState().forceClientComposition = false;
- mOutputLayer.updateCompositionState(false);
+ mOutputLayer.updateCompositionState(false, false);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
TEST_F(OutputLayerUpdateCompositionStateTest,
clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) {
+ mOutputLayer.editState().forceClientComposition = false;
EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
- mOutputLayer.updateCompositionState(false);
+ mOutputLayer.updateCompositionState(false, false);
+
+ EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromArgumentFlag) {
+ mLayerFEState.forceClientComposition = false;
+ mOutputLayer.editState().forceClientComposition = false;
+
+ mOutputLayer.updateCompositionState(false, true);
+
+ EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+
+ mOutputLayer.editState().forceClientComposition = false;
+
+ setupGeometryChildCallValues();
+
+ mOutputLayer.updateCompositionState(true, true);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 70c343b..95ae888 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -50,12 +50,23 @@
const mat4 kNonIdentityQuarter = mat4() * 0.25;
struct OutputTest : public testing::Test {
- OutputTest() {
- mOutput.setDisplayColorProfileForTest(
- std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
- mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ class Output : public impl::Output {
+ public:
+ using impl::Output::injectOutputLayerForTest;
+ virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+ };
- mOutput.editState().bounds = kDefaultDisplaySize;
+ static std::shared_ptr<Output> createOutput(
+ const compositionengine::CompositionEngine& compositionEngine) {
+ return impl::createOutputTemplated<Output>(compositionEngine);
+ }
+
+ OutputTest() {
+ mOutput->setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ mOutput->editState().bounds = kDefaultDisplaySize;
}
static const Rect kDefaultDisplaySize;
@@ -63,7 +74,7 @@
StrictMock<mock::CompositionEngine> mCompositionEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- impl::Output mOutput{mCompositionEngine};
+ std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
};
const Rect OutputTest::kDefaultDisplaySize{100, 200};
@@ -77,14 +88,14 @@
EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
- EXPECT_TRUE(mOutput.isValid());
+ EXPECT_TRUE(mOutput->isValid());
// If we take away the required components, it is no longer valid.
- mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+ mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
- EXPECT_FALSE(mOutput.isValid());
+ EXPECT_FALSE(mOutput->isValid());
}
/*
@@ -92,30 +103,30 @@
*/
TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
- mOutput.editState().isEnabled = true;
+ mOutput->editState().isEnabled = true;
- mOutput.setCompositionEnabled(true);
+ mOutput->setCompositionEnabled(true);
- EXPECT_TRUE(mOutput.getState().isEnabled);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+ EXPECT_TRUE(mOutput->getState().isEnabled);
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
- mOutput.editState().isEnabled = false;
+ mOutput->editState().isEnabled = false;
- mOutput.setCompositionEnabled(true);
+ mOutput->setCompositionEnabled(true);
- EXPECT_TRUE(mOutput.getState().isEnabled);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_TRUE(mOutput->getState().isEnabled);
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
- mOutput.editState().isEnabled = true;
+ mOutput->editState().isEnabled = true;
- mOutput.setCompositionEnabled(false);
+ mOutput->setCompositionEnabled(false);
- EXPECT_FALSE(mOutput.getState().isEnabled);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_FALSE(mOutput->getState().isEnabled);
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/*
@@ -130,14 +141,14 @@
const Rect scissor{9, 10, 11, 12};
const bool needsFiltering = true;
- mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+ mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
- EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
- EXPECT_EQ(orientation, mOutput.getState().orientation);
- EXPECT_EQ(frame, mOutput.getState().frame);
- EXPECT_EQ(viewport, mOutput.getState().viewport);
- EXPECT_EQ(scissor, mOutput.getState().scissor);
- EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+ EXPECT_THAT(mOutput->getState().transform, TransformEq(transform));
+ EXPECT_EQ(orientation, mOutput->getState().orientation);
+ EXPECT_EQ(frame, mOutput->getState().frame);
+ EXPECT_EQ(viewport, mOutput->getState().viewport);
+ EXPECT_EQ(scissor, mOutput->getState().scissor);
+ EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
}
/*
@@ -150,11 +161,11 @@
EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
- mOutput.setBounds(displaySize);
+ mOutput->setBounds(displaySize);
- EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+ EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
}
/*
@@ -163,12 +174,12 @@
TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
const uint32_t layerStack = 123u;
- mOutput.setLayerStackFilter(layerStack, true);
+ mOutput->setLayerStackFilter(layerStack, true);
- EXPECT_TRUE(mOutput.getState().layerStackInternal);
- EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+ EXPECT_TRUE(mOutput->getState().layerStackInternal);
+ EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/*
@@ -176,84 +187,84 @@
*/
TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) {
- mOutput.editState().colorTransformMatrix = kIdentity;
+ mOutput->editState().colorTransformMatrix = kIdentity;
// If no colorTransformMatrix is set the update should be skipped.
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = std::nullopt;
- mOutput.setColorTransform(refreshArgs);
+ mOutput->setColorTransform(refreshArgs);
// The internal state should be unchanged
- EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+ EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
// No dirty region should be set
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) {
- mOutput.editState().colorTransformMatrix = kIdentity;
+ mOutput->editState().colorTransformMatrix = kIdentity;
// Attempting to set the same colorTransformMatrix that is already set should
// also skip the update.
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = kIdentity;
- mOutput.setColorTransform(refreshArgs);
+ mOutput->setColorTransform(refreshArgs);
// The internal state should be unchanged
- EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+ EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
// No dirty region should be set
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) {
- mOutput.editState().colorTransformMatrix = kNonIdentityHalf;
+ mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
// Setting a different colorTransformMatrix should perform the update.
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = kIdentity;
- mOutput.setColorTransform(refreshArgs);
+ mOutput->setColorTransform(refreshArgs);
// The internal state should have been updated
- EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+ EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
// The dirtyRegion should be set to the full display size
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) {
- mOutput.editState().colorTransformMatrix = kIdentity;
+ mOutput->editState().colorTransformMatrix = kIdentity;
// Setting a different colorTransformMatrix should perform the update.
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = kNonIdentityHalf;
- mOutput.setColorTransform(refreshArgs);
+ mOutput->setColorTransform(refreshArgs);
// The internal state should have been updated
- EXPECT_EQ(kNonIdentityHalf, mOutput.getState().colorTransformMatrix);
+ EXPECT_EQ(kNonIdentityHalf, mOutput->getState().colorTransformMatrix);
// The dirtyRegion should be set to the full display size
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) {
- mOutput.editState().colorTransformMatrix = kNonIdentityHalf;
+ mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
// Setting a different colorTransformMatrix should perform the update.
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = kNonIdentityQuarter;
- mOutput.setColorTransform(refreshArgs);
+ mOutput->setColorTransform(refreshArgs);
// The internal state should have been updated
- EXPECT_EQ(kNonIdentityQuarter, mOutput.getState().colorTransformMatrix);
+ EXPECT_EQ(kNonIdentityQuarter, mOutput->getState().colorTransformMatrix);
// The dirtyRegion should be set to the full display size
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/*
@@ -269,16 +280,16 @@
.WillOnce(Return(ui::Dataspace::UNKNOWN));
EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
- mOutput.setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+ ui::Dataspace::UNKNOWN});
- EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
- EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput.getState().targetDataspace);
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
@@ -289,16 +300,16 @@
ui::Dataspace::UNKNOWN))
.WillOnce(Return(ui::Dataspace::UNKNOWN));
- mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
- mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
- mOutput.editState().targetDataspace = ui::Dataspace::UNKNOWN;
+ mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
+ mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
+ mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+ mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
- mOutput.setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+ ui::Dataspace::UNKNOWN});
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+ EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
/*
@@ -311,9 +322,9 @@
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
- mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+ mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
}
/*
@@ -322,11 +333,11 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
const Rect viewport{100, 200};
- mOutput.editState().viewport = viewport;
- mOutput.editState().dirtyRegion.set(50, 300);
+ mOutput->editState().viewport = viewport;
+ mOutput->editState().dirtyRegion.set(50, 300);
{
- Region result = mOutput.getDirtyRegion(true);
+ Region result = mOutput->getDirtyRegion(true);
EXPECT_THAT(result, RegionEq(Region(viewport)));
}
@@ -334,11 +345,11 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
const Rect viewport{100, 200};
- mOutput.editState().viewport = viewport;
- mOutput.editState().dirtyRegion.set(50, 300);
+ mOutput->editState().viewport = viewport;
+ mOutput->editState().dirtyRegion.set(50, 300);
{
- Region result = mOutput.getDirtyRegion(false);
+ Region result = mOutput->getDirtyRegion(false);
// The dirtyRegion should be clipped to the display bounds.
EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
@@ -354,26 +365,26 @@
const uint32_t layerStack2 = 456u;
// If the output accepts layerStack1 and internal-only layers....
- mOutput.setLayerStackFilter(layerStack1, true);
+ mOutput->setLayerStackFilter(layerStack1, true);
// A layer with no layerStack does not belong to it, internal-only or not.
- EXPECT_FALSE(mOutput.belongsInOutput(std::nullopt, false));
- EXPECT_FALSE(mOutput.belongsInOutput(std::nullopt, true));
+ EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
+ EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true));
// Any layer with layerStack1 belongs to it, internal-only or not.
- EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
- EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
- EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
- EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+ EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
+ EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
// If the output accepts layerStack21 but not internal-only layers...
- mOutput.setLayerStackFilter(layerStack1, false);
+ mOutput->setLayerStackFilter(layerStack1, false);
// Only non-internal layers with layerStack1 belong to it.
- EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
- EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
- EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
- EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+ EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
}
TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
@@ -386,59 +397,59 @@
const uint32_t layerStack2 = 456u;
// If the output accepts layerStack1 and internal-only layers....
- mOutput.setLayerStackFilter(layerStack1, true);
+ mOutput->setLayerStackFilter(layerStack1, true);
// A null layer pointer does not belong to the output
- EXPECT_FALSE(mOutput.belongsInOutput(nullptr));
+ EXPECT_FALSE(mOutput->belongsInOutput(nullptr));
// A layer with no layerStack does not belong to it, internal-only or not.
layerFEState.layerStackId = std::nullopt;
layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = std::nullopt;
layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
// Any layer with layerStack1 belongs to it, internal-only or not.
layerFEState.layerStackId = layerStack1;
layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+ EXPECT_TRUE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack1;
layerFEState.internalOnly = true;
- EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+ EXPECT_TRUE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack2;
layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack2;
layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
// If the output accepts layerStack1 but not internal-only layers...
- mOutput.setLayerStackFilter(layerStack1, false);
+ mOutput->setLayerStackFilter(layerStack1, false);
// A null layer pointer does not belong to the output
- EXPECT_FALSE(mOutput.belongsInOutput(nullptr));
+ EXPECT_FALSE(mOutput->belongsInOutput(nullptr));
// Only non-internal layers with layerStack1 belong to it.
layerFEState.layerStackId = layerStack1;
layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+ EXPECT_TRUE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack1;
layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack2;
layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
layerFEState.layerStackId = layerStack2;
layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+ EXPECT_FALSE(mOutput->belongsInOutput(&layer));
}
/*
@@ -449,75 +460,26 @@
mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1));
- outputLayers.emplace_back(nullptr);
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer1));
+ mOutput->injectOutputLayerForTest(nullptr);
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer2));
StrictMock<mock::Layer> layer;
StrictMock<mock::Layer> otherLayer;
// If the input layer matches the first OutputLayer, it will be returned.
EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
- EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer));
+ EXPECT_EQ(outputLayer1, mOutput->getOutputLayerForLayer(&layer));
// If the input layer matches the second OutputLayer, it will be returned.
EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
- EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer));
+ EXPECT_EQ(outputLayer2, mOutput->getOutputLayerForLayer(&layer));
// If the input layer does not match an output layer, null will be returned.
EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
- EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer));
-}
-
-/*
- * Output::getOrCreateOutputLayer()
- */
-
-TEST_F(OutputTest, getOrCreateOutputLayerWorks) {
- mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>();
-
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(nullptr);
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
-
- std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()};
- sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()};
-
- StrictMock<mock::Layer> otherLayer;
-
- {
- // If there is no OutputLayer corresponding to the input layer, a
- // new OutputLayer is constructed and returned.
- EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer));
- auto result = mOutput.getOrCreateOutputLayer(layer, layerFE);
- EXPECT_NE(existingOutputLayer, result.get());
- EXPECT_TRUE(result.get() != nullptr);
- EXPECT_EQ(layer.get(), &result->getLayer());
- EXPECT_EQ(layerFE.get(), &result->getLayerFE());
-
- // The entries in the ordered array should be unchanged.
- auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
- EXPECT_EQ(nullptr, outputLayers[0].get());
- EXPECT_EQ(existingOutputLayer, outputLayers[1].get());
- }
-
- {
- // If there is an existing OutputLayer for the requested layer, an owned
- // pointer is returned
- EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer));
- auto result = mOutput.getOrCreateOutputLayer(layer, layerFE);
- EXPECT_EQ(existingOutputLayer, result.get());
-
- // The corresponding entry in the ordered array should be cleared.
- auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
- EXPECT_EQ(nullptr, outputLayers[0].get());
- EXPECT_EQ(nullptr, outputLayers[1].get());
- }
+ EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(&layer));
}
/*
@@ -526,12 +488,32 @@
struct OutputPrepareFrameTest : public testing::Test {
struct OutputPartialMock : public impl::Output {
- OutputPartialMock(const compositionengine::CompositionEngine& compositionEngine)
- : impl::Output(compositionEngine) {}
-
// Sets up the helper functions called by prepareFrame to use a mock
// implementations.
MOCK_METHOD0(chooseCompositionStrategy, void());
+
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // These need implementations though are not expected to be called.
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+ compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+ impl::OutputCompositionState mState;
};
OutputPrepareFrameTest() {
@@ -543,7 +525,7 @@
StrictMock<mock::CompositionEngine> mCompositionEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- StrictMock<OutputPartialMock> mOutput{mCompositionEngine};
+ StrictMock<OutputPartialMock> mOutput;
};
TEST_F(OutputPrepareFrameTest, takesEarlyOutIfNotEnabled) {
@@ -566,16 +548,16 @@
// Note: Use OutputTest and not OutputPrepareFrameTest, so the real
// base chooseCompositionStrategy() is invoked.
TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) {
- mOutput.editState().isEnabled = true;
- mOutput.editState().usesClientComposition = false;
- mOutput.editState().usesDeviceComposition = true;
+ mOutput->editState().isEnabled = true;
+ mOutput->editState().usesClientComposition = false;
+ mOutput->editState().usesDeviceComposition = true;
EXPECT_CALL(*mRenderSurface, prepareFrame(true, false));
- mOutput.prepareFrame();
+ mOutput->prepareFrame();
- EXPECT_TRUE(mOutput.getState().usesClientComposition);
- EXPECT_FALSE(mOutput.getState().usesDeviceComposition);
+ EXPECT_TRUE(mOutput->getState().usesClientComposition);
+ EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
}
/*
@@ -592,9 +574,6 @@
static const mat4 kDefaultColorTransformMat;
struct OutputPartialMock : public impl::Output {
- OutputPartialMock(const compositionengine::CompositionEngine& compositionEngine)
- : impl::Output(compositionEngine) {}
-
// Sets up the helper functions called by composeSurfaces to use a mock
// implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
@@ -603,6 +582,29 @@
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // These need implementations though are not expected to be called.
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+ compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+ impl::OutputCompositionState mState;
};
OutputComposeSurfacesTest() {
@@ -610,11 +612,6 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(mOutputLayer1));
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(mOutputLayer2));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
-
mOutput.editState().frame = kDefaultOutputFrame;
mOutput.editState().viewport = kDefaultOutputViewport;
mOutput.editState().scissor = kDefaultOutputScissor;
@@ -627,6 +624,12 @@
mOutput.editState().usesClientComposition = true;
mOutput.editState().usesDeviceComposition = false;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mOutputLayer1));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&mOutputLayer2));
+ EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
}
@@ -634,9 +637,9 @@
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- mock::OutputLayer* mOutputLayer1 = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* mOutputLayer2 = new StrictMock<mock::OutputLayer>();
- StrictMock<OutputPartialMock> mOutput{mCompositionEngine};
+ StrictMock<mock::OutputLayer> mOutputLayer1;
+ StrictMock<mock::OutputLayer> mOutputLayer2;
+ StrictMock<OutputPartialMock> mOutput;
sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
};
@@ -691,14 +694,35 @@
struct GenerateClientCompositionRequestsTest : public testing::Test {
struct OutputPartialMock : public impl::Output {
- OutputPartialMock(const compositionengine::CompositionEngine& compositionEngine)
- : impl::Output(compositionEngine) {}
+ // compositionengine::Output overrides
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, Region& clearRegion) override {
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
clearRegion);
}
+
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // These need implementations though are not expected to be called.
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+ compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+ impl::OutputCompositionState mState;
};
GenerateClientCompositionRequestsTest() {
@@ -707,10 +731,9 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
}
- StrictMock<mock::CompositionEngine> mCompositionEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- StrictMock<OutputPartialMock> mOutput{mCompositionEngine};
+ StrictMock<OutputPartialMock> mOutput;
};
// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests
@@ -720,8 +743,8 @@
// one layer on the left covering the left side of the output, and one layer
// on the right covering that side of the output.
- mock::OutputLayer* leftOutputLayer = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* rightOutputLayer = new StrictMock<mock::OutputLayer>();
+ StrictMock<mock::OutputLayer> leftOutputLayer;
+ StrictMock<mock::OutputLayer> rightOutputLayer;
StrictMock<mock::Layer> leftLayer;
StrictMock<mock::LayerFE> leftLayerFE;
@@ -750,26 +773,27 @@
renderengine::LayerSettings rightLayerRESettings;
rightLayerRESettings.source.solidColor = rightLayerColor;
- EXPECT_CALL(*leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(*leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(*leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(*leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(*leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+ EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
+ EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
+ EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
- EXPECT_CALL(*rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(*rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(*rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(*rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(*rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+ EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
+ EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
+ EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(leftOutputLayer));
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(rightOutputLayer));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&leftOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&rightOutputLayer));
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
@@ -798,7 +822,7 @@
// Layers whose visible region does not intersect with the viewport will be
// skipped when generating client composition request state.
- mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
+ StrictMock<mock::OutputLayer> outputLayer;
StrictMock<mock::Layer> layer;
StrictMock<mock::LayerFE> layerFE;
@@ -809,17 +833,16 @@
LayerFECompositionState layerFEState;
layerFEState.isOpaque = true;
- EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
- EXPECT_CALL(*outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
- EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
- EXPECT_CALL(*outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(*outputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
+ EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
@@ -849,8 +872,8 @@
// set to do so. The first layer is skipped as the frame buffer is already
// expected to be clear.
- mock::OutputLayer* leftOutputLayer = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* rightOutputLayer = new StrictMock<mock::OutputLayer>();
+ StrictMock<mock::OutputLayer> leftOutputLayer;
+ StrictMock<mock::OutputLayer> rightOutputLayer;
StrictMock<mock::Layer> leftLayer;
StrictMock<mock::LayerFE> leftLayerFE;
@@ -876,25 +899,26 @@
rightLayerRESettings.geometry.boundaries = FloatRect{456, 0, 0, 0};
rightLayerRESettings.source.solidColor = rightLayerColor;
- EXPECT_CALL(*leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(*leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(*leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(*leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(*leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+ EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
+ EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
+ EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
- EXPECT_CALL(*rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(*rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(*rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(*rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(*rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+ EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
+ EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
+ EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- Output::OutputLayers outputLayers;
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(leftOutputLayer));
- outputLayers.emplace_back(std::unique_ptr<OutputLayer>(rightOutputLayer));
- mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&leftOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&rightOutputLayer));
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 4a11303..e58e6f4 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -30,4 +30,11 @@
return false;
}
+sp<Layer> ContainerLayer::createClone() {
+ String8 name = mName + " (Mirror)";
+ sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
+ LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata()));
+ layer->setInitialValuesForClone(this);
+ return layer;
+}
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index c3624f6..b48d471 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -32,6 +32,9 @@
bool isVisible() const override;
bool isCreatedFromMainThread() const override { return true; }
+
+protected:
+ sp<Layer> createClone() override;
};
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2ada86b..89123df 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -59,12 +59,13 @@
mSequenceId(args.sequenceId),
mDisplayInstallOrientation(args.displayInstallOrientation),
mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
- compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
- args.displayId, args.powerAdvisor})},
+ compositionengine::DisplayCreationArgs{args.isVirtual, args.displayId,
+ args.powerAdvisor})},
mIsVirtual(args.isVirtual),
mOrientation(),
mActiveConfig(0),
mIsPrimary(args.isPrimary) {
+ mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
args.nativeWindow.get()),
@@ -254,13 +255,17 @@
scissor = displayBounds;
}
+ uint32_t transformOrientation;
+
if (isPrimary()) {
sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
+ transformOrientation = displayStateOrientationToTransformOrientation(
+ (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1));
+ } else {
+ transformOrientation = displayStateOrientationToTransformOrientation(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform,
- displayStateOrientationToTransformOrientation(
- orientation),
+ getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
frame, viewport, scissor, needsFiltering);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 5277320..ce4e1e6 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -31,6 +31,7 @@
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
+#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
@@ -65,10 +66,6 @@
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
- enum {
- NO_LAYER_STACK = 0xFFFFFFFF,
- };
-
explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
virtual ~DisplayDevice();
@@ -201,7 +198,7 @@
int32_t sequenceId = sNextSequenceId++;
std::optional<DisplayId> displayId;
sp<IGraphicBufferProducer> surface;
- uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+ uint32_t layerStack = NO_LAYER_STACK;
Rect viewport;
Rect frame;
uint8_t orientation = 0;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index e53d099..acddc42 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -17,9 +17,11 @@
#undef LOG_TAG
#define LOG_TAG "HwcComposer"
-#include <inttypes.h>
#include <log/log.h>
+#include <algorithm>
+#include <cinttypes>
+
#include "ComposerHal.h"
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
@@ -173,7 +175,16 @@
LOG_ALWAYS_FATAL("failed to get hwcomposer service");
}
- if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
+ if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
+ composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError == Error::NONE) {
+ mClient = tmpClient;
+ mClient_2_2 = tmpClient;
+ mClient_2_3 = tmpClient;
+ mClient_2_4 = tmpClient;
+ }
+ });
+ } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
if (tmpError == Error::NONE) {
mClient = tmpClient;
@@ -456,23 +467,6 @@
return Error::NONE;
}
-Error Composer::getDisplayType(Display display,
- IComposerClient::DisplayType* outType)
-{
- Error error = kDefaultError;
- mClient->getDisplayType(display,
- [&](const auto& tmpError, const auto& tmpType) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outType = tmpType;
- });
-
- return error;
-}
-
Error Composer::getDozeSupport(Display display, bool* outSupport)
{
Error error = kDefaultError;
@@ -1113,23 +1107,6 @@
return error;
}
-Error Composer::getDisplayCapabilities(Display display,
- std::vector<DisplayCapability>* outCapabilities) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
- Error error = kDefaultError;
- mClient_2_3->getDisplayCapabilities(display,
- [&](const auto& tmpError, const auto& tmpCapabilities) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
- *outCapabilities = tmpCapabilities;
- });
- return error;
-}
-
Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
uint8_t componentMask, uint64_t maxFrames) {
if (!mClient_2_3) {
@@ -1187,6 +1164,60 @@
return mClient_2_3->setDisplayBrightness(display, brightness);
}
+// Composer HAL 2.4
+
+Error Composer::getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError;
+ if (mClient_2_4) {
+ mClient_2_4->getDisplayCapabilities_2_4(display,
+ [&](const auto& tmpError, const auto& tmpCaps) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outCapabilities = tmpCaps;
+ });
+ } else {
+ mClient_2_3
+ ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ outCapabilities->resize(tmpCaps.size());
+ std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+ [](auto cap) { return static_cast<DisplayCapability>(cap); });
+ });
+ }
+
+ return error;
+}
+
+Error Composer::getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) {
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError;
+ mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outType = tmpType;
+ });
+
+ return error;
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 9f6cac2..e743e59 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -27,8 +27,8 @@
#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
#include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
@@ -49,6 +49,7 @@
namespace V2_1 = hardware::graphics::composer::V2_1;
namespace V2_2 = hardware::graphics::composer::V2_2;
namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
using types::V1_0::ColorTransform;
using types::V1_0::Transform;
@@ -65,8 +66,8 @@
using V2_1::Layer;
using V2_3::CommandReaderBase;
using V2_3::CommandWriterBase;
-using V2_3::IComposer;
-using V2_3::IComposerClient;
+using V2_4::IComposer;
+using V2_4::IComposerClient;
using DisplayCapability = IComposerClient::DisplayCapability;
using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -118,7 +119,6 @@
std::vector<Layer>* outLayers,
std::vector<uint32_t>* outLayerRequestMasks) = 0;
- virtual Error getDisplayType(Display display, IComposerClient::DisplayType* outType) = 0;
virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
@@ -203,11 +203,15 @@
uint8_t componentMask, uint64_t maxFrames) = 0;
virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) = 0;
- virtual Error getDisplayCapabilities(Display display,
- std::vector<DisplayCapability>* outCapabilities) = 0;
virtual Error setLayerPerFrameMetadataBlobs(
Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
virtual Error setDisplayBrightness(Display display, float brightness) = 0;
+
+ // Composer HAL 2.4
+ virtual Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) = 0;
+ virtual Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) = 0;
};
namespace impl {
@@ -334,7 +338,6 @@
std::vector<Layer>* outLayers,
std::vector<uint32_t>* outLayerRequestMasks) override;
- Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
Error getDozeSupport(Display display, bool* outSupport) override;
Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
@@ -414,13 +417,17 @@
uint64_t maxFrames) override;
Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) override;
- Error getDisplayCapabilities(Display display,
- std::vector<DisplayCapability>* outCapabilities) override;
Error setLayerPerFrameMetadataBlobs(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
Error setDisplayBrightness(Display display, float brightness) override;
+ // Composer HAL 2.4
+ Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) override;
+ Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) override;
+
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
class CommandWriter : public CommandWriterBase {
@@ -455,7 +462,8 @@
sp<V2_1::IComposerClient> mClient;
sp<V2_2::IComposerClient> mClient_2_2;
- sp<IComposerClient> mClient_2_3;
+ sp<V2_3::IComposerClient> mClient_2_3;
+ sp<IComposerClient> mClient_2_4;
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize =
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index c463c4e..6f7428a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -169,20 +169,8 @@
}
mDisplays.erase(displayId);
- DisplayType displayType;
- auto intError = mComposer->getDisplayType(displayId,
- reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(
- &displayType));
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). "
- "Aborting hotplug attempt.",
- displayId, to_string(error).c_str(), intError);
- return;
- }
-
auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
- displayId, displayType);
+ displayId, DisplayType::Physical);
newDisplay->setConnected(true);
mDisplays.emplace(displayId, std::move(newDisplay));
} else if (connection == Connection::Disconnected) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7b708bc..6a45625 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -780,6 +780,15 @@
ATRACE_CALL();
if (mLayerDetached) {
+ // Ensure BLAST buffer callbacks are processed.
+ // detachChildren and mLayerDetached were implemented to avoid geometry updates
+ // to layers in the cases of animation. For BufferQueue layers buffers are still
+ // consumed as normal. This is useful as otherwise the client could get hung
+ // inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue
+ // even though it is a little consistent. detachChildren is shortly slated for removal
+ // by the hierarchy mirroring work so we don't need to worry about it too much.
+ mDrawingState.callbackHandles = mCurrentState.callbackHandles;
+ mCurrentState.callbackHandles = {};
return flags;
}
@@ -899,6 +908,7 @@
}
setZOrderRelativeOf(nullptr);
}
+ mCurrentState.isRelativeOf = false;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -942,6 +952,7 @@
mCurrentState.sequence++;
mCurrentState.modified = true;
mCurrentState.z = relativeZ;
+ mCurrentState.isRelativeOf = true;
auto oldZOrderRelativeOf = mCurrentState.zOrderRelativeOf.promote();
if (oldZOrderRelativeOf != nullptr) {
@@ -989,7 +1000,7 @@
// create background color layer if one does not yet exist
uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
const String8& name = mName + "BackgroundColorLayer";
- mCurrentState.bgColorLayer = new ColorLayer(
+ mCurrentState.bgColorLayer = mFlinger->getFactory().createColorLayer(
LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata()));
// add to child list
@@ -1540,7 +1551,7 @@
bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- return state.zOrderRelativeOf != nullptr;
+ return state.isRelativeOf;
}
__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
@@ -1565,8 +1576,7 @@
}
for (const sp<Layer>& child : children) {
- const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState;
- if (childState.zOrderRelativeOf != nullptr) {
+ if (child->usingRelativeZ(stateSet)) {
continue;
}
traverse.add(child);
@@ -2015,6 +2025,127 @@
return outputLayer->getState().visibleRegion;
}
+void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+ // copy drawing state from cloned layer
+ mDrawingState = clonedFrom->mDrawingState;
+ mClonedFrom = clonedFrom;
+
+ // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
+ // InputWindows per client token yet.
+ mDrawingState.inputInfo.token = nullptr;
+}
+
+void Layer::updateMirrorInfo() {
+ if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
+ // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
+ // it means that there is a clone, but the layer it was cloned from has been destroyed. In
+ // that case, we want to delete the reference to the clone since we want it to get
+ // destroyed. The root, this layer, will still be around since the client can continue
+ // to hold a reference, but no cloned layers will be displayed.
+ mClonedChild = nullptr;
+ return;
+ }
+
+ std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
+ // If the real layer exists and is in current state, add the clone as a child of the root.
+ // There's no need to remove from drawingState when the layer is offscreen since currentState is
+ // copied to drawingState for the root layer. So the clonedChild is always removed from
+ // drawingState and then needs to be added back each traversal.
+ if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) {
+ addChildToDrawing(mClonedChild);
+ }
+
+ mClonedChild->updateClonedDrawingState(clonedLayersMap);
+ mClonedChild->updateClonedChildren(this, clonedLayersMap);
+ mClonedChild->updateClonedRelatives(clonedLayersMap);
+}
+
+void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+ // If the layer the clone was cloned from is alive, copy the content of the drawingState
+ // to the clone. If the real layer is no longer alive, continue traversing the children
+ // since we may be able to pull out other children that are still alive.
+ if (isClonedFromAlive()) {
+ sp<Layer> clonedFrom = getClonedFrom();
+ mDrawingState = clonedFrom->mDrawingState;
+ // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
+ // InputWindows per client token yet.
+ mDrawingState.inputInfo.token = nullptr;
+ clonedLayersMap.emplace(clonedFrom, this);
+ }
+
+ // The clone layer may have children in drawingState since they may have been created and
+ // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones
+ // that already exist, since we can just re-use them.
+ // The drawingChildren will not get overwritten by the currentChildren since the clones are
+ // not updated in the regular traversal. They are skipped since the root will lose the
+ // reference to them when it copies its currentChildren to drawing.
+ for (sp<Layer>& child : mDrawingChildren) {
+ child->updateClonedDrawingState(clonedLayersMap);
+ }
+}
+
+void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot,
+ std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+ mDrawingChildren.clear();
+
+ if (!isClonedFromAlive()) {
+ return;
+ }
+
+ sp<Layer> clonedFrom = getClonedFrom();
+ for (sp<Layer>& child : clonedFrom->mDrawingChildren) {
+ if (child == mirrorRoot) {
+ // This is to avoid cyclical mirroring.
+ continue;
+ }
+ sp<Layer> clonedChild = clonedLayersMap[child];
+ if (clonedChild == nullptr) {
+ clonedChild = child->createClone();
+ clonedLayersMap[child] = clonedChild;
+ }
+ addChildToDrawing(clonedChild);
+ clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap);
+ }
+}
+
+void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap) {
+ mDrawingState.zOrderRelativeOf = nullptr;
+ mDrawingState.zOrderRelatives.clear();
+
+ if (!isClonedFromAlive()) {
+ return;
+ }
+
+ sp<Layer> clonedFrom = getClonedFrom();
+ for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
+ sp<Layer> relative = relativeWeak.promote();
+ auto clonedRelative = clonedLayersMap[relative];
+ if (clonedRelative != nullptr) {
+ mDrawingState.zOrderRelatives.add(clonedRelative);
+ }
+ }
+
+ // Check if the relativeLayer for the real layer is part of the cloned hierarchy.
+ // It's possible that the layer it's relative to is outside the requested cloned hierarchy.
+ // In that case, we treat the layer as if the relativeOf has been removed. This way, it will
+ // still traverse the children, but the layer with the missing relativeOf will not be shown
+ // on screen.
+ sp<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+ sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf];
+ if (clonedRelativeOf != nullptr) {
+ mDrawingState.zOrderRelativeOf = clonedRelativeOf;
+ }
+
+ for (sp<Layer>& child : mDrawingChildren) {
+ child->updateClonedRelatives(clonedLayersMap);
+ }
+}
+
+void Layer::addChildToDrawing(const sp<Layer>& layer) {
+ mDrawingChildren.add(layer);
+ layer->mDrawingParent = this;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8771ccd..3023cf5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -91,6 +91,8 @@
LayerMetadata metadata;
pid_t callingPid;
uid_t callingUid;
+ sp<const DisplayDevice> displayDevice;
+ uint32_t textureName;
};
class Layer : public compositionengine::LayerFE {
@@ -170,6 +172,7 @@
// If non-null, a Surface this Surface's Z-order is interpreted relative to.
wp<Layer> zOrderRelativeOf;
+ bool isRelativeOf{false};
// A list of surfaces whose Z-order is interpreted relative to ours.
SortedVector<wp<Layer>> zOrderRelatives;
@@ -465,6 +468,30 @@
virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; }
+ // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+ // variable mClonedChild represents the top layer that will be cloned so this
+ // layer will be the parent of mClonedChild.
+ // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+ // if the real layer is destroyed, then the clone layer will also be destroyed.
+ sp<Layer> mClonedChild;
+
+ virtual sp<Layer> createClone() = 0;
+ void updateMirrorInfo();
+ virtual void updateCloneBufferInfo(){};
+
+protected:
+ sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+ bool isClone() { return mClonedFrom != nullptr; }
+ bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+ virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+
+ void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedChildren(const sp<Layer>& mirrorRoot,
+ std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap);
+ void addChildToDrawing(const sp<Layer>& layer);
+
public:
/*
* compositionengine::LayerFE overrides
@@ -828,7 +855,6 @@
// We encode unset as -1.
int32_t mOverrideScalingMode{-1};
std::atomic<uint64_t> mCurrentFrameNumber{0};
- bool mFrameLatencyNeeded{false};
// Whether filtering is needed b/c of the drawingstate
bool mNeedsFiltering{false};
@@ -912,6 +938,12 @@
// to help debugging.
pid_t mCallingPid;
uid_t mCallingUid;
+
+ // The current layer is a clone of mClonedFrom. This means that this layer will update it's
+ // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
+ // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
+ // and relatives, this layer will update as well.
+ wp<Layer> mClonedFrom;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 8494524..8271fd9 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -64,7 +64,7 @@
const auto& layer = (*this)[i];
auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
: layer->getDrawingState();
- if (state.zOrderRelativeOf != nullptr) {
+ if (state.isRelativeOf) {
continue;
}
layer->traverseInZOrder(stateSet, visitor);
@@ -76,7 +76,7 @@
const auto& layer = (*this)[i];
auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
: layer->getDrawingState();
- if (state.zOrderRelativeOf != nullptr) {
+ if (state.isRelativeOf) {
continue;
}
layer->traverseInReverseZOrder(stateSet, visitor);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 740c8c4..536464e 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -32,6 +32,7 @@
~DispSyncSource() override = default;
// The following methods are implementation of VSyncSource.
+ const char* getName() const override { return mName; }
void setVSyncEnabled(bool enable) override;
void setCallback(VSyncSource::Callback* callback) override;
void setPhaseOffset(nsecs_t phaseOffset) override;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9d1f777..8d9adc8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -154,23 +154,11 @@
namespace impl {
-EventThread::EventThread(std::unique_ptr<VSyncSource> src,
- InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
- : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
- const char* threadName)
- : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
- InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
- : mVSyncSource(src),
- mVSyncSourceUnique(std::move(uniqueSrc)),
+EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+ InterceptVSyncsCallback interceptVSyncsCallback)
+ : mVSyncSource(std::move(vsyncSource)),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
- mThreadName(threadName) {
- if (src == nullptr) {
- mVSyncSource = mVSyncSourceUnique.get();
- }
+ mThreadName(mVSyncSource->getName()) {
mVSyncSource->setCallback(this);
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -178,7 +166,7 @@
threadMain(lock);
});
- pthread_setname_np(mThread.native_handle(), threadName);
+ pthread_setname_np(mThread.native_handle(), mThreadName);
pid_t tid = pthread_gettid_np(mThread.native_handle());
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index dd23b88..a029586 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -62,6 +62,8 @@
};
virtual ~VSyncSource() {}
+
+ virtual const char* getName() const = 0;
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
@@ -126,9 +128,7 @@
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
- // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
- EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
- EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
+ EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -157,10 +157,6 @@
using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
- // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
- EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
- InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
-
void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
@@ -174,9 +170,7 @@
// Implements VSyncSource::Callback
void onVSyncEvent(nsecs_t timestamp) override;
- // TODO(b/128863962): Once the Scheduler is complete this pointer will become obsolete.
- VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
- std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
+ const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
const InterceptVSyncsCallback mInterceptVSyncsCallback;
const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 90609af..6c502e6 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -42,6 +42,7 @@
}
}
+ const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
void pauseVsyncCallback(bool) {}
@@ -51,4 +52,4 @@
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index a733781..17afdda 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -109,7 +109,7 @@
bool isLowActivityLayer() const {
// We want to make sure that we received more than two frames from the layer
// in order to check low activity.
- if (mElements.size() < 2) {
+ if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
return false;
}
@@ -118,7 +118,8 @@
// Check the frame before last to determine whether there is low activity.
// If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
// infrequent updates.
- if (mElements.at(mElements.size() - 2) < obsoleteEpsilon) {
+ if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
+ obsoleteEpsilon) {
return true;
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index fcb307f..5318b00 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -85,24 +85,6 @@
mHandler = new Handler(*this);
}
-void MessageQueue::setEventThread(android::EventThread* eventThread,
- ResyncCallback resyncCallback) {
- if (mEventThread == eventThread) {
- return;
- }
-
- if (mEventTube.getFd() >= 0) {
- mLooper->removeFd(mEventTube.getFd());
- }
-
- mEventThread = eventThread;
- mEvents = eventThread->createEventConnection(std::move(resyncCallback),
- ISurfaceComposer::eConfigChangedSuppress);
- mEvents->stealReceiveChannel(&mEventTube);
- mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
- this);
-}
-
void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
if (mEventTube.getFd() >= 0) {
mLooper->removeFd(mEventTube.getFd());
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 0b2206d..fcfc4aa 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -85,8 +85,6 @@
virtual ~MessageQueue();
virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
- // TODO(b/128863962): Remove this function once everything is migrated to Scheduler.
- virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0;
virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
virtual void waitMessage() = 0;
virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
@@ -115,7 +113,6 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
- android::EventThread* mEventThread;
sp<EventThreadConnection> mEvents;
gui::BitTube mEventTube;
sp<Handler> mHandler;
@@ -126,7 +123,6 @@
public:
~MessageQueue() override = default;
void init(const sp<SurfaceFlinger>& flinger) override;
- void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
void setEventConnection(const sp<EventThreadConnection>& connection) override;
void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index add56fc..d60e101 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -41,6 +41,7 @@
#include "DispSyncSource.h"
#include "EventControlThread.h"
#include "EventThread.h"
+#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
#include "SchedulerUtils.h"
#include "SurfaceFlingerProperties.h"
@@ -116,11 +117,20 @@
return *mPrimaryDispSync;
}
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
+ const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) {
+ return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
+ offsetThresholdForNextVsync, true /* traceVsync */,
+ name);
+}
+
Scheduler::ConnectionHandle Scheduler::createConnection(
const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto eventThread = makeEventThread(connectionName, phaseOffsetNs, offsetThresholdForNextVsync,
- std::move(interceptCallback));
+ auto vsyncSource =
+ makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+ auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+ std::move(interceptCallback));
return createConnection(std::move(eventThread));
}
@@ -135,16 +145,6 @@
return handle;
}
-std::unique_ptr<EventThread> Scheduler::makeEventThread(
- const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
- impl::EventThread::InterceptVSyncsCallback&& interceptCallback) {
- auto source = std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
- offsetThresholdForNextVsync,
- true /* traceVsync */, connectionName);
- return std::make_unique<impl::EventThread>(std::move(source), std::move(interceptCallback),
- connectionName);
-}
-
sp<EventThreadConnection> Scheduler::createConnectionInternal(
EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
return eventThread->createEventConnection([&] { resync(); }, configChanged);
@@ -156,11 +156,6 @@
return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
}
-EventThread* Scheduler::getEventThread(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return mConnections[handle].thread.get();
-}
-
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return mConnections[handle].connection;
@@ -203,6 +198,37 @@
stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
}
+Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+ if (mInjectVSyncs == enable) {
+ return {};
+ }
+
+ ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
+
+ if (!mInjectorConnectionHandle) {
+ auto vsyncSource = std::make_unique<InjectVSyncSource>();
+ mVSyncInjector = vsyncSource.get();
+
+ auto eventThread =
+ std::make_unique<impl::EventThread>(std::move(vsyncSource),
+ impl::EventThread::InterceptVSyncsCallback());
+
+ mInjectorConnectionHandle = createConnection(std::move(eventThread));
+ }
+
+ mInjectVSyncs = enable;
+ return mInjectorConnectionHandle;
+}
+
+bool Scheduler::injectVSync(nsecs_t when) {
+ if (!mInjectVSyncs || !mVSyncInjector) {
+ return false;
+ }
+
+ mVSyncInjector->onInjectSyncEvent(when);
+ return true;
+}
+
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0c8c335..a5971fe 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,7 @@
class DispSync;
class FenceTime;
+class InjectVSyncSource;
struct DisplayStateInfo;
class Scheduler {
@@ -63,7 +64,6 @@
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
ISurfaceComposer::ConfigChanged);
- EventThread* getEventThread(ConnectionHandle);
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
@@ -77,6 +77,12 @@
void getDisplayStatInfo(DisplayStatInfo* stats);
+ // Returns injector handle if injection has toggled, or an invalid handle otherwise.
+ ConnectionHandle enableVSyncInjection(bool enable);
+
+ // Returns false if injection is disabled.
+ bool injectVSync(nsecs_t when);
+
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
@@ -138,12 +144,10 @@
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
const scheduler::RefreshRateConfigs&);
- // Creates a connection on the given EventThread and forwards the given callbacks.
- std::unique_ptr<EventThread> makeEventThread(const char* connectionName, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync,
- impl::EventThread::InterceptVSyncsCallback&&);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync);
- // Create a connection on the given EventThread and forward the resync callback.
+ // Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(EventThread*,
ISurfaceComposer::ConfigChanged);
@@ -173,6 +177,10 @@
ConnectionHandle::Id mNextConnectionHandleId = 0;
std::unordered_map<ConnectionHandle, Connection> mConnections;
+ bool mInjectVSyncs = false;
+ InjectVSyncSource* mVSyncInjector = nullptr;
+ ConnectionHandle mInjectorConnectionHandle;
+
std::mutex mHWVsyncLock;
bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 3bb3a6f..3b7567c 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -46,9 +46,11 @@
// or waiting idle in messaging app, when cursor is blinking.
static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
-// Layer is considered low activity if the buffers come more than LOW_ACTIVITY_EPSILON_NS
-// apart. This is helping SF to vote for lower refresh rates when there is not activity
+// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
+// LOW_ACTIVITY_EPSILON_NS apart.
+// This is helping SF to vote for lower refresh rates when there is not activity
// in screen.
+static constexpr int LOW_ACTIVITY_BUFFERS = 2;
static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
// Calculates the statistical mean (average) in the data structure (array, vector). The
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1555079..b9e95a6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -101,7 +101,6 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
-#include "Scheduler/InjectVSyncSource.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
@@ -337,6 +336,11 @@
mPropagateBackpressure = !atoi(value);
ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
+ property_get("debug.sf.enable_gl_backpressure", value, "0");
+ mPropagateBackpressureClientComposition = atoi(value);
+ ALOGI_IF(mPropagateBackpressureClientComposition,
+ "Enabling backpressure propagation for Client Composition");
+
property_get("debug.sf.enable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = atoi(value);
ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
@@ -809,11 +813,15 @@
info.viewportW = uint32_t(viewport.getWidth());
info.viewportH = uint32_t(viewport.getHeight());
}
+ info.layerStack = display->getLayerStack();
} else {
// TODO: where should this value come from?
static const int TV_DENSITY = 213;
info.density = TV_DENSITY / 160.0f;
info.orientation = 0;
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ info.layerStack = display->getLayerStack();
}
info.xdpi = xdpi;
@@ -868,7 +876,16 @@
return BAD_VALUE;
}
- return display->getActiveConfig();
+ if (display->isPrimary()) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mDesiredActiveConfigChanged) {
+ return mDesiredActiveConfig.configId;
+ } else {
+ return display->getActiveConfig();
+ }
+ } else {
+ return display->getActiveConfig();
+ }
}
void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
@@ -1190,48 +1207,20 @@
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
postMessageSync(new LambdaMessage([&] {
- Mutex::Autolock _l(mStateLock);
+ Mutex::Autolock lock(mStateLock);
- if (mInjectVSyncs == enable) {
- return;
+ if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
+ mEventQueue->setEventConnection(
+ mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
}
-
- // TODO(b/128863962): Part of the Injector should be refactored, so that it
- // can be passed to Scheduler.
- if (enable) {
- ALOGV("VSync Injections enabled");
- if (mVSyncInjector.get() == nullptr) {
- mVSyncInjector = std::make_unique<InjectVSyncSource>();
- mInjectorEventThread = std::make_unique<
- impl::EventThread>(mVSyncInjector.get(),
- impl::EventThread::InterceptVSyncsCallback(),
- "injEventThread");
- }
- mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); });
- } else {
- ALOGV("VSync Injections disabled");
- mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
- [&] { mScheduler->resync(); });
- }
-
- mInjectVSyncs = enable;
}));
return NO_ERROR;
}
status_t SurfaceFlinger::injectVSync(nsecs_t when) {
- Mutex::Autolock _l(mStateLock);
-
- if (!mInjectVSyncs) {
- ALOGE("VSync Injections not enabled");
- return BAD_VALUE;
- }
- if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
- ALOGV("Injecting VSync inside SurfaceFlinger");
- mVSyncInjector->onInjectSyncEvent(when);
- }
- return NO_ERROR;
+ Mutex::Autolock lock(mStateLock);
+ return mScheduler->injectVSync(when) ? NO_ERROR : BAD_VALUE;
}
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
@@ -1529,8 +1518,7 @@
// any HWC layers are destroyed through that interface before it becomes
// invalid.
for (const auto& [token, displayDevice] : mDisplays) {
- displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ(
- compositionengine::Output::OutputLayers());
+ displayDevice->getCompositionDisplay()->clearOutputLayers();
}
// This DisplayDevice will no longer be relevant once resetDisplayState() is
@@ -1583,7 +1571,8 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
@@ -1592,7 +1581,15 @@
? mPreviousPresentFences[0]
: mPreviousPresentFences[1];
- return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
+ if (fence == Fence::NO_FENCE) {
+ return false;
+ }
+
+ if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) {
+ fence->wait(graceTimeMs);
+ }
+
+ return (fence->getStatus() == Fence::Status::Unsignaled);
}
void SurfaceFlinger::populateExpectedPresentTime() {
@@ -1615,7 +1612,17 @@
// seeing this same value.
populateExpectedPresentTime();
- const TracedOrdinal<bool> frameMissed = {"FrameMissed", previousFrameMissed()};
+ // When Backpressure propagation is enabled we want to give a small grace period
+ // for the present fence to fire instead of just giving up on this frame to handle cases
+ // where present fence is just about to get signaled.
+ const int graceTimeForPresentFenceMs =
+ (mPropagateBackpressure &&
+ (mPropagateBackpressureClientComposition || !mHadClientComposition))
+ ? 1
+ : 0;
+ const TracedOrdinal<bool> frameMissed = {"FrameMissed",
+ previousFrameMissed(
+ graceTimeForPresentFenceMs)};
const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
mHadDeviceComposition && frameMissed};
const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
@@ -1644,9 +1651,9 @@
break;
}
- // For now, only propagate backpressure when missing a hwc frame.
- if (hwcFrameMissed && !gpuFrameMissed) {
- if (mPropagateBackpressure) {
+ if (frameMissed && mPropagateBackpressure) {
+ if ((hwcFrameMissed && !gpuFrameMissed) ||
+ mPropagateBackpressureClientComposition) {
signalLayerUpdate();
break;
}
@@ -2468,7 +2475,7 @@
setInputWindowsFinished();
}
- executeInputWindowCommands();
+ mInputWindowCommands.clear();
}
void SurfaceFlinger::updateInputWindowInfo() {
@@ -2492,19 +2499,6 @@
mPendingInputWindowCommands.clear();
}
-void SurfaceFlinger::executeInputWindowCommands() {
- for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) {
- if (transferTouchFocusCommand.fromToken != nullptr &&
- transferTouchFocusCommand.toToken != nullptr &&
- transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) {
- mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken,
- transferTouchFocusCommand.toToken);
- }
- }
-
- mInputWindowCommands.clear();
-}
-
void SurfaceFlinger::updateCursorAsync()
{
compositionengine::CompositionRefreshArgs refreshArgs;
@@ -2568,53 +2562,56 @@
void SurfaceFlinger::commitTransaction()
{
- withTracingLock([&]() {
- if (!mLayersPendingRemoval.isEmpty()) {
- // Notify removed layers now that they can't be drawn from
- for (const auto& l : mLayersPendingRemoval) {
- recordBufferingStats(l->getName().string(), l->getOccupancyHistory(true));
-
- // Ensure any buffers set to display on any children are released.
- if (l->isRemovedFromCurrentState()) {
- l->latchAndReleaseBuffer();
- }
-
- // If the layer has been removed and has no parent, then it will not be reachable
- // when traversing layers on screen. Add the layer to the offscreenLayers set to
- // ensure we can copy its current to drawing state.
- if (!l->getParent()) {
- mOffscreenLayers.emplace(l.get());
- }
- }
- mLayersPendingRemoval.clear();
- }
-
- // If this transaction is part of a window animation then the next frame
- // we composite should be considered an animation as well.
- mAnimCompositionPending = mAnimTransactionPending;
-
- mDrawingState = mCurrentState;
- // clear the "changed" flags in current state
- mCurrentState.colorMatrixChanged = false;
-
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- layer->commitChildList();
-
- // If the layer can be reached when traversing mDrawingState, then the layer is no
- // longer offscreen. Remove the layer from the offscreenLayer set.
- if (mOffscreenLayers.count(layer)) {
- mOffscreenLayers.erase(layer);
- }
- });
-
- commitOffscreenLayers();
- });
+ withTracingLock([this]() { commitTransactionLocked(); });
mTransactionPending = false;
mAnimTransactionPending = false;
mTransactionCV.broadcast();
}
+void SurfaceFlinger::commitTransactionLocked() {
+ if (!mLayersPendingRemoval.isEmpty()) {
+ // Notify removed layers now that they can't be drawn from
+ for (const auto& l : mLayersPendingRemoval) {
+ recordBufferingStats(l->getName().string(), l->getOccupancyHistory(true));
+
+ // Ensure any buffers set to display on any children are released.
+ if (l->isRemovedFromCurrentState()) {
+ l->latchAndReleaseBuffer();
+ }
+
+ // If the layer has been removed and has no parent, then it will not be reachable
+ // when traversing layers on screen. Add the layer to the offscreenLayers set to
+ // ensure we can copy its current to drawing state.
+ if (!l->getParent()) {
+ mOffscreenLayers.emplace(l.get());
+ }
+ }
+ mLayersPendingRemoval.clear();
+ }
+
+ // If this transaction is part of a window animation then the next frame
+ // we composite should be considered an animation as well.
+ mAnimCompositionPending = mAnimTransactionPending;
+
+ mDrawingState = mCurrentState;
+ // clear the "changed" flags in current state
+ mCurrentState.colorMatrixChanged = false;
+
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ layer->commitChildList();
+
+ // If the layer can be reached when traversing mDrawingState, then the layer is no
+ // longer offscreen. Remove the layer from the offscreenLayer set.
+ if (mOffscreenLayers.count(layer)) {
+ mOffscreenLayers.erase(layer);
+ }
+ });
+
+ commitOffscreenLayers();
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); });
+}
+
void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
if (mTracingEnabledChanged) {
mTracingEnabled = mTracing.isEnabled();
@@ -2731,6 +2728,8 @@
mBootStage = BootStage::BOOTANIMATION;
}
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+
// Only continue with the refresh if there is actually new work to do
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
@@ -2839,9 +2838,9 @@
transactions.push_back(transaction);
applyTransactionState(transaction.states, transaction.displays, transaction.flags,
mPendingInputWindowCommands, transaction.desiredPresentTime,
- transaction.buffer, transaction.callback,
- transaction.postTime, transaction.privileged,
- /*isMainThread*/ true);
+ transaction.buffer, transaction.postTime,
+ transaction.privileged, transaction.hasListenerCallbacks,
+ transaction.listenerCallbacks, /*isMainThread*/ true);
transactionQueue.pop();
flushedATransaction = true;
}
@@ -2888,13 +2887,11 @@
return true;
}
-void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
- const std::vector<ListenerCallbacks>& listenerCallbacks) {
+void SurfaceFlinger::setTransactionState(
+ const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) {
ATRACE_CALL();
const int64_t postTime = systemTime();
@@ -2924,24 +2921,23 @@
if (itr != mTransactionQueues.end() || !transactionIsReadyToBeApplied(
desiredPresentTime, false /* useCachedExpectedPresentTime */, states)) {
mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
- uncacheBuffer, listenerCallbacks, postTime,
- privileged);
+ uncacheBuffer, postTime, privileged,
+ hasListenerCallbacks, listenerCallbacks);
setTransactionFlags(eTransactionFlushNeeded);
return;
}
applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
- uncacheBuffer, listenerCallbacks, postTime, privileged);
+ uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+ listenerCallbacks);
}
-void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
- const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- const int64_t postTime, bool privileged,
- bool isMainThread) {
+void SurfaceFlinger::applyTransactionState(
+ const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -2964,28 +2960,27 @@
transactionFlags |= setDisplayStateLocked(display);
}
- // In case the client has sent a Transaction that should receive callbacks but without any
- // SurfaceControls that should be included in the callback, send the listener and callbackIds
- // to the callback thread so it can send an empty callback
- if (!listenerCallbacks.empty()) {
- mTransactionCompletedThread.run();
- }
- for (const auto& listenerCallback : listenerCallbacks) {
- mTransactionCompletedThread.startRegistration(listenerCallback);
+ // start and end registration for listeners w/ no surface so they can get their callback. Note
+ // that listeners with SurfaceControls will start registration during setClientStateLocked
+ // below.
+ for (const auto& listener : listenerCallbacks) {
+ mTransactionCompletedThread.startRegistration(listener);
+ mTransactionCompletedThread.endRegistration(listener);
}
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
- postTime, privileged);
+ clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
+ listenerCallbacksWithSurfaces);
}
- for (const auto& listenerCallback : listenerCallbacks) {
+ for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
mTransactionCompletedThread.endRegistration(listenerCallback);
}
// If the state doesn't require a traversal and there are callbacks, send them now
- if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) {
+ if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
mTransactionCompletedThread.sendCallbacks();
}
transactionFlags |= clientStateFlags;
@@ -3113,17 +3108,23 @@
}
uint32_t SurfaceFlinger::setClientStateLocked(
- const ComposerState& composerState, int64_t desiredPresentTime,
- const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
- bool privileged) {
+ const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+ bool privileged,
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
const layer_state_t& s = composerState.state;
+ for (auto& listener : s.listeners) {
+ // note that startRegistration will not re-register if the listener has
+ // already be registered for a prior surface control
+ mTransactionCompletedThread.startRegistration(listener);
+ listenerCallbacks.insert(listener);
+ }
+
sp<Layer> layer(fromHandle(s.surface));
if (layer == nullptr) {
- for (auto& listenerCallback : listenerCallbacks) {
+ for (auto& [listener, callbackIds] : s.listeners) {
mTransactionCompletedThread.registerUnpresentedCallbackHandle(
- new CallbackHandle(listenerCallback.transactionCompletedListener,
- listenerCallback.callbackIds, s.surface));
+ new CallbackHandle(listener, callbackIds, s.surface));
}
return 0;
}
@@ -3346,8 +3347,8 @@
}
}
std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
+ for (auto& [listener, callbackIds] : s.listeners) {
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
@@ -3396,6 +3397,35 @@
return flags;
}
+status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+ sp<IBinder>* outHandle) {
+ if (!mirrorFromHandle) {
+ return NAME_NOT_FOUND;
+ }
+
+ sp<Layer> mirrorLayer;
+ sp<Layer> mirrorFrom;
+ String8 uniqueName = getUniqueLayerName(String8("MirrorRoot"));
+
+ {
+ Mutex::Autolock _l(mStateLock);
+ mirrorFrom = fromHandle(mirrorFromHandle);
+ if (!mirrorFrom) {
+ return NAME_NOT_FOUND;
+ }
+
+ status_t result = createContainerLayer(client, uniqueName, -1, -1, 0, LayerMetadata(),
+ outHandle, &mirrorLayer);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ mirrorLayer->mClonedChild = mirrorFrom->createClone();
+ }
+
+ return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false);
+}
+
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
uint32_t h, PixelFormat format, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
@@ -3531,8 +3561,18 @@
break;
}
- sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer(
- LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+ sp<BufferQueueLayer> layer;
+ LayerCreationArgs args =
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata));
+ args.textureName = getNewTexture();
+ {
+ // Grab the SF state lock during this since it's the only safe way to access
+ // RenderEngine when creating a BufferLayerConsumer
+ // TODO: Check if this lock is still needed here
+ Mutex::Autolock lock(mStateLock);
+ layer = getFactory().createBufferQueueLayer(args);
+ }
+
status_t err = layer->setDefaultBufferProperties(w, h, format);
if (err == NO_ERROR) {
*handle = layer->getHandle();
@@ -3548,8 +3588,11 @@
uint32_t w, uint32_t h, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(
- LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+ LayerCreationArgs args =
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata));
+ args.displayDevice = getDefaultDisplayDevice();
+ args.textureName = getNewTexture();
+ sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
*handle = layer->getHandle();
*outLayer = layer;
@@ -3629,7 +3672,8 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
+ setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
+ {});
setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
@@ -4092,6 +4136,7 @@
const int32_t offscreenRootLayerId = INT32_MAX - 2;
rootProto->set_id(offscreenRootLayerId);
rootProto->set_name("Offscreen Root");
+ rootProto->set_parent(-1);
for (Layer* offscreenLayer : mOffscreenLayers) {
// Add layer as child of the fake root
@@ -4211,6 +4256,12 @@
result.append("\n");
/*
+ * Dump CompositionEngine state
+ */
+
+ mCompositionEngine->dump(result);
+
+ /*
* Dump SurfaceFlinger global state
*/
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c9e4692..a9a4276 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,7 +89,6 @@
class HWComposer;
class IGraphicBufferProducer;
class IInputFlinger;
-class InjectVSyncSource;
class Layer;
class MessageBase;
class RefreshRateOverlay;
@@ -347,9 +346,11 @@
static const size_t MAX_LAYERS = 4096;
static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
+private:
/* ------------------------------------------------------------------------
* Internal data structures
*/
@@ -400,6 +401,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+ bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
@@ -548,7 +550,6 @@
void updateInputFlinger();
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
- void executeInputWindowCommands();
void setInputWindowsFinished();
void updateCursorAsync();
void initScheduler(DisplayId primaryDisplayId);
@@ -566,10 +567,10 @@
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer,
+ const client_cache_t& uncacheBuffer, const int64_t postTime,
+ bool privileged, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
- const int64_t postTime, bool privileged, bool isMainThread = false)
- REQUIRES(mStateLock);
+ bool isMainThread = false) REQUIRES(mStateLock);
// Returns true if at least one transaction was flushed
bool flushTransactionQueues();
// Returns true if there is at least one transaction that needs to be flushed
@@ -584,13 +585,19 @@
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
bool useCachedExpectedPresentTime,
const Vector<ComposerState>& states);
- uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- int64_t postTime, bool privileged) REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
+protected:
+ virtual uint32_t setClientStateLocked(
+ const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+ bool privileged,
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+ REQUIRES(mStateLock);
+ virtual void commitTransactionLocked();
+
+private:
/* ------------------------------------------------------------------------
* Layer management
*/
@@ -616,6 +623,9 @@
uint32_t h, uint32_t flags, LayerMetadata metadata,
sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+ sp<IBinder>* outHandle);
+
String8 getUniqueLayerName(const String8& name);
// called when all clients have released all their references to
@@ -777,7 +787,7 @@
bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock);
- bool previousFrameMissed();
+ bool previousFrameMissed(int graceTimeMs = 0);
// Populates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
@@ -928,8 +938,6 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
bool mGpuToCpuSupported = false;
- std::unique_ptr<EventThread> mInjectorEventThread;
- std::unique_ptr<InjectVSyncSource> mVSyncInjector;
// Can only accessed from the main thread, these members
// don't need synchronization
@@ -979,6 +987,7 @@
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
bool mPropagateBackpressure = true;
+ bool mPropagateBackpressureClientComposition = false;
std::unique_ptr<SurfaceInterceptor> mInterceptor;
SurfaceTracing mTracing{*this};
bool mTracingEnabled = false;
@@ -1018,25 +1027,27 @@
TransactionState(const Vector<ComposerState>& composerStates,
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
- bool privileged)
+ int64_t postTime, bool privileged, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> listenerCallbacks)
: states(composerStates),
displays(displayStates),
flags(transactionFlags),
desiredPresentTime(desiredPresentTime),
buffer(uncacheBuffer),
- callback(listenerCallbacks),
postTime(postTime),
- privileged(privileged) {}
+ privileged(privileged),
+ hasListenerCallbacks(hasListenerCallbacks),
+ listenerCallbacks(listenerCallbacks) {}
Vector<ComposerState> states;
Vector<DisplayState> displays;
uint32_t flags;
const int64_t desiredPresentTime;
client_cache_t buffer;
- std::vector<ListenerCallbacks> callback;
const int64_t postTime;
bool privileged;
+ bool hasListenerCallbacks;
+ std::vector<ListenerCallbacks> listenerCallbacks;
};
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
@@ -1044,8 +1055,6 @@
* Feature prototyping
*/
- bool mInjectVSyncs = false;
-
// Static screen stats
bool mHasPoweredOff = false;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 4f303a3..3fd4de8 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -54,7 +54,9 @@
namespace scheduler {
class PhaseOffsets;
+class RefreshRateConfigs;
} // namespace scheduler
+
namespace surfaceflinger {
class NativeWindowSurface;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 1324f20..92e59d7 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -75,27 +75,29 @@
}
status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+ // begin running if not already running
+ run();
std::lock_guard lock(mMutex);
if (!mRunning) {
ALOGE("cannot add callback because the callback thread isn't running");
return BAD_VALUE;
}
+ auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
auto& [listener, callbackIds] = listenerCallbacks;
- if (mCompletedTransactions.count(listener) == 0) {
- status_t err = listener->linkToDeath(mDeathRecipient);
- if (err != NO_ERROR) {
- ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
- return err;
+ if (inserted) {
+ if (mCompletedTransactions.count(listener) == 0) {
+ status_t err = listener->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+ return err;
+ }
}
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
}
- mRegisteringTransactions.insert(listenerCallbacks);
-
- auto& transactionStatsDeque = mCompletedTransactions[listener];
- transactionStatsDeque.emplace_back(callbackIds);
-
return NO_ERROR;
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 159c2a4..d021fc2 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -19,10 +19,21 @@
srcs: [
"BufferGenerator.cpp",
"Credentials_test.cpp",
+ "DereferenceSurfaceControl_test.cpp",
+ "DisplayActiveConfig_test.cpp",
"InvalidHandles_test.cpp",
+ "LayerCallback_test.cpp",
+ "LayerRenderTypeTransaction_test.cpp",
+ "LayerTransaction_test.cpp",
+ "LayerTypeAndRenderTypeTransaction_test.cpp",
+ "LayerTypeTransaction_test.cpp",
+ "LayerUpdate_test.cpp",
+ "MirrorLayer_test.cpp",
+ "MultiDisplayLayerBounds_test.cpp",
+ "RelativeZ_test.cpp",
+ "SetGeometry_test.cpp",
"Stress_test.cpp",
"SurfaceInterceptor_test.cpp",
- "Transaction_test.cpp",
"VirtualDisplay_test.cpp",
],
data: ["SurfaceFlinger_test.filter"],
@@ -43,7 +54,48 @@
"libui",
"libutils",
]
+}
+cc_defaults {
+ name: "ipc_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "IPC_test",
+ defaults: ["ipc_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "BufferGenerator.cpp",
+ "IPC_test.cpp",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+ shared_libs: [
+ "libandroid",
+ "libbinder",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libgui",
+ "liblayers_proto",
+ "liblog",
+ "libprotobuf-cpp-full",
+ "libtimestats_proto",
+ "libui",
+ "libutils",
+ ],
+ cpp_std: "experimental",
+ gnu_extensions: false,
}
subdirs = [
diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
new file mode 100644
index 0000000..0cef0d1
--- /dev/null
+++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class DereferenceSurfaceControlTest : public LayerTransactionTest {
+protected:
+ void SetUp() override {
+ LayerTransactionTest::SetUp();
+ bgLayer = createLayer("BG layer", 20, 20);
+ fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
+ fgLayer = createLayer("FG layer", 20, 20);
+ fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
+ Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
+ {
+ SCOPED_TRACE("before anything");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+ }
+ }
+ void TearDown() override {
+ LayerTransactionTest::TearDown();
+ bgLayer = 0;
+ fgLayer = 0;
+ }
+
+ sp<SurfaceControl> bgLayer;
+ sp<SurfaceControl> fgLayer;
+};
+
+TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
+ fgLayer = nullptr;
+ {
+ SCOPED_TRACE("after setting null");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
+ }
+}
+
+TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
+ auto transaction = Transaction().show(fgLayer);
+ fgLayer = nullptr;
+ {
+ SCOPED_TRACE("after setting null");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
new file mode 100644
index 0000000..2e3b760
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class DisplayActiveConfigTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
+ EXPECT_GT(mDisplayconfigs.size(), 0);
+
+ // set display power to on to make sure config can be changed
+ SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
+ }
+
+ sp<IBinder> mDisplayToken;
+ Vector<DisplayInfo> mDisplayconfigs;
+};
+
+TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
+ std::vector<int32_t> allowedConfigs;
+
+ // Add all configs to the allowed configs
+ for (int i = 0; i < mDisplayconfigs.size(); i++) {
+ allowedConfigs.push_back(i);
+ }
+
+ status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+
+ std::vector<int32_t> outConfigs;
+ res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(allowedConfigs, outConfigs);
+}
+
+TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
+ // we need at least 2 configs available for this test
+ if (mDisplayconfigs.size() <= 1) return;
+
+ int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
+
+ // We want to set the allowed config to everything but the active config
+ std::vector<int32_t> allowedConfigs;
+ for (int i = 0; i < mDisplayconfigs.size(); i++) {
+ if (i != activeConfig) {
+ allowedConfigs.push_back(i);
+ }
+ }
+
+ status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+
+ // Allow some time for the config change
+ std::this_thread::sleep_for(200ms);
+
+ int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
+ EXPECT_NE(activeConfig, newActiveConfig);
+
+ // Make sure the new config is part of allowed config
+ EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
+ allowedConfigs.end());
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
new file mode 100644
index 0000000..8a756a6
--- /dev/null
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <limits>
+
+#include <ui/DisplayInfo.h>
+
+#include <utils/String8.h>
+
+#include "BufferGenerator.h"
+#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using CallbackInfo = SurfaceComposerClient::CallbackInfo;
+using TCLHash = SurfaceComposerClient::TCLHash;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class TransactionHelper : public Transaction {
+public:
+ size_t getNumListeners() { return mListenerCallbacks.size(); }
+
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+ getListenerCallbacks() {
+ return mListenerCallbacks;
+ }
+};
+
+class IPCTestUtils {
+public:
+ static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+ bool finalState = false);
+ static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+};
+
+class IIPCTest : public IInterface {
+public:
+ DECLARE_META_INTERFACE(IPCTest)
+ enum class Tag : uint32_t {
+ SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+ InitClient,
+ CreateTransaction,
+ MergeAndApply,
+ VerifyCallbacks,
+ CleanUp,
+ Last,
+ };
+
+ virtual status_t setDeathToken(sp<IBinder>& token) = 0;
+
+ virtual status_t initClient() = 0;
+
+ virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width,
+ uint32_t height) = 0;
+
+ virtual status_t mergeAndApply(TransactionHelper transaction) = 0;
+
+ virtual status_t verifyCallbacks() = 0;
+
+ virtual status_t cleanUp() = 0;
+};
+
+class BpIPCTest : public SafeBpInterface<IIPCTest> {
+public:
+ explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {}
+
+ status_t setDeathToken(sp<IBinder>& token) {
+ return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token);
+ }
+
+ status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); }
+
+ status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+ return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction,
+ transaction, width, height);
+ }
+
+ status_t mergeAndApply(TransactionHelper transaction) {
+ return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction);
+ }
+
+ status_t verifyCallbacks() {
+ return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks);
+ }
+
+ status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); }
+};
+
+IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest")
+
+class onTestDeath : public IBinder::DeathRecipient {
+public:
+ void binderDied(const wp<IBinder>& /*who*/) override {
+ ALOGE("onTestDeath::binderDied, exiting");
+ exit(0);
+ }
+};
+
+sp<onTestDeath> getDeathToken() {
+ static sp<onTestDeath> token = new onTestDeath;
+ return token;
+}
+
+class BnIPCTest : public SafeBnInterface<IIPCTest> {
+public:
+ BnIPCTest() : SafeBnInterface("BnIPCTest") {}
+
+ status_t setDeathToken(sp<IBinder>& token) override {
+ return token->linkToDeath(getDeathToken());
+ }
+
+ status_t initClient() override {
+ mClient = new SurfaceComposerClient;
+ auto err = mClient->initCheck();
+ return err;
+ }
+
+ status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+ if (transaction == nullptr) {
+ ALOGE("Error in createTransaction: transaction is nullptr");
+ return BAD_VALUE;
+ }
+ mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+ sp<GraphicBuffer> gb;
+ sp<Fence> fence;
+ int err = IPCTestUtils::getBuffer(&gb, &fence);
+ if (err != NO_ERROR) return err;
+
+ TransactionUtils::fillGraphicBufferColor(gb,
+ {0, 0, static_cast<int32_t>(width),
+ static_cast<int32_t>(height)},
+ Color::RED);
+ transaction->setLayerStack(mSurfaceControl, 0)
+ .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+ .setFrame(mSurfaceControl, Rect(0, 0, width, height))
+ .setBuffer(mSurfaceControl, gb)
+ .setAcquireFence(mSurfaceControl, fence)
+ .show(mSurfaceControl)
+ .addTransactionCompletedCallback(mCallbackHelper.function,
+ mCallbackHelper.getContext());
+ return NO_ERROR;
+ }
+
+ status_t mergeAndApply(TransactionHelper /*transaction*/) {
+ // transaction.apply();
+ return NO_ERROR;
+ }
+
+ status_t verifyCallbacks() {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl);
+ EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true));
+ return NO_ERROR;
+ }
+
+ status_t cleanUp() {
+ if (mClient) mClient->dispose();
+ mSurfaceControl = nullptr;
+ IPCThreadState::self()->stopProcess();
+ return NO_ERROR;
+ }
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/) override {
+ EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+ EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last));
+ switch (static_cast<IIPCTest::Tag>(code)) {
+ case IIPCTest::Tag::SetDeathToken:
+ return callLocal(data, reply, &IIPCTest::setDeathToken);
+ case IIPCTest::Tag::InitClient:
+ return callLocal(data, reply, &IIPCTest::initClient);
+ case IIPCTest::Tag::CreateTransaction:
+ return callLocal(data, reply, &IIPCTest::createTransaction);
+ case IIPCTest::Tag::MergeAndApply:
+ return callLocal(data, reply, &IIPCTest::mergeAndApply);
+ case IIPCTest::Tag::VerifyCallbacks:
+ return callLocal(data, reply, &IIPCTest::verifyCallbacks);
+ case IIPCTest::Tag::CleanUp:
+ return callLocal(data, reply, &IIPCTest::cleanUp);
+ default:
+ return UNKNOWN_ERROR;
+ }
+ }
+
+private:
+ sp<SurfaceComposerClient> mClient;
+ sp<SurfaceControl> mSurfaceControl;
+ CallbackHelper mCallbackHelper;
+};
+
+class IPCTest : public ::testing::Test {
+public:
+ IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) {
+ ProcessState::self()->startThreadPool();
+ }
+ void SetUp() {
+ mClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mPrimaryDisplay = mClient->getInternalDisplayToken();
+ DisplayInfo info;
+ mClient->getDisplayInfo(mPrimaryDisplay, &info);
+ mDisplayWidth = info.w;
+ mDisplayHeight = info.h;
+
+ Transaction setupTransaction;
+ setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+ setupTransaction.apply();
+ }
+
+protected:
+ sp<IIPCTest> initRemoteService();
+
+ sp<IBinder> mDeathRecipient;
+ sp<IIPCTest> mRemote;
+ sp<SurfaceComposerClient> mClient;
+ sp<IBinder> mPrimaryDisplay;
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
+ sp<SurfaceControl> sc;
+};
+
+status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.get(outBuffer, outFence);
+}
+
+void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+ bool finalState) {
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+ EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+ if (finalState) {
+ ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+ }
+}
+
+sp<IIPCTest> IPCTest::initRemoteService() {
+ static std::mutex mMutex;
+ static sp<IIPCTest> remote;
+ const String16 serviceName("IPCTest");
+
+ std::unique_lock<decltype(mMutex)> lock;
+ if (remote == nullptr) {
+ pid_t forkPid = fork();
+ EXPECT_NE(forkPid, -1);
+
+ if (forkPid == 0) {
+ sp<IIPCTest> nativeService = new BnIPCTest;
+ if (!nativeService) {
+ ALOGE("null service...");
+ }
+ status_t err = defaultServiceManager()->addService(serviceName,
+ IInterface::asBinder(nativeService));
+ if (err != NO_ERROR) {
+ ALOGE("failed to add service: %d", err);
+ }
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ [&]() { exit(0); }();
+ }
+ sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+ remote = interface_cast<IIPCTest>(binder);
+ remote->setDeathToken(mDeathRecipient);
+ }
+ return remote;
+}
+
+TEST_F(IPCTest, MergeBasic) {
+ CallbackHelper helper1;
+ sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+ sp<GraphicBuffer> gb;
+ sp<Fence> fence;
+ int err = IPCTestUtils::getBuffer(&gb, &fence);
+ ASSERT_EQ(NO_ERROR, err);
+ TransactionUtils::fillGraphicBufferColor(gb,
+ {0, 0, static_cast<int32_t>(mDisplayWidth),
+ static_cast<int32_t>(mDisplayHeight)},
+ Color::RED);
+
+ Transaction transaction;
+ transaction.setLayerStack(sc, 0)
+ .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
+ .setBuffer(sc, gb)
+ .setAcquireFence(sc, fence)
+ .show(sc)
+ .addTransactionCompletedCallback(helper1.function, helper1.getContext());
+
+ TransactionHelper remote;
+ mRemote->initClient();
+ mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2);
+ ASSERT_EQ(1, remote.getNumListeners());
+ auto remoteListenerCallbacks = remote.getListenerCallbacks();
+ auto remoteCallback = remoteListenerCallbacks.begin();
+ auto remoteCallbackInfo = remoteCallback->second;
+ auto remoteListenerScs = remoteCallbackInfo.surfaceControls;
+ ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size());
+ ASSERT_EQ(1, remoteListenerScs.size());
+
+ sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin());
+ transaction.merge(std::move(remote));
+ transaction.apply();
+
+ sleep(1);
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc);
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc);
+ EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true));
+
+ mRemote->verifyCallbacks();
+ mRemote->cleanUp();
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
new file mode 100644
index 0000000..7a5ed48
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerCallbackTest : public LayerTransactionTest {
+public:
+ virtual sp<SurfaceControl> createBufferStateLayer() {
+ return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+ }
+
+ static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+ const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
+ bool setBackgroundColor = false) {
+ if (layer) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ if (setBuffer) {
+ int err = getBuffer(&buffer, &fence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transaction.setBuffer(layer, buffer);
+ transaction.setAcquireFence(layer, fence);
+ }
+
+ if (setBackgroundColor) {
+ transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
+ ui::Dataspace::UNKNOWN);
+ }
+ }
+
+ transaction.addTransactionCompletedCallback(callbackHelper->function,
+ callbackHelper->getContext());
+ return NO_ERROR;
+ }
+
+ static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+ bool finalState = false) {
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+ EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+ if (finalState) {
+ ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+ }
+ }
+
+ static void waitForCallbacks(CallbackHelper& helper,
+ const std::vector<ExpectedResult>& expectedResults,
+ bool finalState = false) {
+ for (const auto& expectedResult : expectedResults) {
+ waitForCallback(helper, expectedResult);
+ }
+ if (finalState) {
+ ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+ }
+ }
+};
+
+TEST_F(LayerCallbackTest, BufferColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferNoColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, false, false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, BufferNoColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true, false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeBufferNoColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeNoBufferColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction1, &callback, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ ExpectedResult expected;
+
+ if (i == 0) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ } else {
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ }
+
+ transaction.apply();
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == 0) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ } else {
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+ : ExpectedResult::Transaction::NOT_PRESENTED,
+ layer,
+ (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+ : ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+ ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+ ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+
+ // Normal call to set up test
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+ expected.reset();
+
+ // Test
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+
+ // Normal call to set up test
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+ expected.reset();
+
+ // Test
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::UNKNOWN);
+
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ // Normal call to set up test
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+ // Test
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ // Normal call to set up test
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expectedResult;
+ expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+ // Test
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(time);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms after the first frame
+ time += (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ expected2.addExpectedPresentTime(time);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms before the previous frame
+ time -= (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the past
+ nsecs_t time = systemTime() - (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(systemTime());
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..a48f553
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -0,0 +1,1834 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItemConsumer.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+ public ::testing::WithParamInterface<RenderPath> {
+public:
+ LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+ void setRelativeZBasicHelper(uint32_t layerType);
+ void setRelativeZGroupHelper(uint32_t layerType);
+ void setAlphaBasicHelper(uint32_t layerType);
+ void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
+ Color finalColor);
+
+protected:
+ LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("default position");
+ const Rect rect(0, 0, 32, 32);
+ auto shot = getScreenCapture();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+
+ Transaction().setPosition(layer, 5, 10).apply();
+ {
+ SCOPED_TRACE("new position");
+ const Rect rect(5, 10, 37, 42);
+ auto shot = getScreenCapture();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // GLES requires only 4 bits of subpixel precision during rasterization
+ // XXX GLES composition does not match HWC composition due to precision
+ // loss (b/69315223)
+ const float epsilon = 1.0f / 16.0f;
+ Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
+ {
+ SCOPED_TRACE("rounding down");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+
+ Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
+ {
+ SCOPED_TRACE("rounding up");
+ getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().setPosition(layer, -32, -32).apply();
+ {
+ SCOPED_TRACE("negative coordinates");
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+ }
+
+ Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
+ {
+ SCOPED_TRACE("positive coordinates");
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // partially out of bounds
+ Transaction().setPosition(layer, -30, -30).apply();
+ {
+ SCOPED_TRACE("negative coordinates");
+ getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+ }
+
+ Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
+ {
+ SCOPED_TRACE("positive coordinates");
+ getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+ mDisplayHeight),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // setPosition is applied immediately by default, with or without resize
+ // pending
+ Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
+ {
+ SCOPED_TRACE("resize pending");
+ auto shot = getScreenCapture();
+ const Rect rect(5, 10, 37, 42);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+ {
+ SCOPED_TRACE("resize applied");
+ getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().setSize(layer, 64, 64).apply();
+ {
+ SCOPED_TRACE("resize pending");
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 32, 32);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+ {
+ SCOPED_TRACE("resize applied");
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 64, 64);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
+ Transaction()
+ .setSize(layer, 64, 64)
+ .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+ .apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setPosition(layerG, 16, 16)
+ .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setFrame(layerR, Rect(0, 0, 32, 32))
+ .setFrame(layerG, Rect(16, 16, 48, 48))
+ .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
+ {
+ SCOPED_TRACE("layerG above");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+ shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
+ }
+
+ Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+ {
+ SCOPED_TRACE("layerG below");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ sp<SurfaceControl> layerB;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
+
+ // layerR = 0, layerG = layerR + 3, layerB = 2
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setPosition(layerG, 8, 8)
+ .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setPosition(layerB, 16, 16)
+ .setLayer(layerB, mLayerZBase + 2)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setFrame(layerR, Rect(0, 0, 32, 32))
+ .setFrame(layerG, Rect(8, 8, 40, 40))
+ .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setFrame(layerB, Rect(16, 16, 48, 48))
+ .setLayer(layerB, mLayerZBase + 2)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
+
+ {
+ SCOPED_TRACE("(layerR < layerG) < layerB");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+ shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
+ shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
+ }
+
+ // layerR = 4, layerG = layerR + 3, layerB = 2
+ Transaction().setLayer(layerR, mLayerZBase + 4).apply();
+ {
+ SCOPED_TRACE("layerB < (layerR < layerG)");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+ shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
+ shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
+ }
+
+ // layerR = 4, layerG = layerR - 3, layerB = 2
+ Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+ {
+ SCOPED_TRACE("layerB < (layerG < layerR)");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
+ shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
+ }
+
+ // restore to absolute z
+ // layerR = 4, layerG = 0, layerB = 2
+ Transaction().setLayer(layerG, mLayerZBase).apply();
+ {
+ SCOPED_TRACE("layerG < layerB < layerR");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
+ }
+
+ // layerR should not affect layerG anymore
+ // layerR = 1, layerG = 0, layerB = 2
+ Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+ {
+ SCOPED_TRACE("layerG < layerR < layerB");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+ shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
+ const Rect top(0, 0, 32, 16);
+ const Rect bottom(0, 16, 32, 32);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+
+ ANativeWindow_Buffer buffer;
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::RED));
+ // setTransparentRegionHint always applies to the following buffer
+ Transaction().setTransparentRegionHint(layer, Region(top)).apply();
+ ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+ {
+ SCOPED_TRACE("top transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::RED);
+ }
+
+ Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+ {
+ SCOPED_TRACE("transparent region hint pending");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+ {
+ SCOPED_TRACE("bottom transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::RED);
+ shot->expectColor(bottom, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+ const Rect top(0, 0, 32, 16);
+ const Rect bottom(0, 16, 32, 32);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::RED));
+ Transaction()
+ .setTransparentRegionHint(layer, Region(top))
+ .setBuffer(layer, buffer)
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .apply();
+ {
+ SCOPED_TRACE("top transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::RED);
+ }
+
+ Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+ {
+ SCOPED_TRACE("transparent region hint intermediate");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::BLACK);
+ }
+
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
+ Transaction().setBuffer(layer, buffer).apply();
+ {
+ SCOPED_TRACE("bottom transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::RED);
+ shot->expectColor(bottom, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
+ sp<SurfaceControl> layerTransparent;
+ sp<SurfaceControl> layerR;
+ ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+
+ // check that transparent region hint is bound by the layer size
+ Transaction()
+ .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+ .setPosition(layerR, 16, 16)
+ .setLayer(layerR, mLayerZBase + 1)
+ .apply();
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+ sp<SurfaceControl> layerTransparent;
+ sp<SurfaceControl> layerR;
+ ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(
+ layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ // check that transparent region hint is bound by the layer size
+ Transaction()
+ .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+ .setFrame(layerR, Rect(16, 16, 48, 48))
+ .setLayer(layerR, mLayerZBase + 1)
+ .apply();
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
+ sp<SurfaceControl> layer1;
+ sp<SurfaceControl> layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
+
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setAlpha(layer1, 0.25f)
+ .setAlpha(layer2, 0.75f)
+ .setPosition(layer2, 16, 0)
+ .setLayer(layer2, mLayerZBase + 1)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setAlpha(layer1, 0.25f)
+ .setAlpha(layer2, 0.75f)
+ .setFrame(layer1, Rect(0, 0, 32, 32))
+ .setFrame(layer2, Rect(16, 0, 48, 32))
+ .setLayer(layer2, mLayerZBase + 1)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
+ {
+ auto shot = getScreenCapture();
+ uint8_t r = 16; // 64 * 0.25f
+ uint8_t g = 48; // 64 * 0.75f
+ shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
+ shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
+
+ r /= 4; // r * (1.0f - 0.75f)
+ shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
+ sp<SurfaceControl> bufferLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
+
+ {
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+ const Color expected = {15, 51, 85, 255};
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+ Transaction().setColor(colorLayer, color).apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+ }
+}
+
+// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// BLUE: prior background color
+// GREEN: final background color
+// BLACK: no color or fill
+void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
+ bool bufferFill, float alpha,
+ Color finalColor) {
+ sp<SurfaceControl> layer;
+ int32_t width = 500;
+ int32_t height = 500;
+
+ Color fillColor = Color::RED;
+ Color priorBgColor = Color::BLUE;
+ Color expectedColor = Color::BLACK;
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceColor:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
+ Transaction()
+ .setCrop_legacy(layer, Rect(0, 0, width, height))
+ .setColor(layer, half3(1.0f, 0, 0))
+ .apply();
+ expectedColor = fillColor;
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+ break;
+ default:
+ GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
+ return;
+ }
+
+ if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
+ .apply();
+ if (!bufferFill) {
+ expectedColor = priorBgColor;
+ }
+ }
+
+ {
+ SCOPED_TRACE("default before setting background color layer");
+ screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
+ }
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
+ .apply();
+
+ {
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, width, height), finalColor);
+ shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
+ .apply();
+
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
+ sp<SurfaceControl> bufferLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+
+ const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+ const float alpha = 0.25f;
+ const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+ Transaction()
+ .setColor(colorLayer, color)
+ .setAlpha(colorLayer, alpha)
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+ sp<SurfaceControl> bufferLayer;
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+ const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+ const float alpha = 0.25f;
+ const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
+ // this is handwavy, but the precision loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+ Transaction()
+ .reparent(colorLayer, parentLayer->getHandle())
+ .setColor(colorLayer, color)
+ .setAlpha(parentLayer, alpha)
+ .setLayer(parentLayer, mLayerZBase + 1)
+ .apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
+ {
+ SCOPED_TRACE("IDENTITY");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
+ {
+ SCOPED_TRACE("FLIP_H");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+ Color::WHITE, Color::BLUE);
+ }
+
+ Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
+ {
+ SCOPED_TRACE("FLIP_V");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+ Color::RED, Color::GREEN);
+ }
+
+ Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
+ {
+ SCOPED_TRACE("ROT_90");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+ Color::WHITE, Color::GREEN);
+ }
+
+ Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
+ {
+ SCOPED_TRACE("SCALE");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .apply();
+ {
+ SCOPED_TRACE("IDENTITY");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
+ {
+ SCOPED_TRACE("FLIP_H");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
+ {
+ SCOPED_TRACE("FLIP_V");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
+ {
+ SCOPED_TRACE("ROT_90");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
+ {
+ SCOPED_TRACE("SCALE");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ const float rot = M_SQRT1_2; // 45 degrees
+ const float trans = M_SQRT2 * 16.0f;
+ Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
+
+ auto shot = getScreenCapture();
+ // check a 8x8 region inside each color
+ auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
+ const int32_t halfL = 4;
+ return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
+ };
+ const int32_t unit = int32_t(trans / 2);
+ shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
+ shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
+ shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
+ shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // setMatrix is applied after any pending resize, unlike setPosition
+ Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
+ {
+ SCOPED_TRACE("resize pending");
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 32, 32);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+ {
+ SCOPED_TRACE("resize applied");
+ const Rect rect(0, 0, 128, 128);
+ getScreenCapture()->expectColor(rect, Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
+ Transaction()
+ .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+ .setSize(layer, 64, 64)
+ .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+ .apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ // XXX SCALE_CROP is not respected; calling setSize and
+ // setOverrideScalingMode in separate transactions does not work
+ // (b/69315456)
+ Transaction()
+ .setSize(layer, 64, 16)
+ .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+ .apply();
+ {
+ SCOPED_TRACE("SCALE_TO_WINDOW");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ const Rect crop(8, 8, 24, 24);
+
+ Transaction().setCrop_legacy(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(crop, Color::RED);
+ shot->expectBorder(crop, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ const Rect crop(8, 8, 24, 24);
+
+ Transaction().setCrop(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("empty rect");
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+
+ {
+ SCOPED_TRACE("negative rect");
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("empty rect");
+ Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ }
+
+ {
+ SCOPED_TRACE("negative rect");
+ Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
+
+ Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
+
+ Transaction().setBuffer(layer, buffer).apply();
+
+ // Partially out of bounds in the negative (upper left) direction
+ Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
+ {
+ SCOPED_TRACE("out of bounds, negative (upper left) direction");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+
+ // Partially out of bounds in the positive (lower right) direction
+ Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
+ {
+ SCOPED_TRACE("out of bounds, positive (lower right) direction");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+
+ // Fully out of buffer space bounds
+ Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
+ {
+ SCOPED_TRACE("Fully out of bounds");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
+ shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ const Point position(32, 32);
+ const Rect crop(8, 8, 24, 24);
+ Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(crop + position, Color::RED);
+ shot->expectBorder(crop + position, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ const Rect frame(32, 32, 64, 64);
+ const Rect crop(8, 8, 24, 24);
+ Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(frame, Color::RED);
+ shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // crop_legacy is affected by matrix
+ Transaction()
+ .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+ .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+ .apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ // setCrop_legacy is applied immediately by default, with or without resize pending
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+ {
+ SCOPED_TRACE("resize pending");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
+ shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
+ {
+ SCOPED_TRACE("resize applied");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
+ shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ const Rect frame(8, 8, 24, 24);
+
+ Transaction().setFrame(layer, frame).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(frame, Color::RED);
+ shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("empty rect");
+ Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ {
+ SCOPED_TRACE("negative rect");
+ Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+
+ // A parentless layer will default to a frame with the same size as the buffer
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+ Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ // A layer will default to the frame of its parent
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
+
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ // A layer will default to the frame of its parent
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ std::this_thread::sleep_for(500ms);
+
+ Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+ Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+ Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
+ shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 1");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 2");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 3");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
+ sp<SurfaceControl> layer1;
+ ASSERT_NO_FATAL_FAILURE(
+ layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<SurfaceControl> layer2;
+ ASSERT_NO_FATAL_FAILURE(
+ layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+
+ Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
+ {
+ SCOPED_TRACE("set layer 1 buffer red");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+
+ Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
+ {
+ SCOPED_TRACE("set layer 2 buffer blue");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+ {
+ SCOPED_TRACE("set layer 1 buffer green");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+
+ {
+ SCOPED_TRACE("set layer 2 buffer white");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 10> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 70> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 65> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ if (idx == 0) {
+ buffers[0].clear();
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
+ .apply();
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+ Color::GREEN, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
+ .apply();
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+ Color::BLUE, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
+ .apply();
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+ Color::GREEN, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ Transaction transaction;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ sp<Fence> fence;
+ if (getBuffer(nullptr, &fence) != NO_ERROR) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+ status_t status = fence->wait(1000);
+ ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+ std::this_thread::sleep_for(200ms);
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ sp<Fence> fence = Fence::NO_FENCE;
+
+ Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ HdrMetadata hdrMetadata;
+ hdrMetadata.validTypes = 0;
+ Transaction().setBuffer(layer, buffer).setHdrMetadata(layer, hdrMetadata).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Region region;
+ region.set(32, 32);
+ Transaction().setBuffer(layer, buffer).setSurfaceDamageRegion(layer, region).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
+ {
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrix;
+ matrix[0][0] = 0.3;
+ matrix[1][0] = 0.59;
+ matrix[2][0] = 0.11;
+ matrix[0][1] = 0.3;
+ matrix[1][1] = 0.59;
+ matrix[2][1] = 0.11;
+ matrix[0][2] = 0.3;
+ matrix[1][2] = 0.59;
+ matrix[2][2] = 0.11;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
+ }
+
+ ColorTransformHelper::applyMatrix(expected, matrix);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
+ }
+
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction().setColor(colorLayer, color).setColorTransform(colorLayer, matrix, vec3()).apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer));
+ ASSERT_NO_FATAL_FAILURE(
+ colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
+
+ Transaction()
+ .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(parentLayer, mLayerZBase + 1)
+ .apply();
+ {
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrix;
+ matrix[0][0] = 0.3;
+ matrix[1][0] = 0.59;
+ matrix[2][0] = 0.11;
+ matrix[0][1] = 0.3;
+ matrix[1][1] = 0.59;
+ matrix[2][1] = 0.11;
+ matrix[0][2] = 0.3;
+ matrix[1][2] = 0.59;
+ matrix[2][2] = 0.11;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
+ }
+
+ ColorTransformHelper::applyMatrix(expected, matrix);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
+ }
+
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction()
+ .setColor(colorLayer, color)
+ .setColorTransform(parentLayer, matrix, vec3())
+ .apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer));
+ ASSERT_NO_FATAL_FAILURE(
+ colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
+
+ Transaction()
+ .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(parentLayer, mLayerZBase + 1)
+ .apply();
+ {
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrixChild;
+ matrixChild[0][0] = 0.3;
+ matrixChild[1][0] = 0.59;
+ matrixChild[2][0] = 0.11;
+ matrixChild[0][1] = 0.3;
+ matrixChild[1][1] = 0.59;
+ matrixChild[2][1] = 0.11;
+ matrixChild[0][2] = 0.3;
+ matrixChild[1][2] = 0.59;
+ matrixChild[2][2] = 0.11;
+ mat3 matrixParent;
+ matrixParent[0][0] = 0.2;
+ matrixParent[1][0] = 0.4;
+ matrixParent[2][0] = 0.10;
+ matrixParent[0][1] = 0.2;
+ matrixParent[1][1] = 0.4;
+ matrixParent[2][1] = 0.10;
+ matrixParent[0][2] = 0.2;
+ matrixParent[1][2] = 0.4;
+ matrixParent[2][2] = 0.10;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
+ }
+
+ ColorTransformHelper::applyMatrix(expected, matrixChild);
+ ColorTransformHelper::applyMatrix(expected, matrixParent);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
+ }
+
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction()
+ .setColor(colorLayer, color)
+ .setColorTransform(parentLayer, matrixParent, vec3())
+ .setColorTransform(colorLayer, matrixChild, vec3())
+ .apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ }
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
new file mode 100644
index 0000000..7edddb6
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2019 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_LAYER_TRANSACTION_TEST_H
+#define ANDROID_LAYER_TRANSACTION_TEST_H
+
+#include <gtest/gtest.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/gui/ComposerService.h>
+
+#include <ui/DisplayInfo.h>
+
+#include "BufferGenerator.h"
+#include "utils/ScreenshotUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTransactionTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
+
+ ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+ }
+
+ virtual void TearDown() {
+ mBlackBgSurface = 0;
+ mClient->dispose();
+ mClient = 0;
+ }
+
+ virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+ const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ auto layer =
+ createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
+
+ Transaction t;
+ t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+
+ status_t error = t.apply();
+ if (error != NO_ERROR) {
+ ADD_FAILURE() << "failed to initialize SurfaceControl";
+ layer.clear();
+ }
+
+ return layer;
+ }
+
+ virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+ const char* name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t flags,
+ SurfaceControl* parent = nullptr) {
+ auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+ EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+ return layer;
+ }
+
+ virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ return createLayer(mClient, name, width, height, flags, parent);
+ }
+
+ sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
+ SurfaceControl* parent = nullptr) {
+ auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, parent);
+ asTransaction([&](Transaction& t) {
+ t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
+ t.setAlpha(colorLayer, color.a / 255.0f);
+ });
+ return colorLayer;
+ }
+
+ ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
+ // wait for previous transactions (such as setSize) to complete
+ Transaction().apply(true);
+
+ ANativeWindow_Buffer buffer = {};
+ EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+
+ return buffer;
+ }
+
+ void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
+ ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+
+ // wait for the newly posted buffer to be latched
+ waitForLayerBuffers();
+ }
+
+ virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
+ ANativeWindow_Buffer buffer;
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ TransactionUtils::fillANativeWindowBufferColor(buffer,
+ Rect(0, 0, bufferWidth, bufferHeight),
+ color);
+ postBufferQueueLayerBuffer(layer);
+ }
+
+ virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
+ color);
+ Transaction().setBuffer(layer, buffer).apply();
+ }
+
+ void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
+ switch (mLayerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+ break;
+ default:
+ ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+ }
+ }
+
+ void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
+ int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
+ const Color& topRight, const Color& bottomLeft,
+ const Color& bottomRight) {
+ switch (mLayerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+ bottomLeft, bottomRight);
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+ bottomLeft, bottomRight);
+ break;
+ default:
+ ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+ }
+ }
+
+ virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft,
+ const Color& topRight, const Color& bottomLeft,
+ const Color& bottomRight) {
+ ANativeWindow_Buffer buffer;
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+ const int32_t halfW = bufferWidth / 2;
+ const int32_t halfH = bufferHeight / 2;
+ TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+ TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+ topRight);
+ TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+ bottomLeft);
+ TransactionUtils::fillANativeWindowBufferColor(buffer,
+ Rect(halfW, halfH, bufferWidth,
+ bufferHeight),
+ bottomRight);
+
+ postBufferQueueLayerBuffer(layer);
+ }
+
+ virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft,
+ const Color& topRight, const Color& bottomLeft,
+ const Color& bottomRight) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+ const int32_t halfW = bufferWidth / 2;
+ const int32_t halfH = bufferHeight / 2;
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+ topRight);
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+ bottomLeft);
+ TransactionUtils::fillGraphicBufferColor(buffer,
+ Rect(halfW, halfH, bufferWidth, bufferHeight),
+ bottomRight);
+
+ Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+ }
+
+ std::unique_ptr<ScreenCapture> screenshot() {
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ return screenshot;
+ }
+
+ void asTransaction(const std::function<void(Transaction&)>& exec) {
+ Transaction t;
+ exec(t);
+ t.apply(true);
+ }
+
+ static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.get(outBuffer, outFence);
+ }
+
+ sp<SurfaceComposerClient> mClient;
+
+ sp<IBinder> mDisplay;
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
+ uint32_t mDisplayLayerStack;
+ Rect mDisplayRect = Rect::INVALID_RECT;
+
+ // leave room for ~256 layers
+ const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
+
+ sp<SurfaceControl> mBlackBgSurface;
+ bool mColorManagementUsed;
+
+private:
+ void SetUpDisplay() {
+ mDisplay = mClient->getInternalDisplayToken();
+ ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
+
+ // get display width/height
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
+ mDisplayWidth = info.w;
+ mDisplayHeight = info.h;
+ mDisplayRect =
+ Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
+
+ // After a new buffer is queued, SurfaceFlinger is notified and will
+ // latch the new buffer on next vsync. Let's heuristically wait for 3
+ // vsyncs.
+ mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+
+ mDisplayLayerStack = 0;
+
+ mBlackBgSurface =
+ createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+
+ // set layer stack (b/68888219)
+ Transaction t;
+ t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+ t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+ t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
+ t.setColor(mBlackBgSurface, half3{0, 0, 0});
+ t.setLayer(mBlackBgSurface, mLayerZBase);
+ t.apply();
+ }
+
+ void waitForLayerBuffers() {
+ // Request an empty transaction to get applied synchronously to ensure the buffer is
+ // latched.
+ Transaction().apply(true);
+ usleep(mBufferPostDelay);
+ }
+
+ int32_t mBufferPostDelay;
+
+ friend class LayerRenderPathTestHarness;
+};
+} // namespace android
+
+#endif
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
new file mode 100644
index 0000000..35c51e1
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <private/android_filesystem_config.h>
+#include <thread>
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<GraphicBuffer> outBuffer;
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply(true);
+ ASSERT_EQ(PERMISSION_DENIED,
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // By default the system can capture screenshots with secure layers but they
+ // will be blacked out
+ ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ // 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.
+ bool outCapturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
+ 0, false, ISurfaceComposer::eRotateNone, true));
+ ASSERT_EQ(true, outCapturedSecureLayers);
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction().setTransformToDisplayInverse(layer, false).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+
+ Transaction().setTransformToDisplayInverse(layer, true).apply();
+}
+
+TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ // verify this doesn't cause a crash
+ Transaction().setSidebandStream(layer, nullptr).apply();
+}
+
+TEST_F(LayerTransactionTest, ReparentToSelf) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().reparent(layer, layer->getHandle()).apply();
+
+ {
+ // We expect the transaction to be silently dropped, but for SurfaceFlinger
+ // to still be functioning.
+ SCOPED_TRACE("after reparent to self");
+ const Rect rect(0, 0, 32, 32);
+ auto shot = screenshot();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+}
+
+// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
+// the dropped buffer's damage region into the next buffer's damage region. If
+// we don't do this, we'll report an incorrect damage region to hardware
+// composer, resulting in broken rendering. This test checks the BufferQueue
+// case.
+//
+// Unfortunately, we don't currently have a way to inspect the damage region
+// SurfaceFlinger sends to hardware composer from a test, so this test requires
+// the dev to manually watch the device's screen during the test to spot broken
+// rendering. Because the results can't be automatically verified, this test is
+// marked disabled.
+TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
+ const int width = mDisplayWidth;
+ const int height = mDisplayHeight;
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+ const auto producer = layer->getIGraphicBufferProducer();
+ const sp<IProducerListener> dummyListener(new DummyProducerListener);
+ IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
+ ASSERT_EQ(OK,
+ producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+
+ std::map<int, sp<GraphicBuffer>> slotMap;
+ auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
+ ASSERT_NE(nullptr, buf);
+ const auto iter = slotMap.find(slot);
+ ASSERT_NE(slotMap.end(), iter);
+ *buf = iter->second;
+ };
+
+ auto dequeue = [&](int* outSlot) {
+ ASSERT_NE(nullptr, outSlot);
+ *outSlot = -1;
+ int slot;
+ sp<Fence> fence;
+ uint64_t age;
+ FrameEventHistoryDelta timestamps;
+ const status_t dequeueResult =
+ producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ &age, ×tamps);
+ if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ sp<GraphicBuffer> newBuf;
+ ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
+ ASSERT_NE(nullptr, newBuf.get());
+ slotMap[slot] = newBuf;
+ } else {
+ ASSERT_EQ(OK, dequeueResult);
+ }
+ *outSlot = slot;
+ };
+
+ auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
+ IGraphicBufferProducer::QueueBufferInput input(
+ /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
+ /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ /*transform=*/0, Fence::NO_FENCE);
+ input.setSurfaceDamage(damage);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
+ };
+
+ auto fillAndPostBuffers = [&](const Color& color) {
+ int slot1;
+ ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
+ int slot2;
+ ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
+
+ sp<GraphicBuffer> buf1;
+ ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
+ sp<GraphicBuffer> buf2;
+ ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
+ TransactionUtils::fillGraphicBufferColor(buf1, Rect(width, height), color);
+ TransactionUtils::fillGraphicBufferColor(buf2, Rect(width, height), color);
+
+ const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
+ ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
+ ASSERT_NO_FATAL_FAILURE(
+ queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
+ displayTime));
+ };
+
+ const auto startTime = systemTime();
+ const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
+ int colorIndex = 0;
+ while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
+ ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
+ std::this_thread::sleep_for(1s);
+ }
+
+ ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..daeff17
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTypeAndRenderTypeTransactionTest
+ : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+ LayerTypeAndRenderTypeTransactionTest()
+ : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+ mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ return mRenderPathHarness.getScreenCapture();
+ }
+
+protected:
+ LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+INSTANTIATE_TEST_CASE_P(
+ LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+ ::testing::Combine(
+ ::testing::Values(
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
+ // cannot test robustness against invalid sizes (zero or really huge)
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+ Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+ {
+ SCOPED_TRACE("layerR");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+
+ Transaction().setLayer(layerG, mLayerZBase + 2).apply();
+ {
+ SCOPED_TRACE("layerG");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+ Transaction()
+ .setPosition(layerG, 16, 16)
+ .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .apply();
+
+ layerG.clear();
+ // layerG should have been removed
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
+ {
+ SCOPED_TRACE("layer hidden");
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+ }
+
+ Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
+ {
+ SCOPED_TRACE("layer shown");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
+ const Color translucentRed = {100, 0, 0, 100};
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+ Transaction()
+ .setLayer(layerR, mLayerZBase + 1)
+ .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+ .apply();
+ {
+ SCOPED_TRACE("layerR opaque");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+ }
+
+ Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
+ {
+ SCOPED_TRACE("layerR translucent");
+ const uint8_t g = uint8_t(255 - translucentRed.a);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+ Transaction()
+ .reparent(layerR, parent->getHandle())
+ .reparent(layerG, parent->getHandle())
+ .apply();
+ Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
+ {
+ SCOPED_TRACE("layerR");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+
+ Transaction().setLayer(layerR, -3).apply();
+ {
+ SCOPED_TRACE("layerG");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
+ const Color color = {64, 0, 0, 255};
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
+
+ Transaction().setAlpha(layer, 2.0f).apply();
+ {
+ SCOPED_TRACE("clamped to 1.0f");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
+ }
+
+ Transaction().setAlpha(layer, -1.0f).apply();
+ {
+ SCOPED_TRACE("clamped to 0.0f");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setCornerRadius(layer, cornerRadius)
+ .setCrop_legacy(layer, Rect(0, 0, size, size))
+ .apply();
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Transparent corners
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .reparent(child, parent->getHandle())
+ .setPosition(child, 0, size / 2)
+ .apply();
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Top edge of child should not have rounded corners because it's translated in the parent
+ shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
+ Color::GREEN);
+ // But bottom edges should have been clipped according to parent bounds
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+ shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ }
+}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
+ sp<SurfaceControl> bufferLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
+
+ // color is ignored
+ Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+ {
+ SCOPED_TRACE("non-existing layer stack");
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+ }
+
+ Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
+ {
+ SCOPED_TRACE("original layer stack");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
new file mode 100644
index 0000000..42ec34a
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<uint32_t> {
+public:
+ LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+INSTANTIATE_TEST_CASE_P(
+ LayerTypeTransactionTests, LayerTypeTransactionTest,
+ ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
+
+TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ sp<SurfaceControl> layerR;
+ sp<SurfaceControl> layerG;
+ sp<SurfaceControl> layerB;
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
+
+ Transaction().reparent(layerB, parent->getHandle()).apply();
+
+ // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
+ Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
+
+ std::unique_ptr<ScreenCapture> screenshot;
+ // only layerB is in this range
+ sp<IBinder> parentHandle = parent->getHandle();
+ ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+ screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+}
+
+TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ parent.get()));
+ Transaction()
+ .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+ .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+ .show(childLayer)
+ .show(parent)
+ .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+ .apply();
+
+ Transaction()
+ .setRelativeLayer(childLayer, parent->getHandle(), -1)
+ .setLayer(childLayer, 1)
+ .apply();
+
+ {
+ SCOPED_TRACE("setLayer above");
+ // Set layer should get applied and place the child above.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+ }
+
+ Transaction()
+ .setLayer(childLayer, 1)
+ .setRelativeLayer(childLayer, parent->getHandle(), -1)
+ .apply();
+
+ {
+ SCOPED_TRACE("setRelative below");
+ // Set relative layer should get applied and place the child below.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor);
+ sp<SurfaceControl> relativeParent =
+ LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ parent.get()));
+ Transaction()
+ .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+ .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+ .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
+ .show(childLayer)
+ .show(parent)
+ .show(relativeParent)
+ .setLayer(parent, mLayerZBase - 1)
+ .setLayer(relativeParent, mLayerZBase)
+ .apply();
+
+ Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+
+ {
+ SCOPED_TRACE("setLayer above");
+ // Set layer should get applied and place the child above.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+ }
+
+ Transaction().hide(relativeParent).apply();
+
+ {
+ SCOPED_TRACE("hide relative parent");
+ // The relative should no longer be visible.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<GraphicBuffer> outBuffer;
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply(true);
+ ASSERT_EQ(PERMISSION_DENIED,
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
+ ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+}
+TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+
+ sp<IBinder> handle = layer->getHandle();
+ ASSERT_TRUE(handle != nullptr);
+
+ FrameStats frameStats;
+ mClient->getLayerFrameStats(handle, &frameStats);
+
+ ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
new file mode 100644
index 0000000..73f563d
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -0,0 +1,1655 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerUpdateTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, displayHeight, 0);
+ 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);
+
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+ // Synchronization surface
+ mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
+ ASSERT_TRUE(mSyncSurfaceControl != nullptr);
+ ASSERT_TRUE(mSyncSurfaceControl->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+
+ t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+ t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .show(mFGSurfaceControl);
+
+ t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
+ .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
+ .show(mSyncSurfaceControl);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ mSyncSurfaceControl = 0;
+ }
+
+ void waitForPostedBuffers() {
+ // Since the sync surface is in synchronous mode (i.e. double buffered)
+ // posting three buffers to it should ensure that at least two
+ // SurfaceFlinger::handlePageFlip calls have been made, which should
+ // guaranteed that a buffer posted to another Surface has been retired.
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ }
+
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mFGSurfaceControl;
+
+ // This surface is used to ensure that the buffers posted to
+ // mFGSurfaceControl have been picked up by SurfaceFlinger.
+ sp<SurfaceControl> mSyncSurfaceControl;
+};
+
+TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
+ TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
+ waitForPostedBuffers();
+
+ Transaction{}
+ .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
+ .setPosition(relative, 64, 64)
+ .apply();
+
+ {
+ // The relative should be on top of the FG control.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(64, 64, 10, 10, 10);
+ }
+ Transaction{}.detachChildren(mFGSurfaceControl).apply();
+
+ {
+ // Nothing should change at this point.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(64, 64, 10, 10, 10);
+ }
+
+ Transaction{}.hide(relative).apply();
+
+ {
+ // Ensure that the relative was actually hidden, rather than
+ // being left in the detached but visible state.
+ ScreenCapture::captureScreen(&sc);
+ sc->expectFGColor(64, 64);
+ }
+}
+
+class GeometryLatchingTest : public LayerUpdateTest {
+protected:
+ void EXPECT_INITIAL_STATE(const char* trace) {
+ SCOPED_TRACE(trace);
+ ScreenCapture::captureScreen(&sc);
+ // We find the leading edge of the FG surface.
+ sc->expectFGColor(127, 127);
+ sc->expectBGColor(128, 128);
+ }
+
+ void lockAndFillFGBuffer() {
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false);
+ }
+
+ void unlockFGBuffer() {
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ waitForPostedBuffers();
+ }
+
+ void completeFGResize() {
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+ }
+ void restoreInitialState() {
+ asTransaction([&](Transaction& t) {
+ t.setSize(mFGSurfaceControl, 64, 64);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+ });
+
+ EXPECT_INITIAL_STATE("After restoring initial state");
+ }
+ std::unique_ptr<ScreenCapture> sc;
+};
+
+class CropLatchingTest : public GeometryLatchingTest {
+protected:
+ void EXPECT_CROPPED_STATE(const char* trace) {
+ SCOPED_TRACE(trace);
+ ScreenCapture::captureScreen(&sc);
+ // The edge should be moved back one pixel by our crop.
+ sc->expectFGColor(126, 126);
+ sc->expectBGColor(127, 127);
+ sc->expectBGColor(128, 128);
+ }
+
+ void EXPECT_RESIZE_STATE(const char* trace) {
+ SCOPED_TRACE(trace);
+ ScreenCapture::captureScreen(&sc);
+ // The FG is now resized too 128,128 at 64,64
+ sc->expectFGColor(64, 64);
+ sc->expectFGColor(191, 191);
+ sc->expectBGColor(192, 192);
+ }
+};
+
+TEST_F(LayerUpdateTest, DeferredTransactionTest) {
+ std::unique_ptr<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before anything");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(32, 32);
+ sc->expectFGColor(96, 96);
+ sc->expectBGColor(160, 160);
+ }
+
+ // set up two deferred transactions on different frames
+ asTransaction([&](Transaction& t) {
+ t.setAlpha(mFGSurfaceControl, 0.75);
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+ });
+
+ asTransaction([&](Transaction& t) {
+ t.setPosition(mFGSurfaceControl, 128, 128);
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ });
+
+ {
+ SCOPED_TRACE("before any trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(32, 32);
+ sc->expectFGColor(96, 96);
+ sc->expectBGColor(160, 160);
+ }
+
+ // should trigger the first deferred transaction, but not the second one
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ {
+ SCOPED_TRACE("after first trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(32, 32);
+ sc->checkPixel(96, 96, 162, 63, 96);
+ sc->expectBGColor(160, 160);
+ }
+
+ // should show up immediately since it's not deferred
+ asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
+
+ // trigger the second deferred transaction
+ TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ {
+ SCOPED_TRACE("after second trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(32, 32);
+ sc->expectBGColor(96, 96);
+ sc->expectFGColor(160, 160);
+ }
+}
+
+TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ sp<SurfaceControl> childNoBuffer =
+ createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+ PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+ TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
+ SurfaceComposerClient::Transaction{}
+ .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+ .show(childNoBuffer)
+ .show(childBuffer)
+ .apply(true);
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectChildColor(73, 73);
+ sc->expectFGColor(74, 74);
+ }
+ SurfaceComposerClient::Transaction{}
+ .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+ .apply(true);
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectChildColor(73, 73);
+ sc->expectChildColor(74, 74);
+ }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactions) {
+ std::unique_ptr<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before move");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(0, 12);
+ sc->expectFGColor(75, 75);
+ sc->expectBGColor(145, 145);
+ }
+
+ Transaction t1, t2;
+ t1.setPosition(mFGSurfaceControl, 128, 128);
+ t2.setPosition(mFGSurfaceControl, 0, 0);
+ // We expect that the position update from t2 now
+ // overwrites the position update from t1.
+ t1.merge(std::move(t2));
+ t1.apply();
+
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectFGColor(1, 1);
+ }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactionFlags) {
+ Transaction().hide(mFGSurfaceControl).apply();
+ std::unique_ptr<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before merge");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(0, 12);
+ sc->expectBGColor(75, 75);
+ sc->expectBGColor(145, 145);
+ }
+
+ Transaction t1, t2;
+ t1.show(mFGSurfaceControl);
+ t2.setFlags(mFGSurfaceControl, 0 /* flags */, layer_state_t::eLayerSecure /* mask */);
+ t1.merge(std::move(t2));
+ t1.apply();
+
+ {
+ SCOPED_TRACE("after merge");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectFGColor(75, 75);
+ }
+}
+
+class ChildLayerTest : public LayerUpdateTest {
+protected:
+ void SetUp() override {
+ LayerUpdateTest::SetUp();
+ mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ {
+ SCOPED_TRACE("before anything");
+ mCapture = screenshot();
+ mCapture->expectChildColor(64, 64);
+ }
+ }
+ void TearDown() override {
+ LayerUpdateTest::TearDown();
+ mChild = 0;
+ }
+
+ sp<SurfaceControl> mChild;
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ChildLayerTest, ChildLayerPositioning) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground should now be at 0, 0
+ mCapture->expectFGColor(0, 0);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(10, 10);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(20, 20);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildLayerCropping) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+ });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(4, 4);
+ mCapture->expectBGColor(5, 5);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildLayerConstraints) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setPosition(mChild, 63, 63);
+ });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(0, 0);
+ // Last pixel in foreground should now be the child.
+ mCapture->expectChildColor(63, 63);
+ // But the child should be constrained and the next pixel
+ // must be the background
+ mCapture->expectBGColor(64, 64);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildLayerScaling) {
+ asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+ // Find the boundary between the parent and child
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(9, 9);
+ mCapture->expectFGColor(10, 10);
+ }
+
+ asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
+
+ // The boundary should be twice as far from the origin now.
+ // The pixels from the last test should all be child now
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(9, 9);
+ mCapture->expectChildColor(10, 10);
+ mCapture->expectChildColor(19, 19);
+ mCapture->expectFGColor(20, 20);
+ }
+}
+
+// A child with a scale transform should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
+ asTransaction([&](Transaction& t) {
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setPosition(mChild, 0, 0);
+ });
+
+ // Find the boundary between the parent and child.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+ mCapture->expectFGColor(10, 10);
+ }
+
+ asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
+
+ // The child should fill its parent bounds and be cropped by it.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildLayerAlpha) {
+ TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
+ TransactionUtils::fillSurfaceRGBA8(mChild, 0, 254, 0);
+ waitForPostedBuffers();
+
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ });
+
+ {
+ mCapture = screenshot();
+ // Unblended child color
+ mCapture->checkPixel(0, 0, 0, 254, 0);
+ }
+
+ asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
+
+ {
+ mCapture = screenshot();
+ // Child and BG blended.
+ mCapture->checkPixel(0, 0, 127, 127, 0);
+ }
+
+ asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
+
+ {
+ mCapture = screenshot();
+ // Child and BG blended.
+ mCapture->checkPixel(0, 0, 95, 64, 95);
+ }
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
+ });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ // In reparenting we should have exposed the entire foreground surface.
+ mCapture->expectFGColor(74, 74);
+ // And the child layer should now begin at 10, 10 (since the BG
+ // layer is at (0, 0)).
+ mCapture->expectBGColor(9, 9);
+ mCapture->expectChildColor(10, 10);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
+ sp<SurfaceControl> mGrandChild =
+ createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+ {
+ SCOPED_TRACE("Grandchild visible");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 111, 111, 111);
+ }
+
+ mChild.clear();
+
+ {
+ SCOPED_TRACE("After destroying child");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->expectFGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); });
+
+ {
+ SCOPED_TRACE("After reparenting grandchild");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 111, 111, 111);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) {
+ sp<SurfaceControl> mGrandChild =
+ createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+ // draw grand child behind the foreground surface
+ asTransaction([&](Transaction& t) {
+ t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
+ });
+
+ {
+ SCOPED_TRACE("Child visible");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 200, 200, 200);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.reparent(mChild, nullptr);
+ t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+ });
+
+ {
+ SCOPED_TRACE("foreground visible reparenting grandchild");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 195, 63, 63);
+ }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+ asTransaction([&](Transaction& t) { t.hide(mChild); });
+
+ // Since the child has the same client as the parent, it will not get
+ // detached and will be hidden.
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectFGColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+ sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> mChildNewClient =
+ createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(mChildNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
+
+ asTransaction([&](Transaction& t) {
+ t.hide(mChild);
+ t.show(mChildNewClient);
+ t.setPosition(mChildNewClient, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+ asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectChildColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(childNewClient != nullptr);
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+ Transaction()
+ .hide(mChild)
+ .show(childNewClient)
+ .setPosition(childNewClient, 10, 10)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ Transaction().detachChildren(mFGSurfaceControl).apply();
+ Transaction().hide(childNewClient).apply();
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectChildColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+
+ sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
+ fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
+ 32);
+ Transaction()
+ .setLayer(newParentSurface, INT32_MAX - 1)
+ .show(newParentSurface)
+ .setPosition(newParentSurface, 20, 20)
+ .reparent(childNewClient, newParentSurface->getHandle())
+ .apply();
+ {
+ mCapture = screenshot();
+ // Child is now hidden.
+ mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
+ }
+}
+TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(childNewClient != nullptr);
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+ Transaction()
+ .hide(mChild)
+ .show(childNewClient)
+ .setPosition(childNewClient, 10, 10)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ Rect rect = Rect(74, 74, 84, 84);
+ mCapture->expectBorder(rect, Color{195, 63, 63, 255});
+ mCapture->expectColor(rect, Color{200, 200, 200, 255});
+ }
+
+ Transaction()
+ .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber())
+ .apply();
+ Transaction().detachChildren(mFGSurfaceControl).apply();
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
+
+ // BufferLayer can still dequeue buffers even though there's a detached layer with a
+ // deferred transaction.
+ {
+ SCOPED_TRACE("new buffer");
+ mCapture = screenshot();
+ Rect rect = Rect(74, 74, 84, 84);
+ mCapture->expectBorder(rect, Color::RED);
+ mCapture->expectColor(rect, Color{200, 200, 200, 255});
+ }
+}
+
+TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ });
+
+ {
+ mCapture = screenshot();
+ // We've positioned the child in the top left.
+ mCapture->expectChildColor(0, 0);
+ // But it's only 10x15.
+ mCapture->expectFGColor(10, 15);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // We cause scaling by 2.
+ t.setSize(mFGSurfaceControl, 128, 128);
+ });
+
+ {
+ mCapture = screenshot();
+ // We've positioned the child in the top left.
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(10, 10);
+ mCapture->expectChildColor(19, 29);
+ // And now it should be scaled all the way to 20x30
+ mCapture->expectFGColor(20, 30);
+ }
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ });
+
+ {
+ mCapture = screenshot();
+ // We've positioned the child in the top left.
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 14);
+ // But it's only 10x15.
+ mCapture->expectFGColor(10, 15);
+ }
+ // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+ // the WM specified state size.
+ asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+
+ {
+ // The child should still be in the same place and not have any strange scaling as in
+ // b/37673612.
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectFGColor(10, 10);
+ }
+}
+
+// A child with a buffer transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setSize(mChild, 100, 100);
+ });
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ {
+ mCapture = screenshot();
+
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ // Apply a 90 transform on the buffer.
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+
+ // The child should be cropped by the new parent bounds.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(99, 63);
+ mCapture->expectFGColor(100, 63);
+ mCapture->expectBGColor(128, 64);
+ }
+}
+
+// A child with a scale transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setSize(mChild, 200, 200);
+ });
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ {
+ mCapture = screenshot();
+
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // Set a scaling by 2.
+ t.setSize(mFGSurfaceControl, 128, 128);
+ });
+
+ // Child should inherit its parents scale but should be cropped by its parent bounds.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(127, 127);
+ mCapture->expectBGColor(128, 128);
+ }
+}
+
+// Regression test for b/127368943
+// Child should ignore the buffer transform but apply parent scale transform.
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 14);
+ mCapture->expectFGColor(10, 15);
+ }
+
+ // Change the size of the foreground to 128 * 64 so we can test rotation as well.
+ asTransaction([&](Transaction& t) {
+ t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ t.setSize(mFGSurfaceControl, 128, 64);
+ });
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
+ // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 32, 64);
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+
+ // The child should ignore the buffer transform but apply the 2.0 scale from parent.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(19, 29);
+ mCapture->expectFGColor(20, 30);
+ }
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+ // Destroy the child layer
+ mChild.clear();
+
+ // Now recreate it as hidden
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
+
+ // Show the child layer in a deferred transaction
+ asTransaction([&](Transaction& t) {
+ t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber());
+ t.show(mChild);
+ });
+
+ // Render the foreground surface a few times
+ //
+ // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+ // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+ // never acquire/release the first buffer
+ ALOGI("Filling 1");
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+ ALOGI("Filling 2");
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
+ ALOGI("Filling 3");
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
+ ALOGI("Filling 4");
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+}
+
+TEST_F(ChildLayerTest, Reparent) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ // In reparenting we should have exposed the entire foreground surface.
+ mCapture->expectFGColor(74, 74);
+ // And the child layer should now begin at 10, 10 (since the BG
+ // layer is at (0, 0)).
+ mCapture->expectBGColor(9, 9);
+ mCapture->expectChildColor(10, 10);
+ }
+}
+
+TEST_F(ChildLayerTest, ReparentToNoParent) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 10, 10);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+ asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
+ {
+ mCapture = screenshot();
+ // The surface should now be offscreen.
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectFGColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, ReparentFromNoParent) {
+ sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
+ ASSERT_TRUE(newSurface != nullptr);
+ ASSERT_TRUE(newSurface->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(newSurface, 63, 195, 63);
+ asTransaction([&](Transaction& t) {
+ t.hide(mChild);
+ t.show(newSurface);
+ t.setPosition(newSurface, 10, 10);
+ t.setLayer(newSurface, INT32_MAX - 2);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ });
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // At 10, 10 we should see the new surface
+ mCapture->checkPixel(10, 10, 63, 195, 63);
+ }
+
+ asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+
+ {
+ mCapture = screenshot();
+ // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
+ // mFGSurface, putting it at 74, 74.
+ mCapture->expectFGColor(64, 64);
+ mCapture->checkPixel(74, 74, 63, 195, 63);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, NestedChildren) {
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ {
+ mCapture = screenshot();
+ // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
+ // which begins at 64, 64
+ mCapture->checkPixel(64, 64, 50, 50, 50);
+ }
+}
+
+TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
+ TransactionUtils::fillSurfaceRGBA8(relative, 255, 255, 255);
+
+ Transaction t;
+ t.setLayer(relative, INT32_MAX)
+ .setRelativeLayer(mChild, relative->getHandle(), 1)
+ .setPosition(mFGSurfaceControl, 0, 0)
+ .apply(true);
+
+ // We expect that the child should have been elevated above our
+ // INT_MAX layer even though it's not a child of it.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+ mCapture->checkPixel(10, 10, 255, 255, 255);
+ }
+}
+
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+ sp<SurfaceControl> bufferLayer =
+ createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ ASSERT_TRUE(bufferLayer->isValid());
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+ asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+ // Buffer layer should not extend past buffer bounds
+ mCapture->expectFGColor(95, 95);
+ }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+ sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(cropLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(cropLayer);
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // 5 pixels from the foreground we should see the child surface
+ mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+ // 10 pixels from the foreground we should be back to the foreground surface
+ mCapture->expectFGColor(74, 74);
+ }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setPosition(colorLayer, 320, 320);
+ t.setMatrix(colorLayer, 2, 0, 0, 2);
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+ sp<SurfaceControl> boundlessLayerRightShift =
+ createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(boundlessLayerRightShift->isValid());
+ sp<SurfaceControl> boundlessLayerDownShift =
+ createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, boundlessLayerRightShift.get());
+ ASSERT_TRUE(boundlessLayerDownShift->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setPosition(boundlessLayerRightShift, 32, 0);
+ t.show(boundlessLayerRightShift);
+ t.setPosition(boundlessLayerDownShift, 0, 32);
+ t.show(boundlessLayerDownShift);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+ sp<SurfaceControl> boundlessLayer =
+ mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(boundlessLayer != nullptr);
+ ASSERT_TRUE(boundlessLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
+ ASSERT_TRUE(colorLayer != nullptr);
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ // shift child layer off bounds. If this layer was not boundless, we will
+ // expect the child layer to be cropped.
+ t.setPosition(boundlessLayer, 32, 32);
+ t.show(boundlessLayer);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ // undo shift by parent
+ t.setPosition(colorLayer, -32, -32);
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+ sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+ PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+ ASSERT_TRUE(rootBoundlessLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
+
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+ t.setPosition(rootBoundlessLayer, 32, 32);
+ t.show(rootBoundlessLayer);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ t.hide(mFGSurfaceControl);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectBGColor(31, 31);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(97, 97);
+ }
+}
+
+class ScreenCaptureTest : public LayerUpdateTest {
+protected:
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+ auto bgHandle = mBGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, bgHandle);
+ mCapture->expectBGColor(0, 0);
+ // Doesn't capture FG layer which is at 64, 64
+ mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl layer and its child.
+ ScreenCapture::captureLayers(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl's child
+ ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+ sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .show(child3)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ auto childHandle = child->getHandle();
+
+ // Captures child
+ ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
+ mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+ // Area outside of child's bounds is transparent.
+ mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer above fg layer so should be shown above when computing all layers.
+ .setRelativeLayer(relative, fgHandle, 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+ ScreenCapture::captureLayers(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer below fg layer but relative to child layer so it should be shown
+ // above child layer.
+ .setLayer(relative, -1)
+ .setRelativeLayer(relative, child->getHandle(), 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+ // relative value should be taken into account, placing it above child layer.
+ ScreenCapture::captureLayers(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ // Relative layer is showing on top of child layer
+ mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
+public:
+ void SetUp() override {
+ LayerUpdateTest::SetUp();
+
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(mChild).apply(true);
+ }
+
+ void verify(std::function<void()> verifyStartingState) {
+ // Verify starting state before a screenshot is taken.
+ verifyStartingState();
+
+ // Verify child layer does not inherit any of the properties of its
+ // parent when its screenshot is captured.
+ auto fgHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+
+ // Verify all assumptions are still true after the screenshot is taken.
+ verifyStartingState();
+ }
+
+ std::unique_ptr<ScreenCapture> mCapture;
+ sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+ SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+ // Even though the parent is hidden we should still capture the child.
+
+ // Before and after reparenting, verify child is properly hidden
+ // when rendering full-screen.
+ verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+ SurfaceComposerClient::Transaction()
+ .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+ .apply(true);
+
+ // Even though the parent is cropped out we should still capture the child.
+
+ // Before and after reparenting, verify child is cropped by parent.
+ verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+ SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+ // We should not inherit the parent scaling.
+
+ // Before and after reparenting, verify child is properly scaled.
+ verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures mFGSurfaceControl, its child, and the grandchild.
+ ScreenCapture::captureLayers(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+ mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+ // Captures only the child layer, and not the parent.
+ ScreenCapture::captureLayers(&mCapture, childHandle);
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ auto grandchildHandle = grandchild->getHandle();
+
+ // Captures only the grandchild.
+ ScreenCapture::captureLayers(&mCapture, grandchildHandle);
+ mCapture->checkPixel(0, 0, 50, 50, 50);
+ mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ auto redLayerHandle = redLayer->getHandle();
+
+ // Capturing full screen should have both red and blue are visible.
+ ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ const Rect crop = Rect(0, 0, 30, 30);
+ ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
+ // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
+ // area visible.
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ auto redLayerHandle = redLayer->getHandle();
+
+ // Capturing full screen should have both red and blue are visible.
+ ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
+ // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+ mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+ auto redLayerHandle = redLayer->getHandle();
+ redLayer.clear();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+
+ // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
new file mode 100644
index 0000000..0bcac1a
--- /dev/null
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class MirrorLayerTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ mParentLayer = createColorLayer("Parent layer", Color::RED);
+ mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+ t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+ t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+ t.setPosition(mChildLayer, 50, 50);
+ t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+ t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mParentLayer = 0;
+ mChildLayer = 0;
+ }
+
+ sp<SurfaceControl> mParentLayer;
+ sp<SurfaceControl> mChildLayer;
+};
+
+TEST_F(MirrorLayerTest, MirrorColorLayer) {
+ sp<SurfaceControl> grandchild =
+ createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
+ Transaction()
+ .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+ .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+ .show(grandchild)
+ .apply();
+
+ // Mirror mChildLayer
+ sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+ ASSERT_NE(mirrorLayer, nullptr);
+
+ // Add mirrorLayer as child of mParentLayer so it's shown on the display
+ Transaction()
+ .reparent(mirrorLayer, mParentLayer->getHandle())
+ .setPosition(mirrorLayer, 500, 500)
+ .show(mirrorLayer)
+ .apply();
+
+ {
+ SCOPED_TRACE("Initial Mirror");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ // Set color to white on grandchild layer.
+ Transaction().setColor(grandchild, half3{1, 1, 1}).apply();
+ {
+ SCOPED_TRACE("Updated Grandchild Layer Color");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ // Set color to black on child layer.
+ Transaction().setColor(mChildLayer, half3{0, 0, 0}).apply();
+ {
+ SCOPED_TRACE("Updated Child Layer Color");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+ }
+
+ // Remove grandchild layer
+ Transaction().reparent(grandchild, nullptr).apply();
+ {
+ SCOPED_TRACE("Removed Grandchild Layer");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+ }
+
+ // Remove child layer
+ Transaction().reparent(mChildLayer, nullptr).apply();
+ {
+ SCOPED_TRACE("Removed Child Layer");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ }
+
+ // Add grandchild layer to offscreen layer
+ Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+ {
+ SCOPED_TRACE("Added Grandchild Layer");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ }
+
+ // Add child layer
+ Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+ {
+ SCOPED_TRACE("Added Child Layer");
+ auto shot = screenshot();
+ // Grandchild mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+ }
+}
+
+TEST_F(MirrorLayerTest, MirrorBufferLayer) {
+ sp<SurfaceControl> bufferQueueLayer =
+ createLayer("BufferQueueLayer", 200, 200, 0, mChildLayer.get());
+ fillBufferQueueLayerColor(bufferQueueLayer, Color::BLUE, 200, 200);
+ Transaction().show(bufferQueueLayer).apply();
+
+ sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+ Transaction()
+ .reparent(mirrorLayer, mParentLayer->getHandle())
+ .setPosition(mirrorLayer, 500, 500)
+ .show(mirrorLayer)
+ .apply();
+
+ {
+ SCOPED_TRACE("Initial Mirror BufferQueueLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ fillBufferQueueLayerColor(bufferQueueLayer, Color::WHITE, 200, 200);
+ {
+ SCOPED_TRACE("Update BufferQueueLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ Transaction().reparent(bufferQueueLayer, nullptr).apply();
+ {
+ SCOPED_TRACE("Removed BufferQueueLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ sp<SurfaceControl> bufferStateLayer =
+ createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+ mChildLayer.get());
+ fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
+ Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply();
+
+ {
+ SCOPED_TRACE("Initial Mirror BufferStateLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+ {
+ SCOPED_TRACE("Update BufferStateLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+
+ Transaction().reparent(bufferStateLayer, nullptr).apply();
+ {
+ SCOPED_TRACE("Removed BufferStateLayer");
+ auto shot = screenshot();
+ // Buffer mirror
+ shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+ // Child mirror
+ shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
new file mode 100644
index 0000000..066c9aa
--- /dev/null
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+ }
+
+ virtual void TearDown() {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ LayerTransactionTest::TearDown();
+ mColorLayer = 0;
+ }
+
+ void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+ mVirtualDisplay =
+ SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ asTransaction([&](Transaction& t) {
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
+ Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+ });
+ }
+
+ void createColorLayer(uint32_t layerStack) {
+ mColorLayer =
+ createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_TRUE(mColorLayer != nullptr);
+ ASSERT_TRUE(mColorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayerStack(mColorLayer, layerStack);
+ t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setLayer(mColorLayer, INT32_MAX - 2);
+ t.setColor(mColorLayer,
+ half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+ mExpectedColor.b / 255.0f});
+ t.show(mColorLayer);
+ });
+ }
+
+ DisplayInfo mMainDisplayInfo;
+ sp<IBinder> mMainDisplay;
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<SurfaceControl> mColorLayer;
+ Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+ createColorLayer(1 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer does not render on main display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ // Verify color layer renders correctly on virtual display.
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+ // Create a display and set its layer stack to the main display's layer stack so
+ // the contents of the main display are mirrored on to the virtual display.
+
+ // Assumption here is that the new mirrored display has the same viewport as the
+ // primary display that it is mirroring.
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+ createColorLayer(0 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer renders correctly on main display and it is mirrored on the
+ // virtual display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
new file mode 100644
index 0000000..8549db2
--- /dev/null
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class RelativeZTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ // Back layer
+ mBackgroundLayer = createColorLayer("Background layer", Color::RED);
+
+ // Front layer
+ mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+ t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
+ t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mBackgroundLayer = 0;
+ mForegroundLayer = 0;
+ }
+
+ sp<SurfaceControl> mBackgroundLayer;
+ sp<SurfaceControl> mForegroundLayer;
+};
+
+// When a layer is reparented offscreen, remove relative z order if the relative parent
+// is still onscreen so that the layer is not drawn.
+TEST_F(RelativeZTest, LayerRemoved) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ // Background layer (RED)
+ // Child layer (WHITE) (relative to foregroud layer)
+ // Foregroud layer (GREEN)
+ sp<SurfaceControl> childLayer =
+ createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
+
+ Transaction{}
+ .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
+ .show(childLayer)
+ .apply();
+
+ {
+ // The childLayer should be in front of the FG control.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLayer, nullptr).apply();
+
+ // Background layer (RED)
+ // Child layer (WHITE)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+
+ {
+ // The relative z info for child layer should be reset, leaving FG control on top.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+}
+
+// When a layer is reparented offscreen, preseve relative z order if the relative parent
+// is also offscreen. Regression test b/132613412
+TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1 (WHITE)
+ // child level 2a (BLUE)
+ // child level 3 (GREEN) (relative to child level 2b)
+ // child level 2b (BLACK)
+ sp<SurfaceControl> childLevel1 =
+ createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
+ sp<SurfaceControl> childLevel2a =
+ createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
+ sp<SurfaceControl> childLevel2b =
+ createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
+ sp<SurfaceControl> childLevel3 =
+ createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
+
+ Transaction{}
+ .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+ .show(childLevel2a)
+ .show(childLevel2b)
+ .show(childLevel3)
+ .apply();
+
+ {
+ // The childLevel3 should be in front of childLevel2b.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLevel1, nullptr).apply();
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1 (WHITE)
+ // child level 2 back (BLUE)
+ // child level 3 (GREEN) (relative to child level 2b)
+ // child level 2 front (BLACK)
+ Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+
+ {
+ // Nothing should change at this point since relative z info was preserved.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/SetGeometry_test.cpp b/services/surfaceflinger/tests/SetGeometry_test.cpp
new file mode 100644
index 0000000..dca06ec
--- /dev/null
+++ b/services/surfaceflinger/tests/SetGeometry_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class SetGeometryTest : public LayerTransactionTest {
+protected:
+ void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mLayer = createLayer("Layer", mLayerWidth, mLayerHeight);
+ fillBufferQueueLayerColor(mLayer, Color::RED, mLayerWidth, mLayerHeight);
+ asTransaction([&](Transaction& t) { t.setLayer(mLayer, INT32_MAX - 1).show(mLayer); });
+
+ {
+ SCOPED_TRACE("init");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(Rect(0, 0, mLayerWidth, mLayerHeight), Color::RED);
+ sc->expectBorder(Rect(0, 0, mLayerWidth, mLayerHeight), Color::BLACK);
+ }
+ }
+
+ void TearDown() {
+ LayerTransactionTest::TearDown();
+ sc = 0;
+ mLayer = 0;
+ }
+
+ std::unique_ptr<ScreenCapture> sc;
+ sp<SurfaceControl> mLayer;
+ const int mLayerWidth = 100;
+ const int mLayerHeight = 200;
+};
+
+TEST_F(SetGeometryTest, SourceAtZeroNoScale) {
+ Rect source = Rect(0, 0, 30, 30);
+ Rect dest = Rect(60, 60, 90, 90);
+ Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+ {
+ SCOPED_TRACE("geometry applied");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(dest, Color::RED);
+ sc->expectBorder(dest, Color::BLACK);
+ }
+}
+
+TEST_F(SetGeometryTest, SourceNotAtZero) {
+ Rect source = Rect(40, 40, 70, 70);
+ Rect dest = Rect(60, 60, 90, 90);
+ Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+ {
+ SCOPED_TRACE("geometry applied");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(dest, Color::RED);
+ sc->expectBorder(dest, Color::BLACK);
+ }
+}
+
+TEST_F(SetGeometryTest, Scale) {
+ Rect source = Rect(0, 0, 100, 200);
+ Rect dest = Rect(0, 0, 200, 400);
+ Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+ {
+ SCOPED_TRACE("Scaled by 2");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(dest, Color::RED);
+ sc->expectBorder(dest, Color::BLACK);
+ }
+
+ dest = Rect(0, 0, 50, 100);
+ Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+ {
+ SCOPED_TRACE("Scaled by .5");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(dest, Color::RED);
+ sc->expectBorder(dest, Color::BLACK);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 6b4634a..b196684 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*"
+ "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*:SetGeometryTest.*"
}
}
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
new file mode 100644
index 0000000..8fdcde4
--- /dev/null
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 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_TRANSACTION_TEST_HARNESSES
+#define ANDROID_TRANSACTION_TEST_HARNESSES
+
+/*#include <algorithm>
+#include <chrono>
+#include <cinttypes>
+#include <functional>
+#include <limits>
+#include <ostream>
+
+#include <android/native_window.h>
+
+#include <binder/ProcessState.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/android_filesystem_config.h>
+#include <private/gui/ComposerService.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <math.h>
+#include <math/vec3.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "BufferGenerator.h"
+*/
+#include "LayerTransactionTest.h"
+/*#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/ScreenshotUtils.h"
+#include "utils/TransactionUtils.h"
+*/
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerRenderPathTestHarness {
+public:
+ LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+ : mDelegate(delegate), mRenderPath(renderPath) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ switch (mRenderPath) {
+ case RenderPath::SCREENSHOT:
+ return mDelegate->screenshot();
+ case RenderPath::VIRTUAL_DISPLAY:
+
+ const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ DisplayInfo mainDisplayInfo;
+ SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+
+ sp<IBinder> vDisplay;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ sp<BufferItemConsumer> itemConsumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+
+ itemConsumer = new BufferItemConsumer(consumer,
+ // Sample usage bits from screenrecord
+ GRALLOC_USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_SW_READ_OFTEN);
+
+ vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+ false /*secure*/);
+
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(vDisplay, producer);
+ t.setDisplayLayerStack(vDisplay, 0);
+ t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
+ Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
+ Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+ t.apply();
+ SurfaceComposerClient::Transaction().apply(true);
+ BufferItem item;
+ itemConsumer->acquireBuffer(&item, 0, true);
+ auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+ itemConsumer->releaseBuffer(item);
+ SurfaceComposerClient::destroyDisplay(vDisplay);
+ return sc;
+ }
+ }
+
+protected:
+ LayerTransactionTest* mDelegate;
+ RenderPath mRenderPath;
+};
+
+class LayerTypeTransactionHarness : public LayerTransactionTest {
+public:
+ LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
+
+ sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ // if the flags already have a layer type specified, return an error
+ if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+ return nullptr;
+ }
+ return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
+ }
+
+ void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
+ int32_t bufferHeight) {
+ ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
+ bufferWidth, bufferHeight));
+ }
+
+ void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+ const Color& bottomLeft, const Color& bottomRight) {
+ ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
+ bufferWidth, bufferHeight,
+ topLeft, topRight,
+ bottomLeft, bottomRight));
+ }
+
+protected:
+ uint32_t mLayerType;
+};
+} // namespace android
+#endif
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
deleted file mode 100644
index d765f68..0000000
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ /dev/null
@@ -1,5593 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <chrono>
-#include <cinttypes>
-#include <functional>
-#include <limits>
-#include <ostream>
-
-#include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
-#include <binder/ProcessState.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <hardware/hwcomposer_defs.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <math.h>
-#include <math/vec3.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "BufferGenerator.h"
-#include "utils/CallbackUtils.h"
-#include "utils/ColorUtils.h"
-#include "utils/ScreenshotUtils.h"
-#include "utils/TransactionUtils.h"
-
-namespace android {
-
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
-class LayerTransactionTest : public ::testing::Test {
-protected:
- void SetUp() override {
- mClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
-
- ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
- }
-
- virtual void TearDown() {
- mBlackBgSurface = 0;
- mClient->dispose();
- mClient = 0;
- }
-
- virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
- const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
- auto layer =
- createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
-
- Transaction t;
- t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
-
- status_t error = t.apply();
- if (error != NO_ERROR) {
- ADD_FAILURE() << "failed to initialize SurfaceControl";
- layer.clear();
- }
-
- return layer;
- }
-
- virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
- const char* name, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t flags,
- SurfaceControl* parent = nullptr) {
- auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
- EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
- return layer;
- }
-
- virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
- return createLayer(mClient, name, width, height, flags, parent);
- }
-
- sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
- SurfaceControl* parent = nullptr) {
- auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, parent);
- asTransaction([&](Transaction& t) {
- t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
- t.setAlpha(colorLayer, color.a / 255.0f);
- });
- return colorLayer;
- }
-
- ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
- // wait for previous transactions (such as setSize) to complete
- Transaction().apply(true);
-
- ANativeWindow_Buffer buffer = {};
- EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
-
- return buffer;
- }
-
- void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
- ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
-
- // wait for the newly posted buffer to be latched
- waitForLayerBuffers();
- }
-
- virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
- ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
- TransactionUtils::fillANativeWindowBufferColor(buffer,
- Rect(0, 0, bufferWidth, bufferHeight),
- color);
- postBufferQueueLayerBuffer(layer);
- }
-
- virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
- color);
- Transaction().setBuffer(layer, buffer).apply();
- }
-
- void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
- switch (mLayerType) {
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
- break;
- default:
- ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
- }
- }
-
- void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
- int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
- const Color& topRight, const Color& bottomLeft,
- const Color& bottomRight) {
- switch (mLayerType) {
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
- bottomLeft, bottomRight);
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
- bottomLeft, bottomRight);
- break;
- default:
- ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
- }
- }
-
- virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft,
- const Color& topRight, const Color& bottomLeft,
- const Color& bottomRight) {
- ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
- ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
-
- const int32_t halfW = bufferWidth / 2;
- const int32_t halfH = bufferHeight / 2;
- TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
- TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
- topRight);
- TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
- bottomLeft);
- TransactionUtils::fillANativeWindowBufferColor(buffer,
- Rect(halfW, halfH, bufferWidth,
- bufferHeight),
- bottomRight);
-
- postBufferQueueLayerBuffer(layer);
- }
-
- virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft,
- const Color& topRight, const Color& bottomLeft,
- const Color& bottomRight) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
-
- const int32_t halfW = bufferWidth / 2;
- const int32_t halfH = bufferHeight / 2;
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
- topRight);
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
- bottomLeft);
- TransactionUtils::fillGraphicBufferColor(buffer,
- Rect(halfW, halfH, bufferWidth, bufferHeight),
- bottomRight);
-
- Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
- }
-
- std::unique_ptr<ScreenCapture> screenshot() {
- std::unique_ptr<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot);
- return screenshot;
- }
-
- void asTransaction(const std::function<void(Transaction&)>& exec) {
- Transaction t;
- exec(t);
- t.apply(true);
- }
-
- static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
- static BufferGenerator bufferGenerator;
- return bufferGenerator.get(outBuffer, outFence);
- }
-
- sp<SurfaceComposerClient> mClient;
-
- sp<IBinder> mDisplay;
- uint32_t mDisplayWidth;
- uint32_t mDisplayHeight;
- uint32_t mDisplayLayerStack;
- Rect mDisplayRect = Rect::INVALID_RECT;
-
- // leave room for ~256 layers
- const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
-
- sp<SurfaceControl> mBlackBgSurface;
- bool mColorManagementUsed;
-
-private:
- void SetUpDisplay() {
- mDisplay = mClient->getInternalDisplayToken();
- ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
-
- // get display width/height
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
- mDisplayRect =
- Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
-
- // After a new buffer is queued, SurfaceFlinger is notified and will
- // latch the new buffer on next vsync. Let's heuristically wait for 3
- // vsyncs.
- mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
-
- mDisplayLayerStack = 0;
-
- mBlackBgSurface =
- createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
-
- // set layer stack (b/68888219)
- Transaction t;
- t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
- t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
- t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
- t.setColor(mBlackBgSurface, half3{0, 0, 0});
- t.setLayer(mBlackBgSurface, mLayerZBase);
- t.apply();
- }
-
- void waitForLayerBuffers() {
- // Request an empty transaction to get applied synchronously to ensure the buffer is
- // latched.
- Transaction().apply(true);
- usleep(mBufferPostDelay);
- }
-
- int32_t mBufferPostDelay;
-
- friend class LayerRenderPathTestHarness;
-};
-
-class LayerRenderPathTestHarness {
-public:
- LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
- : mDelegate(delegate), mRenderPath(renderPath) {}
-
- std::unique_ptr<ScreenCapture> getScreenCapture() {
- switch (mRenderPath) {
- case RenderPath::SCREENSHOT:
- return mDelegate->screenshot();
- case RenderPath::VIRTUAL_DISPLAY:
-
- const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
- DisplayInfo mainDisplayInfo;
- SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
-
- sp<IBinder> vDisplay;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- sp<BufferItemConsumer> itemConsumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
- consumer->setConsumerName(String8("Virtual disp consumer"));
- consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
-
- itemConsumer = new BufferItemConsumer(consumer,
- // Sample usage bits from screenrecord
- GRALLOC_USAGE_HW_VIDEO_ENCODER |
- GRALLOC_USAGE_SW_READ_OFTEN);
-
- vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
- false /*secure*/);
-
- SurfaceComposerClient::Transaction t;
- t.setDisplaySurface(vDisplay, producer);
- t.setDisplayLayerStack(vDisplay, 0);
- t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
- Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
- Rect(mainDisplayInfo.w, mainDisplayInfo.h));
- t.apply();
- SurfaceComposerClient::Transaction().apply(true);
- BufferItem item;
- itemConsumer->acquireBuffer(&item, 0, true);
- auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
- itemConsumer->releaseBuffer(item);
- SurfaceComposerClient::destroyDisplay(vDisplay);
- return sc;
- }
- }
-
-protected:
- LayerTransactionTest* mDelegate;
- RenderPath mRenderPath;
-};
-
-class LayerTypeTransactionHarness : public LayerTransactionTest {
-public:
- LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
-
- sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) {
- // if the flags already have a layer type specified, return an error
- if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
- return nullptr;
- }
- return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
- }
-
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
- int32_t bufferHeight) {
- ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
- bufferWidth, bufferHeight));
- }
-
- void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft, const Color& topRight,
- const Color& bottomLeft, const Color& bottomRight) {
- ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
- bufferWidth, bufferHeight,
- topLeft, topRight,
- bottomLeft, bottomRight));
- }
-
-protected:
- uint32_t mLayerType;
-};
-
-class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
- public ::testing::WithParamInterface<uint32_t> {
-public:
- LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
-};
-
-class LayerTypeAndRenderTypeTransactionTest
- : public LayerTypeTransactionHarness,
- public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
-public:
- LayerTypeAndRenderTypeTransactionTest()
- : LayerTypeTransactionHarness(std::get<0>(GetParam())),
- mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
-
- std::unique_ptr<ScreenCapture> getScreenCapture() {
- return mRenderPathHarness.getScreenCapture();
- }
-
-protected:
- LayerRenderPathTestHarness mRenderPathHarness;
-};
-
-::testing::Environment* const binderEnv =
- ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-
-class LayerRenderTypeTransactionTest : public LayerTransactionTest,
- public ::testing::WithParamInterface<RenderPath> {
-public:
- LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
-
- std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
- void setRelativeZBasicHelper(uint32_t layerType);
- void setRelativeZGroupHelper(uint32_t layerType);
- void setAlphaBasicHelper(uint32_t layerType);
- void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
- Color finalColor);
-
-protected:
- LayerRenderPathTestHarness mHarness;
-};
-
-INSTANTIATE_TEST_CASE_P(
- LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
- ::testing::Combine(
- ::testing::Values(
- static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
- static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
- ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
-
-INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
- ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
-
-INSTANTIATE_TEST_CASE_P(
- LayerTypeTransactionTests, LayerTypeTransactionTest,
- ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
- static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("default position");
- const Rect rect(0, 0, 32, 32);
- auto shot = getScreenCapture();
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- Transaction().setPosition(layer, 5, 10).apply();
- {
- SCOPED_TRACE("new position");
- const Rect rect(5, 10, 37, 42);
- auto shot = getScreenCapture();
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // GLES requires only 4 bits of subpixel precision during rasterization
- // XXX GLES composition does not match HWC composition due to precision
- // loss (b/69315223)
- const float epsilon = 1.0f / 16.0f;
- Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
- {
- SCOPED_TRACE("rounding down");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-
- Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
- {
- SCOPED_TRACE("rounding up");
- getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setPosition(layer, -32, -32).apply();
- {
- SCOPED_TRACE("negative coordinates");
- getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
- }
-
- Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
- {
- SCOPED_TRACE("positive coordinates");
- getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // partially out of bounds
- Transaction().setPosition(layer, -30, -30).apply();
- {
- SCOPED_TRACE("negative coordinates");
- getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
- }
-
- Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
- {
- SCOPED_TRACE("positive coordinates");
- getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
- mDisplayHeight),
- Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setPosition is applied immediately by default, with or without resize
- // pending
- Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(5, 10, 37, 42);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 32, 32);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 64, 64);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
- // cannot test robustness against invalid sizes (zero or really huge)
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
- Transaction().setLayer(layerR, mLayerZBase + 1).apply();
- {
- SCOPED_TRACE("layerR");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-
- Transaction().setLayer(layerG, mLayerZBase + 2).apply();
- {
- SCOPED_TRACE("layerG");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
- }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
- sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
- Transaction()
- .reparent(layerR, parent->getHandle())
- .reparent(layerG, parent->getHandle())
- .apply();
- Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
- {
- SCOPED_TRACE("layerR");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-
- Transaction().setLayer(layerR, -3).apply();
- {
- SCOPED_TRACE("layerG");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
- }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
-
- switch (layerType) {
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(16, 16, 48, 48))
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
- break;
- default:
- ASSERT_FALSE(true) << "Unsupported layer type";
- }
- {
- SCOPED_TRACE("layerG above");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
- shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
- }
-
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
- {
- SCOPED_TRACE("layerG below");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
- ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
- ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
- sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- sp<SurfaceControl> layerB;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
-
- Transaction()
- .reparent(layerB, parent->getHandle())
- .apply();
-
- // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
-
- std::unique_ptr<ScreenCapture> screenshot;
- // only layerB is in this range
- sp<IBinder> parentHandle = parent->getHandle();
- ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
- screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-}
-
-TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
- sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor);
-
- sp<SurfaceControl> childLayer;
- ASSERT_NO_FATAL_FAILURE(
- childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
- 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor,
- parent.get()));
- Transaction()
- .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
- .setColor(parent, half3{0.0f, 0.0f, 0.0f})
- .show(childLayer)
- .show(parent)
- .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
- .apply();
-
- Transaction()
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .setLayer(childLayer, 1)
- .apply();
-
- {
- SCOPED_TRACE("setLayer above");
- // Set layer should get applied and place the child above.
- std::unique_ptr<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot);
- screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
- }
-
- Transaction()
- .setLayer(childLayer, 1)
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .apply();
-
- {
- SCOPED_TRACE("setRelative below");
- // Set relative layer should get applied and place the child below.
- std::unique_ptr<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot);
- screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
- }
-}
-
-TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
- sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor);
- sp<SurfaceControl> relativeParent =
- LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
- 0 /* buffer height */, ISurfaceComposerClient::eFXSurfaceColor);
-
- sp<SurfaceControl> childLayer;
- ASSERT_NO_FATAL_FAILURE(
- childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
- 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor,
- parent.get()));
- Transaction()
- .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
- .setColor(parent, half3{0.0f, 0.0f, 0.0f})
- .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
- .show(childLayer)
- .show(parent)
- .show(relativeParent)
- .setLayer(parent, mLayerZBase - 1)
- .setLayer(relativeParent, mLayerZBase)
- .apply();
-
- Transaction()
- .setRelativeLayer(childLayer, relativeParent->getHandle(), 1)
- .apply();
-
- {
- SCOPED_TRACE("setLayer above");
- // Set layer should get applied and place the child above.
- std::unique_ptr<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot);
- screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
- }
-
- Transaction()
- .hide(relativeParent)
- .apply();
-
- {
- SCOPED_TRACE("hide relative parent");
- // The relative should no longer be visible.
- std::unique_ptr<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot);
- screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
- }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- sp<SurfaceControl> layerB;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
-
- // layerR = 0, layerG = layerR + 3, layerB = 2
- switch (layerType) {
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setPosition(layerG, 8, 8)
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
- .setPosition(layerB, 16, 16)
- .setLayer(layerB, mLayerZBase + 2)
- .apply();
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(8, 8, 40, 40))
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
- .setFrame(layerB, Rect(16, 16, 48, 48))
- .setLayer(layerB, mLayerZBase + 2)
- .apply();
- break;
- default:
- ASSERT_FALSE(true) << "Unsupported layer type";
- }
-
- {
- SCOPED_TRACE("(layerR < layerG) < layerB");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
- shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
- shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
- }
-
- // layerR = 4, layerG = layerR + 3, layerB = 2
- Transaction().setLayer(layerR, mLayerZBase + 4).apply();
- {
- SCOPED_TRACE("layerB < (layerR < layerG)");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
- shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
- shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
- }
-
- // layerR = 4, layerG = layerR - 3, layerB = 2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
- {
- SCOPED_TRACE("layerB < (layerG < layerR)");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
- shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
- }
-
- // restore to absolute z
- // layerR = 4, layerG = 0, layerB = 2
- Transaction().setLayer(layerG, mLayerZBase).apply();
- {
- SCOPED_TRACE("layerG < layerB < layerR");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
- }
-
- // layerR should not affect layerG anymore
- // layerR = 1, layerG = 0, layerB = 2
- Transaction().setLayer(layerR, mLayerZBase + 1).apply();
- {
- SCOPED_TRACE("layerG < layerR < layerB");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
- shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
- ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
- ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
-
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
-
- layerG.clear();
- // layerG should have been removed
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
- {
- SCOPED_TRACE("layer hidden");
- getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
- }
-
- Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
- {
- SCOPED_TRACE("layer shown");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
- const Color translucentRed = {100, 0, 0, 100};
- sp<SurfaceControl> layerR;
- sp<SurfaceControl> layerG;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
- Transaction()
- .setLayer(layerR, mLayerZBase + 1)
- .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
- .apply();
- {
- SCOPED_TRACE("layerR opaque");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
- }
-
- Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
- {
- SCOPED_TRACE("layerR translucent");
- const uint8_t g = uint8_t(255 - translucentRed.a);
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
- }
-}
-
-TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- sp<GraphicBuffer> outBuffer;
- Transaction()
- .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
- .apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
- ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-}
-
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- sp<GraphicBuffer> outBuffer;
- Transaction()
- .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
- .apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- UIDFaker f(AID_SYSTEM);
-
- // By default the system can capture screenshots with secure layers but they
- // will be blacked out
- ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- {
- SCOPED_TRACE("as system");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- // 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.
- bool outCapturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
- 0, false, ISurfaceComposer::eRotateNone, true));
- ASSERT_EQ(true, outCapturedSecureLayers);
- ScreenCapture sc(outBuffer);
- sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
- const Rect top(0, 0, 32, 16);
- const Rect bottom(0, 16, 32, 32);
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-
- ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::RED));
- // setTransparentRegionHint always applies to the following buffer
- Transaction().setTransparentRegionHint(layer, Region(top)).apply();
- ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
- {
- SCOPED_TRACE("top transparent");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::BLACK);
- shot->expectColor(bottom, Color::RED);
- }
-
- Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
- {
- SCOPED_TRACE("transparent region hint pending");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::BLACK);
- shot->expectColor(bottom, Color::RED);
- }
-
- ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::RED));
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
- {
- SCOPED_TRACE("bottom transparent");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::RED);
- shot->expectColor(bottom, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
- const Rect top(0, 0, 32, 16);
- const Rect bottom(0, 16, 32, 32);
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::RED));
- Transaction()
- .setTransparentRegionHint(layer, Region(top))
- .setBuffer(layer, buffer)
- .setFrame(layer, Rect(0, 0, 32, 32))
- .apply();
- {
- SCOPED_TRACE("top transparent");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::BLACK);
- shot->expectColor(bottom, Color::RED);
- }
-
- Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
- {
- SCOPED_TRACE("transparent region hint intermediate");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::BLACK);
- shot->expectColor(bottom, Color::BLACK);
- }
-
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
- ASSERT_NO_FATAL_FAILURE(
- TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
- Transaction().setBuffer(layer, buffer).apply();
- {
- SCOPED_TRACE("bottom transparent");
- auto shot = getScreenCapture();
- shot->expectColor(top, Color::RED);
- shot->expectColor(bottom, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
- sp<SurfaceControl> layerTransparent;
- sp<SurfaceControl> layerR;
- ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-
- // check that transparent region hint is bound by the layer size
- Transaction()
- .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
- .setPosition(layerR, 16, 16)
- .setLayer(layerR, mLayerZBase + 1)
- .apply();
- ASSERT_NO_FATAL_FAILURE(
- fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
- getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
- sp<SurfaceControl> layerTransparent;
- sp<SurfaceControl> layerR;
- ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- // check that transparent region hint is bound by the layer size
- Transaction()
- .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
- .setFrame(layerR, Rect(16, 16, 48, 48))
- .setLayer(layerR, mLayerZBase + 1)
- .apply();
- ASSERT_NO_FATAL_FAILURE(
- fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
- getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
-}
-
-void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
- sp<SurfaceControl> layer1;
- sp<SurfaceControl> layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
-
- switch (layerType) {
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setAlpha(layer1, 0.25f)
- .setAlpha(layer2, 0.75f)
- .setPosition(layer2, 16, 0)
- .setLayer(layer2, mLayerZBase + 1)
- .apply();
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- Transaction()
- .setAlpha(layer1, 0.25f)
- .setAlpha(layer2, 0.75f)
- .setFrame(layer1, Rect(0, 0, 32, 32))
- .setFrame(layer2, Rect(16, 0, 48, 32))
- .setLayer(layer2, mLayerZBase + 1)
- .apply();
- break;
- default:
- ASSERT_FALSE(true) << "Unsupported layer type";
- }
- {
- auto shot = getScreenCapture();
- uint8_t r = 16; // 64 * 0.25f
- uint8_t g = 48; // 64 * 0.75f
- shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
- shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
-
- r /= 4; // r * (1.0f - 0.75f)
- shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
- ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
- ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
- const Color color = {64, 0, 0, 255};
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
-
- Transaction().setAlpha(layer, 2.0f).apply();
- {
- SCOPED_TRACE("clamped to 1.0f");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
- }
-
- Transaction().setAlpha(layer, -1.0f).apply();
- {
- SCOPED_TRACE("clamped to 0.0f");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
- sp<SurfaceControl> layer;
- const uint8_t size = 64;
- const uint8_t testArea = 4;
- const float cornerRadius = 20.0f;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
-
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setCrop_legacy(layer, Rect(0, 0, size, size))
- .apply();
- {
- const uint8_t bottom = size - 1;
- const uint8_t right = size - 1;
- auto shot = getScreenCapture();
- // Transparent corners
- shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
- shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
- shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
- shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
- }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
- sp<SurfaceControl> parent;
- sp<SurfaceControl> child;
- const uint8_t size = 64;
- const uint8_t testArea = 4;
- const float cornerRadius = 20.0f;
- ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
- ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
-
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
- .setPosition(child, 0, size / 2)
- .apply();
- {
- const uint8_t bottom = size - 1;
- const uint8_t right = size - 1;
- auto shot = getScreenCapture();
- // Top edge of child should not have rounded corners because it's translated in the parent
- shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
- Color::GREEN);
- // But bottom edges should have been clipped according to parent bounds
- shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
- shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
- sp<SurfaceControl> bufferLayer;
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(colorLayer =
- createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor));
-
- Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
- .setLayer(colorLayer, mLayerZBase + 1)
- .apply();
-
- {
- SCOPED_TRACE("default color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
- const Color expected = {15, 51, 85, 255};
- // this is handwavy, but the precison loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
- Transaction().setColor(colorLayer, color).apply();
- {
- SCOPED_TRACE("new color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
- }
-}
-
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
-// BLUE: prior background color
-// GREEN: final background color
-// BLACK: no color or fill
-void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
- bool bufferFill, float alpha,
- Color finalColor) {
- sp<SurfaceControl> layer;
- int32_t width = 500;
- int32_t height = 500;
-
- Color fillColor = Color::RED;
- Color priorBgColor = Color::BLUE;
- Color expectedColor = Color::BLACK;
- switch (layerType) {
- case ISurfaceComposerClient::eFXSurfaceColor:
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
- Transaction()
- .setCrop_legacy(layer, Rect(0, 0, width, height))
- .setColor(layer, half3(1.0f, 0, 0))
- .apply();
- expectedColor = fillColor;
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
- if (bufferFill) {
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
- expectedColor = fillColor;
- }
- Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
- break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
- if (bufferFill) {
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
- expectedColor = fillColor;
- }
- Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
- break;
- default:
- GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
- return;
- }
-
- if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
- Transaction()
- .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
- .apply();
- if (!bufferFill) {
- expectedColor = priorBgColor;
- }
- }
-
- {
- SCOPED_TRACE("default before setting background color layer");
- screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
- }
- Transaction()
- .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
- .apply();
-
- {
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, width, height), finalColor);
- shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = true;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
- bool priorColor = true;
- bool bufferFill = true;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
- bool priorColor = true;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 0;
- Color finalColor = Color::BLACK;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
- bool priorColor = true;
- bool bufferFill = false;
- float alpha = 0;
- Color finalColor = Color::BLACK;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = true;
- float alpha = 1.0f;
- Color finalColor = Color::RED;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
- bool priorColor = true;
- bool bufferFill = false;
- float alpha = 1.0f;
- Color finalColor = Color::GREEN;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
- bool priorColor = false;
- bool bufferFill = false;
- float alpha = 0;
- Color finalColor = Color::BLACK;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
- SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
- bool priorColor = true;
- bool bufferFill = false;
- float alpha = 0;
- Color finalColor = Color::BLACK;
- ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
- priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(colorLayer =
- createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor));
- Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
- .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
- .apply();
-
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
- sp<SurfaceControl> bufferLayer;
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(colorLayer =
- createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
-
- const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
- const float alpha = 0.25f;
- const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
- // this is handwavy, but the precison loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
- Transaction()
- .setColor(colorLayer, color)
- .setAlpha(colorLayer, alpha)
- .setLayer(colorLayer, mLayerZBase + 1)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
- sp<SurfaceControl> bufferLayer;
- sp<SurfaceControl> parentLayer;
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
- ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
- 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
- const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
- const float alpha = 0.25f;
- const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
- // this is handwavy, but the precision loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
- Transaction()
- .reparent(colorLayer, parentLayer->getHandle())
- .setColor(colorLayer, color)
- .setAlpha(parentLayer, alpha)
- .setLayer(parentLayer, mLayerZBase + 1)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
- sp<SurfaceControl> bufferLayer;
- ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
-
- // color is ignored
- Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
- {
- SCOPED_TRACE("non-existing layer stack");
- getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
- }
-
- Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
- {
- SCOPED_TRACE("original layer stack");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
- {
- SCOPED_TRACE("IDENTITY");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-
- Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
- {
- SCOPED_TRACE("FLIP_H");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
- Color::WHITE, Color::BLUE);
- }
-
- Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
- {
- SCOPED_TRACE("FLIP_V");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
- Color::RED, Color::GREEN);
- }
-
- Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
- {
- SCOPED_TRACE("ROT_90");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
- Color::WHITE, Color::GREEN);
- }
-
- Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
- {
- SCOPED_TRACE("SCALE");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE, true /* filtered */);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- Transaction()
- .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
- .setFrame(layer, Rect(0, 0, 32, 32))
- .apply();
- {
- SCOPED_TRACE("IDENTITY");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-
- Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
- {
- SCOPED_TRACE("FLIP_H");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-
- Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
- {
- SCOPED_TRACE("FLIP_V");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-
- Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
- {
- SCOPED_TRACE("ROT_90");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-
- Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
- {
- SCOPED_TRACE("SCALE");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- const float rot = M_SQRT1_2; // 45 degrees
- const float trans = M_SQRT2 * 16.0f;
- Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
-
- auto shot = getScreenCapture();
- // check a 8x8 region inside each color
- auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
- const int32_t halfL = 4;
- return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
- };
- const int32_t unit = int32_t(trans / 2);
- shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
- shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
- shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
- shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setMatrix is applied after any pending resize, unlike setPosition
- Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- const Rect rect(0, 0, 32, 32);
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
- {
- SCOPED_TRACE("resize applied");
- const Rect rect(0, 0, 128, 128);
- getScreenCapture()->expectColor(rect, Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- // XXX SCALE_CROP is not respected; calling setSize and
- // setOverrideScalingMode in separate transactions does not work
- // (b/69315456)
- Transaction()
- .setSize(layer, 64, 16)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- {
- SCOPED_TRACE("SCALE_TO_WINDOW");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE, true /* filtered */);
- }
-}
-
-TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-
- sp<IBinder> handle = layer->getHandle();
- ASSERT_TRUE(handle != nullptr);
-
- FrameStats frameStats;
- mClient->getLayerFrameStats(handle, &frameStats);
-
- ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- const Rect crop(8, 8, 24, 24);
-
- Transaction().setCrop_legacy(layer, crop).apply();
- auto shot = getScreenCapture();
- shot->expectColor(crop, Color::RED);
- shot->expectBorder(crop, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- const Rect crop(8, 8, 24, 24);
-
- Transaction().setCrop(layer, crop).apply();
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("empty rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-
- {
- SCOPED_TRACE("negative rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("empty rect");
- Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- }
-
- {
- SCOPED_TRACE("negative rect");
- Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
-
- Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
-
- Transaction().setBuffer(layer, buffer).apply();
-
- // Partially out of bounds in the negative (upper left) direction
- Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
- {
- SCOPED_TRACE("out of bounds, negative (upper left) direction");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
- }
-
- // Partially out of bounds in the positive (lower right) direction
- Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
- {
- SCOPED_TRACE("out of bounds, positive (lower right) direction");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
- }
-
- // Fully out of buffer space bounds
- Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
- {
- SCOPED_TRACE("Fully out of bounds");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
- shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- const Point position(32, 32);
- const Rect crop(8, 8, 24, 24);
- Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
- auto shot = getScreenCapture();
- shot->expectColor(crop + position, Color::RED);
- shot->expectBorder(crop + position, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- const Rect frame(32, 32, 64, 64);
- const Rect crop(8, 8, 24, 24);
- Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
- auto shot = getScreenCapture();
- shot->expectColor(frame, Color::RED);
- shot->expectBorder(frame, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // crop_legacy is affected by matrix
- Transaction()
- .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setCrop_legacy(layer, Rect(8, 8, 24, 24))
- .apply();
- auto shot = getScreenCapture();
- shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
- shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setCrop_legacy is applied immediately by default, with or without resize pending
- Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
- shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
- {
- SCOPED_TRACE("resize applied");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
- shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- const Rect frame(8, 8, 24, 24);
-
- Transaction().setFrame(layer, frame).apply();
- auto shot = getScreenCapture();
- shot->expectColor(frame, Color::RED);
- shot->expectBorder(frame, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("empty rect");
- Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- {
- SCOPED_TRACE("negative rect");
- Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
-
- // A parentless layer will default to a frame with the same size as the buffer
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
- sp<SurfaceControl> parent, child;
- ASSERT_NO_FATAL_FAILURE(
- parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
-
- ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
-
- Transaction().reparent(child, parent->getHandle()).apply();
-
- // A layer will default to the frame of its parent
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
- sp<SurfaceControl> parent, child;
- ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
-
- ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
-
- Transaction().reparent(child, parent->getHandle()).apply();
-
- // A layer will default to the frame of its parent
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- std::this_thread::sleep_for(500ms);
-
- Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
- shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
- sp<SurfaceControl> parent, child;
- ASSERT_NO_FATAL_FAILURE(
- parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- Transaction().reparent(child, parent->getHandle()).apply();
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
- shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("set buffer 1");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
-
- {
- SCOPED_TRACE("set buffer 2");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
- {
- SCOPED_TRACE("set buffer 3");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
- sp<SurfaceControl> layer1;
- ASSERT_NO_FATAL_FAILURE(
- layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<SurfaceControl> layer2;
- ASSERT_NO_FATAL_FAILURE(
- layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
-
- Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
- {
- SCOPED_TRACE("set layer 1 buffer red");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
-
- Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
- {
- SCOPED_TRACE("set layer 2 buffer blue");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
- shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
- shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
- {
- SCOPED_TRACE("set layer 1 buffer green");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
- shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
- shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
- }
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
-
- {
- SCOPED_TRACE("set layer 2 buffer white");
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
- shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
- shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
- std::array<sp<GraphicBuffer>, 10> buffers;
-
- size_t idx = 0;
- for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- Color color = colors[idx % colors.size()];
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
- idx++;
- }
-
- // Set each buffer twice. The first time adds it to the cache, the second time tests that the
- // cache is working.
- idx = 0;
- for (auto& buffer : buffers) {
- for (int i = 0; i < 2; i++) {
- Transaction().setBuffer(layer, buffer).apply();
-
- Color color = colors[idx % colors.size()];
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
- idx++;
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
- std::array<sp<GraphicBuffer>, 70> buffers;
-
- size_t idx = 0;
- for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- Color color = colors[idx % colors.size()];
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
- idx++;
- }
-
- // Set each buffer twice. The first time adds it to the cache, the second time tests that the
- // cache is working.
- idx = 0;
- for (auto& buffer : buffers) {
- for (int i = 0; i < 2; i++) {
- Transaction().setBuffer(layer, buffer).apply();
-
- Color color = colors[idx % colors.size()];
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
- idx++;
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
- std::array<sp<GraphicBuffer>, 65> buffers;
-
- size_t idx = 0;
- for (auto& buffer : buffers) {
- buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- Color color = colors[idx % colors.size()];
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
- idx++;
- }
-
- // Set each buffer twice. The first time adds it to the cache, the second time tests that the
- // cache is working.
- idx = 0;
- for (auto& buffer : buffers) {
- for (int i = 0; i < 2; i++) {
- Transaction().setBuffer(layer, buffer).apply();
-
- Color color = colors[idx % colors.size()];
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
- }
- if (idx == 0) {
- buffers[0].clear();
- }
- idx++;
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
- .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
- .apply();
-
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
- Color::GREEN, true /* filtered */);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
- .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
- .apply();
-
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
- Color::BLUE, true /* filtered */);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
- .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
- .apply();
-
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
- Color::GREEN, true /* filtered */);
-}
-
-TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- Transaction().setTransformToDisplayInverse(layer, false).apply();
-
- ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
-
- Transaction().setTransformToDisplayInverse(layer, true).apply();
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
- sp<SurfaceControl> layer;
- Transaction transaction;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- sp<Fence> fence;
- if (getBuffer(nullptr, &fence) != NO_ERROR) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
-
- status_t status = fence->wait(1000);
- ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
- std::this_thread::sleep_for(200ms);
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- sp<Fence> fence = Fence::NO_FENCE;
-
- Transaction()
- .setBuffer(layer, buffer)
- .setAcquireFence(layer, fence)
- .apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- Transaction()
- .setBuffer(layer, buffer)
- .setDataspace(layer, ui::Dataspace::UNKNOWN)
- .apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- HdrMetadata hdrMetadata;
- hdrMetadata.validTypes = 0;
- Transaction()
- .setBuffer(layer, buffer)
- .setHdrMetadata(layer, hdrMetadata)
- .apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- Region region;
- region.set(32, 32);
- Transaction()
- .setBuffer(layer, buffer)
- .setSurfaceDamageRegion(layer, region)
- .apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- Transaction()
- .setBuffer(layer, buffer)
- .setApi(layer, NATIVE_WINDOW_API_CPU)
- .apply();
-
- auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- // verify this doesn't cause a crash
- Transaction().setSidebandStream(layer, nullptr).apply();
-}
-
-TEST_F(LayerTransactionTest, ReparentToSelf) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().reparent(layer, layer->getHandle()).apply();
-
- {
- // We expect the transaction to be silently dropped, but for SurfaceFlinger
- // to still be functioning.
- SCOPED_TRACE("after reparent to self");
- const Rect rect(0, 0, 32, 32);
- auto shot = screenshot();
- shot->expectColor(rect, Color::RED);
- shot->expectBorder(rect, Color::BLACK);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(colorLayer =
- createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor));
- Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
- .setLayer(colorLayer, mLayerZBase + 1)
- .apply();
- {
- SCOPED_TRACE("default color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
- half3 expected = color;
- mat3 matrix;
- matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
- matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
- matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
-
- // degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
-
- ColorTransformHelper::applyMatrix(expected, matrix);
-
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
-
- const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
- uint8_t(expected.b * 255), 255};
-
- // this is handwavy, but the precison loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
-
- Transaction().setColor(colorLayer, color)
- .setColorTransform(colorLayer, matrix, vec3()).apply();
- {
- SCOPED_TRACE("new color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
- sp<SurfaceControl> parentLayer;
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
- 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceContainer));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
-
- Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
- .setLayer(parentLayer, mLayerZBase + 1)
- .apply();
- {
- SCOPED_TRACE("default color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
- half3 expected = color;
- mat3 matrix;
- matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
- matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
- matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
-
- // degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
-
- ColorTransformHelper::applyMatrix(expected, matrix);
-
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
-
- const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
- uint8_t(expected.b * 255), 255};
-
- // this is handwavy, but the precison loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
-
- Transaction()
- .setColor(colorLayer, color)
- .setColorTransform(parentLayer, matrix, vec3())
- .apply();
- {
- SCOPED_TRACE("new color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
- }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
- sp<SurfaceControl> parentLayer;
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
- 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceContainer));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
- ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
-
- Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
- .setLayer(parentLayer, mLayerZBase + 1)
- .apply();
- {
- SCOPED_TRACE("default color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
- half3 expected = color;
- mat3 matrixChild;
- matrixChild[0][0] = 0.3; matrixChild[1][0] = 0.59; matrixChild[2][0] = 0.11;
- matrixChild[0][1] = 0.3; matrixChild[1][1] = 0.59; matrixChild[2][1] = 0.11;
- matrixChild[0][2] = 0.3; matrixChild[1][2] = 0.59; matrixChild[2][2] = 0.11;
- mat3 matrixParent;
- matrixParent[0][0] = 0.2; matrixParent[1][0] = 0.4; matrixParent[2][0] = 0.10;
- matrixParent[0][1] = 0.2; matrixParent[1][1] = 0.4; matrixParent[2][1] = 0.10;
- matrixParent[0][2] = 0.2; matrixParent[1][2] = 0.4; matrixParent[2][2] = 0.10;
-
- // degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
-
- ColorTransformHelper::applyMatrix(expected, matrixChild);
- ColorTransformHelper::applyMatrix(expected, matrixParent);
-
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
-
- const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
- uint8_t(expected.b * 255), 255};
-
- // this is handwavy, but the precison loss scaled by 255 (8-bit per
- // channel) should be less than one
- const uint8_t tolerance = 1;
-
- Transaction()
- .setColor(colorLayer, color)
- .setColorTransform(parentLayer, matrixParent, vec3())
- .setColorTransform(colorLayer, matrixChild, vec3())
- .apply();
- {
- SCOPED_TRACE("new color");
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
- }
-}
-
-class LayerCallbackTest : public LayerTransactionTest {
-public:
- virtual sp<SurfaceControl> createBufferStateLayer() {
- return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
- }
-
- static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
- const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
- bool setBackgroundColor = false) {
- if (layer) {
- sp<GraphicBuffer> buffer;
- sp<Fence> fence;
- if (setBuffer) {
- int err = getBuffer(&buffer, &fence);
- if (err != NO_ERROR) {
- return err;
- }
-
- transaction.setBuffer(layer, buffer);
- transaction.setAcquireFence(layer, fence);
- }
-
- if (setBackgroundColor) {
- transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
- ui::Dataspace::UNKNOWN);
- }
- }
-
- transaction.addTransactionCompletedCallback(callbackHelper->function,
- callbackHelper->getContext());
- return NO_ERROR;
- }
-
- static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
- bool finalState = false) {
- CallbackData callbackData;
- ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
- EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
-
- if (finalState) {
- ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
- }
- }
-
- static void waitForCallbacks(CallbackHelper& helper,
- const std::vector<ExpectedResult>& expectedResults,
- bool finalState = false) {
- for (const auto& expectedResult : expectedResults) {
- waitForCallback(helper, expectedResult);
- }
- if (finalState) {
- ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
- }
- }
-};
-
-TEST_F(LayerCallbackTest, BufferColor) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer, true, true);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoBufferNoColor) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer, false, false);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
- ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, BufferNoColor) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer, true, false);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoBufferColor) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer, false, true);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoStateChange) {
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
-
- ExpectedResult expected;
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, OffScreen) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeBufferNoColor) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeNoBufferColor) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- int err = fillTransaction(transaction1, &callback1, layer1, false, true);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2, false, true);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
- ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2, false, true);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
- ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-TEST_F(LayerCallbackTest, Merge_SameCallback) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback;
- int err = fillTransaction(transaction1, &callback, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction2.merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, Merge_SameLayer) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- int err = fillTransaction(transaction1, &callback1, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction2.merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, Merge_DifferentClients) {
- sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
- client2(new SurfaceComposerClient);
-
- ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
- ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- for (size_t i = 0; i < 10; i++) {
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::ACQUIRED,
- (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
- : ExpectedResult::PreviousBuffer::RELEASED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
- }
- ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- for (size_t i = 0; i < 10; i++) {
- ExpectedResult expected;
-
- if (i == 0) {
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- } else {
- int err = fillTransaction(transaction, &callback);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- }
-
- transaction.apply();
-
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
- }
- ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- for (size_t i = 0; i < 10; i++) {
- if (i == 0) {
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- } else {
- int err = fillTransaction(transaction, &callback);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- ExpectedResult expected;
- expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
- : ExpectedResult::Transaction::NOT_PRESENTED,
- layer,
- (i == 0) ? ExpectedResult::Buffer::ACQUIRED
- : ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
- }
- ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- for (size_t i = 0; i < 10; i++) {
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
- ExpectedResult::Buffer::ACQUIRED,
- (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
- : ExpectedResult::PreviousBuffer::RELEASED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
- }
- ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
- ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
- sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
- client2(new SurfaceComposerClient);
- ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
- ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- for (size_t i = 0; i < 10; i++) {
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
- ExpectedResult::Buffer::ACQUIRED,
- (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
- : ExpectedResult::PreviousBuffer::RELEASED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
- }
- ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
- ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
- sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
- client2(new SurfaceComposerClient);
- ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
- ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
-
- // Normal call to set up test
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
- expected.reset();
-
- // Test
- err = fillTransaction(transaction1, &callback1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction2.merge(std::move(transaction1)).apply();
-
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
- sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
- client2(new SurfaceComposerClient);
-
- ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
- ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
- ISurfaceComposerClient::eFXSurfaceBufferState));
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
-
- // Normal call to set up test
- int err = fillTransaction(transaction1, &callback1, layer1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2, layer2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
- expected.reset();
-
- // Test
- err = fillTransaction(transaction1, &callback1);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
- err = fillTransaction(transaction2, &callback2);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
- ExpectedResult::Buffer::NOT_ACQUIRED);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- std::vector<ExpectedResult> expectedResults(50);
- for (auto& expected : expectedResults) {
- expected.reset();
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::ACQUIRED,
- ExpectedResult::PreviousBuffer::UNKNOWN);
-
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
- }
- EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- // Normal call to set up test
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-
- // Test
- std::vector<ExpectedResult> expectedResults(50);
- for (auto& expected : expectedResults) {
- expected.reset();
-
- err = fillTransaction(transaction, &callback);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.apply();
- }
- EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- // Normal call to set up test
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- ExpectedResult expectedResult;
- expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
-
- // Test
- std::vector<ExpectedResult> expectedResults(50);
- for (auto& expected : expectedResults) {
- expected.reset();
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
- ExpectedResult::Buffer::NOT_ACQUIRED);
-
- err = fillTransaction(transaction, &callback);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
- }
- EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- expected.addExpectedPresentTime(time);
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback1;
- int err = fillTransaction(transaction, &callback1, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected1;
- expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- expected1.addExpectedPresentTime(time);
-
- CallbackHelper callback2;
- err = fillTransaction(transaction, &callback2, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 33ms after the first frame
- time += (33.3 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected2;
- expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::ACQUIRED,
- ExpectedResult::PreviousBuffer::RELEASED);
- expected2.addExpectedPresentTime(time);
-
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback1;
- int err = fillTransaction(transaction, &callback1, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 100ms in the future
- nsecs_t time = systemTime() + (100 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected1;
- expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- expected1.addExpectedPresentTime(time);
-
- CallbackHelper callback2;
- err = fillTransaction(transaction, &callback2, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 33ms before the previous frame
- time -= (33.3 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected2;
- expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::ACQUIRED,
- ExpectedResult::PreviousBuffer::RELEASED);
-
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
- Transaction transaction;
- CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
- if (err) {
- GTEST_SUCCEED() << "test not supported";
- return;
- }
-
- // Try to present 100ms in the past
- nsecs_t time = systemTime() - (100 * 1e6);
-
- transaction.setDesiredPresentTime(time);
- transaction.apply();
-
- ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- expected.addExpectedPresentTime(systemTime());
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-class LayerUpdateTest : public LayerTransactionTest {
-protected:
- virtual void SetUp() {
- LayerTransactionTest::SetUp();
- ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
-
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
-
- // Background surface
- mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth,
- displayHeight, 0);
- 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);
-
- ASSERT_TRUE(mFGSurfaceControl != nullptr);
- ASSERT_TRUE(mFGSurfaceControl->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-
- // Synchronization surface
- mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
- ASSERT_TRUE(mSyncSurfaceControl != nullptr);
- ASSERT_TRUE(mSyncSurfaceControl->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-
- asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
-
- t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
-
- t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
- .setPosition(mFGSurfaceControl, 64, 64)
- .show(mFGSurfaceControl);
-
- t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
- .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
- .show(mSyncSurfaceControl);
- });
- }
-
- virtual void TearDown() {
- LayerTransactionTest::TearDown();
- mBGSurfaceControl = 0;
- mFGSurfaceControl = 0;
- mSyncSurfaceControl = 0;
- }
-
- void waitForPostedBuffers() {
- // Since the sync surface is in synchronous mode (i.e. double buffered)
- // posting three buffers to it should ensure that at least two
- // SurfaceFlinger::handlePageFlip calls have been made, which should
- // guaranteed that a buffer posted to another Surface has been retired.
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- }
-
-
- sp<SurfaceControl> mBGSurfaceControl;
- sp<SurfaceControl> mFGSurfaceControl;
-
- // This surface is used to ensure that the buffers posted to
- // mFGSurfaceControl have been picked up by SurfaceFlinger.
- sp<SurfaceControl> mSyncSurfaceControl;
-};
-
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
-
- std::unique_ptr<ScreenCapture> sc;
-
- sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
- waitForPostedBuffers();
-
- Transaction{}
- .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
- .setPosition(relative, 64, 64)
- .apply();
-
- {
- // The relative should be on top of the FG control.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
- Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
- {
- // Nothing should change at this point.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
-
- Transaction{}.hide(relative).apply();
-
- {
- // Ensure that the relative was actually hidden, rather than
- // being left in the detached but visible state.
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(64, 64);
- }
-}
-
-class GeometryLatchingTest : public LayerUpdateTest {
-protected:
- void EXPECT_INITIAL_STATE(const char* trace) {
- SCOPED_TRACE(trace);
- ScreenCapture::captureScreen(&sc);
- // We find the leading edge of the FG surface.
- sc->expectFGColor(127, 127);
- sc->expectBGColor(128, 128);
- }
-
- void lockAndFillFGBuffer() {
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false);
- }
-
- void unlockFGBuffer() {
- sp<Surface> s = mFGSurfaceControl->getSurface();
- ASSERT_EQ(NO_ERROR, s->unlockAndPost());
- waitForPostedBuffers();
- }
-
- void completeFGResize() {
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
- }
- void restoreInitialState() {
- asTransaction([&](Transaction& t) {
- t.setSize(mFGSurfaceControl, 64, 64);
- t.setPosition(mFGSurfaceControl, 64, 64);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
- });
-
- EXPECT_INITIAL_STATE("After restoring initial state");
- }
- std::unique_ptr<ScreenCapture> sc;
-};
-
-class CropLatchingTest : public GeometryLatchingTest {
-protected:
- void EXPECT_CROPPED_STATE(const char* trace) {
- SCOPED_TRACE(trace);
- ScreenCapture::captureScreen(&sc);
- // The edge should be moved back one pixel by our crop.
- sc->expectFGColor(126, 126);
- sc->expectBGColor(127, 127);
- sc->expectBGColor(128, 128);
- }
-
- void EXPECT_RESIZE_STATE(const char* trace) {
- SCOPED_TRACE(trace);
- ScreenCapture::captureScreen(&sc);
- // The FG is now resized too 128,128 at 64,64
- sc->expectFGColor(64, 64);
- sc->expectFGColor(191, 191);
- sc->expectBGColor(192, 192);
- }
-};
-
-TEST_F(LayerUpdateTest, DeferredTransactionTest) {
- std::unique_ptr<ScreenCapture> sc;
- {
- SCOPED_TRACE("before anything");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectFGColor(96, 96);
- sc->expectBGColor(160, 160);
- }
-
- // set up two deferred transactions on different frames
- asTransaction([&](Transaction& t) {
- t.setAlpha(mFGSurfaceControl, 0.75);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber());
- });
-
- asTransaction([&](Transaction& t) {
- t.setPosition(mFGSurfaceControl, 128, 128);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
- });
-
- {
- SCOPED_TRACE("before any trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectFGColor(96, 96);
- sc->expectBGColor(160, 160);
- }
-
- // should trigger the first deferred transaction, but not the second one
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- {
- SCOPED_TRACE("after first trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->checkPixel(96, 96, 162, 63, 96);
- sc->expectBGColor(160, 160);
- }
-
- // should show up immediately since it's not deferred
- asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
-
- // trigger the second deferred transaction
- TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
- {
- SCOPED_TRACE("after second trigger");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(32, 32);
- sc->expectBGColor(96, 96);
- sc->expectFGColor(160, 160);
- }
-}
-
-TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
- std::unique_ptr<ScreenCapture> sc;
-
- sp<SurfaceControl> childNoBuffer =
- createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
- PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
- TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
- SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
- .show(childNoBuffer)
- .show(childBuffer)
- .apply(true);
- {
- ScreenCapture::captureScreen(&sc);
- sc->expectChildColor(73, 73);
- sc->expectFGColor(74, 74);
- }
- SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
- .apply(true);
- {
- ScreenCapture::captureScreen(&sc);
- sc->expectChildColor(73, 73);
- sc->expectChildColor(74, 74);
- }
-}
-
-TEST_F(LayerUpdateTest, MergingTransactions) {
- std::unique_ptr<ScreenCapture> sc;
- {
- SCOPED_TRACE("before move");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(0, 12);
- sc->expectFGColor(75, 75);
- sc->expectBGColor(145, 145);
- }
-
- Transaction t1, t2;
- t1.setPosition(mFGSurfaceControl, 128, 128);
- t2.setPosition(mFGSurfaceControl, 0, 0);
- // We expect that the position update from t2 now
- // overwrites the position update from t1.
- t1.merge(std::move(t2));
- t1.apply();
-
- {
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(1, 1);
- }
-}
-
-TEST_F(LayerUpdateTest, MergingTransactionFlags) {
- Transaction().hide(mFGSurfaceControl).apply();
- std::unique_ptr<ScreenCapture> sc;
- {
- SCOPED_TRACE("before merge");
- ScreenCapture::captureScreen(&sc);
- sc->expectBGColor(0, 12);
- sc->expectBGColor(75, 75);
- sc->expectBGColor(145, 145);
- }
-
- Transaction t1, t2;
- t1.show(mFGSurfaceControl);
- t2.setFlags(mFGSurfaceControl, 0 /* flags */, layer_state_t::eLayerSecure /* mask */);
- t1.merge(std::move(t2));
- t1.apply();
-
- {
- SCOPED_TRACE("after merge");
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(75, 75);
- }
-}
-
-class ChildLayerTest : public LayerUpdateTest {
-protected:
- void SetUp() override {
- LayerUpdateTest::SetUp();
- mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- {
- SCOPED_TRACE("before anything");
- mCapture = screenshot();
- mCapture->expectChildColor(64, 64);
- }
- }
- void TearDown() override {
- LayerUpdateTest::TearDown();
- mChild = 0;
- }
-
- sp<SurfaceControl> mChild;
- std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ChildLayerTest, ChildLayerPositioning) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
- {
- mCapture = screenshot();
- // Top left of foreground should now be at 0, 0
- mCapture->expectFGColor(0, 0);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(10, 10);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(20, 20);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerCropping) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
- });
-
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(4, 4);
- mCapture->expectBGColor(5, 5);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerConstraints) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setPosition(mChild, 63, 63);
- });
-
- {
- mCapture = screenshot();
- mCapture->expectFGColor(0, 0);
- // Last pixel in foreground should now be the child.
- mCapture->expectChildColor(63, 63);
- // But the child should be constrained and the next pixel
- // must be the background
- mCapture->expectBGColor(64, 64);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerScaling) {
- asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
- // Find the boundary between the parent and child
- {
- mCapture = screenshot();
- mCapture->expectChildColor(9, 9);
- mCapture->expectFGColor(10, 10);
- }
-
- asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
-
- // The boundary should be twice as far from the origin now.
- // The pixels from the last test should all be child now
- {
- mCapture = screenshot();
- mCapture->expectChildColor(9, 9);
- mCapture->expectChildColor(10, 10);
- mCapture->expectChildColor(19, 19);
- mCapture->expectFGColor(20, 20);
- }
-}
-
-// A child with a scale transform should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
- asTransaction([&](Transaction& t) {
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setPosition(mChild, 0, 0);
- });
-
- // Find the boundary between the parent and child.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 9);
- mCapture->expectFGColor(10, 10);
- }
-
- asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
-
- // The child should fill its parent bounds and be cropped by it.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(63, 63);
- mCapture->expectBGColor(64, 64);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerAlpha) {
- TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
- TransactionUtils::fillSurfaceRGBA8(mChild, 0, 254, 0);
- waitForPostedBuffers();
-
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- // Unblended child color
- mCapture->checkPixel(0, 0, 0, 254, 0);
- }
-
- asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
-
- {
- mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 127, 127, 0);
- }
-
- asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
-
- {
- mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 95, 64, 95);
- }
-}
-
-TEST_F(ChildLayerTest, ReparentChildren) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) {
- t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
- });
-
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- // In reparenting we should have exposed the entire foreground surface.
- mCapture->expectFGColor(74, 74);
- // And the child layer should now begin at 10, 10 (since the BG
- // layer is at (0, 0)).
- mCapture->expectBGColor(9, 9);
- mCapture->expectChildColor(10, 10);
- }
-}
-
-TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
- sp<SurfaceControl> mGrandChild =
- createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
- TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
-
- {
- SCOPED_TRACE("Grandchild visible");
- ScreenCapture::captureScreen(&mCapture);
- mCapture->checkPixel(64, 64, 111, 111, 111);
- }
-
- mChild.clear();
-
- {
- SCOPED_TRACE("After destroying child");
- ScreenCapture::captureScreen(&mCapture);
- mCapture->expectFGColor(64, 64);
- }
-
- asTransaction([&](Transaction& t) {
- t.reparent(mGrandChild, mFGSurfaceControl->getHandle());
- });
-
- {
- SCOPED_TRACE("After reparenting grandchild");
- ScreenCapture::captureScreen(&mCapture);
- mCapture->checkPixel(64, 64, 111, 111, 111);
- }
-}
-
-TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) {
- sp<SurfaceControl> mGrandChild =
- createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
- TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
-
- // draw grand child behind the foreground surface
- asTransaction([&](Transaction& t) {
- t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
- });
-
- {
- SCOPED_TRACE("Child visible");
- ScreenCapture::captureScreen(&mCapture);
- mCapture->checkPixel(64, 64, 200, 200, 200);
- }
-
- asTransaction([&](Transaction& t) {
- t.reparent(mChild, nullptr);
- t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
- });
-
- {
- SCOPED_TRACE("foreground visible reparenting grandchild");
- ScreenCapture::captureScreen(&mCapture);
- mCapture->checkPixel(64, 64, 195, 63, 63);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChild); });
-
- // Since the child has the same client as the parent, it will not get
- // detached and will be hidden.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectFGColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
- sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> mChildNewClient =
- createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(mChildNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
- asTransaction([&](Transaction& t) {
- t.hide(mChild);
- t.show(mChildNewClient);
- t.setPosition(mChildNewClient, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- Transaction().detachChildren(mFGSurfaceControl).apply();
- Transaction().hide(childNewClient).apply();
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-
- sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
- fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
- 32);
- Transaction()
- .setLayer(newParentSurface, INT32_MAX - 1)
- .show(newParentSurface)
- .setPosition(newParentSurface, 20, 20)
- .reparent(childNewClient, newParentSurface->getHandle())
- .apply();
- {
- mCapture = screenshot();
- // Child is now hidden.
- mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
- }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color{195, 63, 63, 255});
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-
- Transaction()
- .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber())
- .apply();
- Transaction().detachChildren(mFGSurfaceControl).apply();
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
- // BufferLayer can still dequeue buffers even though there's a detached layer with a
- // deferred transaction.
- {
- SCOPED_TRACE("new buffer");
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color::RED);
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-}
-
-TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- // But it's only 10x15.
- mCapture->expectFGColor(10, 15);
- }
-
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // We cause scaling by 2.
- t.setSize(mFGSurfaceControl, 128, 128);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(10, 10);
- mCapture->expectChildColor(19, 29);
- // And now it should be scaled all the way to 20x30
- mCapture->expectFGColor(20, 30);
- }
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- // We've positioned the child in the top left.
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 14);
- // But it's only 10x15.
- mCapture->expectFGColor(10, 15);
- }
- // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
- // the WM specified state size.
- asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- {
- // The child should still be in the same place and not have any strange scaling as in
- // b/37673612.
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectFGColor(10, 10);
- }
-}
-
-// A child with a buffer transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setSize(mChild, 100, 100);
- });
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- {
- mCapture = screenshot();
-
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(63, 63);
- mCapture->expectBGColor(64, 64);
- }
-
- asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- // Apply a 90 transform on the buffer.
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- // The child should be cropped by the new parent bounds.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(99, 63);
- mCapture->expectFGColor(100, 63);
- mCapture->expectBGColor(128, 64);
- }
-}
-
-// A child with a scale transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setSize(mChild, 200, 200);
- });
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- {
- mCapture = screenshot();
-
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(63, 63);
- mCapture->expectBGColor(64, 64);
- }
-
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // Set a scaling by 2.
- t.setSize(mFGSurfaceControl, 128, 128);
- });
-
- // Child should inherit its parents scale but should be cropped by its parent bounds.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(127, 127);
- mCapture->expectBGColor(128, 128);
- }
-}
-
-// Regression test for b/127368943
-// Child should ignore the buffer transform but apply parent scale transform.
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- });
-
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 14);
- mCapture->expectFGColor(10, 15);
- }
-
- // Change the size of the foreground to 128 * 64 so we can test rotation as well.
- asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- t.setSize(mFGSurfaceControl, 128, 64);
- });
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
- // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 32, 64);
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
- waitForPostedBuffers();
-
- // The child should ignore the buffer transform but apply the 2.0 scale from parent.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(19, 29);
- mCapture->expectFGColor(20, 30);
- }
-}
-
-TEST_F(ChildLayerTest, Bug36858924) {
- // Destroy the child layer
- mChild.clear();
-
- // Now recreate it as hidden
- mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
-
- // Show the child layer in a deferred transaction
- asTransaction([&](Transaction& t) {
- t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber());
- t.show(mChild);
- });
-
- // Render the foreground surface a few times
- //
- // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
- // frame because SurfaceFlinger would never process the deferred transaction and would therefore
- // never acquire/release the first buffer
- ALOGI("Filling 1");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
- ALOGI("Filling 2");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
- ALOGI("Filling 3");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
- ALOGI("Filling 4");
- TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-}
-
-TEST_F(ChildLayerTest, Reparent) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
-
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- // In reparenting we should have exposed the entire foreground surface.
- mCapture->expectFGColor(74, 74);
- // And the child layer should now begin at 10, 10 (since the BG
- // layer is at (0, 0)).
- mCapture->expectBGColor(9, 9);
- mCapture->expectChildColor(10, 10);
- }
-}
-
-TEST_F(ChildLayerTest, ReparentToNoParent) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
- asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
- {
- mCapture = screenshot();
- // The surface should now be offscreen.
- mCapture->expectFGColor(64, 64);
- mCapture->expectFGColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, ReparentFromNoParent) {
- sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
- ASSERT_TRUE(newSurface != nullptr);
- ASSERT_TRUE(newSurface->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(newSurface, 63, 195, 63);
- asTransaction([&](Transaction& t) {
- t.hide(mChild);
- t.show(newSurface);
- t.setPosition(newSurface, 10, 10);
- t.setLayer(newSurface, INT32_MAX - 2);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // At 10, 10 we should see the new surface
- mCapture->checkPixel(10, 10, 63, 195, 63);
- }
-
- asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
-
- {
- mCapture = screenshot();
- // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
- // mFGSurface, putting it at 74, 74.
- mCapture->expectFGColor(64, 64);
- mCapture->checkPixel(74, 74, 63, 195, 63);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, NestedChildren) {
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
- {
- mCapture = screenshot();
- // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
- // which begins at 64, 64
- mCapture->checkPixel(64, 64, 50, 50, 50);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
- sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
- TransactionUtils::fillSurfaceRGBA8(relative, 255, 255, 255);
-
- Transaction t;
- t.setLayer(relative, INT32_MAX)
- .setRelativeLayer(mChild, relative->getHandle(), 1)
- .setPosition(mFGSurfaceControl, 0, 0)
- .apply(true);
-
- // We expect that the child should have been elevated above our
- // INT_MAX layer even though it's not a child of it.
- {
- mCapture = screenshot();
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 9);
- mCapture->checkPixel(10, 10, 255, 255, 255);
- }
-}
-
-class BoundlessLayerTest : public LayerUpdateTest {
-protected:
- std::unique_ptr<ScreenCapture> mCapture;
-};
-
-// Verify setting a size on a buffer layer has no effect.
-TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
- sp<SurfaceControl> bufferLayer =
- createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- ASSERT_TRUE(bufferLayer->isValid());
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
- asTransaction([&](Transaction& t) { t.show(bufferLayer); });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
- // Buffer layer should not extend past buffer bounds
- mCapture->expectFGColor(95, 95);
- }
-}
-
-// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
-// which will crop the color layer.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
- sp<SurfaceControl> colorLayer =
- createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(colorLayer);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
- // Color layer should not extend past foreground bounds
- mCapture->expectBGColor(129, 129);
- }
-}
-
-// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
-// a crop which will be used to crop the color layer.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
- sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
- ASSERT_TRUE(cropLayer->isValid());
- sp<SurfaceControl> colorLayer =
- createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(cropLayer);
- t.show(colorLayer);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // 5 pixels from the foreground we should see the child surface
- mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
- // 10 pixels from the foreground we should be back to the foreground surface
- mCapture->expectFGColor(74, 74);
- }
-}
-
-// Verify for boundless layer with no children, their transforms have no effect.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
- sp<SurfaceControl> colorLayer =
- createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setPosition(colorLayer, 320, 320);
- t.setMatrix(colorLayer, 2, 0, 0, 2);
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(colorLayer);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
- // Color layer should not extend past foreground bounds
- mCapture->expectBGColor(129, 129);
- }
-}
-
-// Verify for boundless layer with children, their transforms have an effect.
-TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
- sp<SurfaceControl> boundlessLayerRightShift =
- createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
- ASSERT_TRUE(boundlessLayerRightShift->isValid());
- sp<SurfaceControl> boundlessLayerDownShift =
- createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, boundlessLayerRightShift.get());
- ASSERT_TRUE(boundlessLayerDownShift->isValid());
- sp<SurfaceControl> colorLayer =
- createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setPosition(boundlessLayerRightShift, 32, 0);
- t.show(boundlessLayerRightShift);
- t.setPosition(boundlessLayerDownShift, 0, 32);
- t.show(boundlessLayerDownShift);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(colorLayer);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
- // Color layer should not extend past foreground bounds
- mCapture->expectBGColor(129, 129);
- }
-}
-
-// Verify child layers do not get clipped if they temporarily move into the negative
-// coordinate space as the result of an intermediate transformation.
-TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
- sp<SurfaceControl> boundlessLayer =
- mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
- ASSERT_TRUE(boundlessLayer != nullptr);
- ASSERT_TRUE(boundlessLayer->isValid());
- sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
- ASSERT_TRUE(colorLayer != nullptr);
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- // shift child layer off bounds. If this layer was not boundless, we will
- // expect the child layer to be cropped.
- t.setPosition(boundlessLayer, 32, 32);
- t.show(boundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
- // undo shift by parent
- t.setPosition(colorLayer, -32, -32);
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(colorLayer);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
- // Color layer should not extend past foreground bounds
- mCapture->expectBGColor(129, 129);
- }
-}
-
-// Verify for boundless root layers with children, their transforms have an effect.
-TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
- sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
- PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
- ASSERT_TRUE(rootBoundlessLayer->isValid());
- sp<SurfaceControl> colorLayer =
- createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
-
- ASSERT_TRUE(colorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
- t.setPosition(rootBoundlessLayer, 32, 32);
- t.show(rootBoundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
- t.setColor(colorLayer, half3{0, 0, 0});
- t.show(colorLayer);
- t.hide(mFGSurfaceControl);
- });
- {
- mCapture = screenshot();
- // Top left of background must now be visible
- mCapture->expectBGColor(0, 0);
- // Top left of foreground must now be visible
- mCapture->expectBGColor(31, 31);
- // Foreground Surface bounds must be color layer
- mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
- // Color layer should not extend past foreground bounds
- mCapture->expectBGColor(97, 97);
- }
-}
-
-class ScreenCaptureTest : public LayerUpdateTest {
-protected:
- std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
- auto bgHandle = mBGSurfaceControl->getHandle();
- ScreenCapture::captureLayers(&mCapture, bgHandle);
- mCapture->expectBGColor(0, 0);
- // Doesn't capture FG layer which is at 64, 64
- mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl layer and its child.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl's child
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
- sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, child2.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .show(child3)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- auto childHandle = child->getHandle();
-
- // Captures child
- ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
- mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
- // Area outside of child's bounds is transparent.
- mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_NE(nullptr, child.get()) << "failed to create surface";
- sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer above fg layer so should be shown above when computing all layers.
- .setRelativeLayer(relative, fgHandle, 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer below fg layer but relative to child layer so it should be shown
- // above child layer.
- .setLayer(relative, -1)
- .setRelativeLayer(relative, child->getHandle(), 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
- // relative value should be taken into account, placing it above child layer.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- // Relative layer is showing on top of child layer
- mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
- void SetUp() override {
- LayerUpdateTest::SetUp();
-
- mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(mChild).apply(true);
- }
-
- void verify(std::function<void()> verifyStartingState) {
- // Verify starting state before a screenshot is taken.
- verifyStartingState();
-
- // Verify child layer does not inherit any of the properties of its
- // parent when its screenshot is captured.
- auto fgHandle = mFGSurfaceControl->getHandle();
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-
- // Verify all assumptions are still true after the screenshot is taken.
- verifyStartingState();
- }
-
- std::unique_ptr<ScreenCapture> mCapture;
- sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
-
- SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
- // Even though the parent is hidden we should still capture the child.
-
- // Before and after reparenting, verify child is properly hidden
- // when rendering full-screen.
- verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
- SurfaceComposerClient::Transaction()
- .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
- .apply(true);
-
- // Even though the parent is cropped out we should still capture the child.
-
- // Before and after reparenting, verify child is cropped by parent.
- verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
- SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
- // We should not inherit the parent scaling.
-
- // Before and after reparenting, verify child is properly scaled.
- verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- // Captures mFGSurfaceControl, its child, and the grandchild.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
- mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
- // Captures only the child layer, and not the parent.
- ScreenCapture::captureLayers(&mCapture, childHandle);
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- auto grandchildHandle = grandchild->getHandle();
-
- // Captures only the grandchild.
- ScreenCapture::captureLayers(&mCapture, grandchildHandle);
- mCapture->checkPixel(0, 0, 50, 50, 50);
- mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- const Rect crop = Rect(0, 0, 30, 30);
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
- // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
- // area visible.
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
- // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
- mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
- auto redLayerHandle = redLayer->getHandle();
- redLayer.clear();
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
-
- // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
-
-
-class DereferenceSurfaceControlTest : public LayerTransactionTest {
-protected:
- void SetUp() override {
- LayerTransactionTest::SetUp();
- bgLayer = createLayer("BG layer", 20, 20);
- fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
- fgLayer = createLayer("FG layer", 20, 20);
- fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
- Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
- {
- SCOPED_TRACE("before anything");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
- }
- }
- void TearDown() override {
- LayerTransactionTest::TearDown();
- bgLayer = 0;
- fgLayer = 0;
- }
-
- sp<SurfaceControl> bgLayer;
- sp<SurfaceControl> fgLayer;
-};
-
-TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
- fgLayer = nullptr;
- {
- SCOPED_TRACE("after setting null");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
- }
-}
-
-TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
- auto transaction = Transaction().show(fgLayer);
- fgLayer = nullptr;
- {
- SCOPED_TRACE("after setting null");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
- }
-}
-
-class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
-protected:
- virtual void SetUp() {
- LayerTransactionTest::SetUp();
- ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
- mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
- SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
-
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&mProducer, &consumer);
- consumer->setConsumerName(String8("Virtual disp consumer"));
- consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
- }
-
- virtual void TearDown() {
- SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
- LayerTransactionTest::TearDown();
- mColorLayer = 0;
- }
-
- void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
- mVirtualDisplay =
- SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
- asTransaction([&](Transaction& t) {
- t.setDisplaySurface(mVirtualDisplay, mProducer);
- t.setDisplayLayerStack(mVirtualDisplay, layerStack);
- t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
- Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
- });
- }
-
- void createColorLayer(uint32_t layerStack) {
- mColorLayer =
- createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
- ASSERT_TRUE(mColorLayer != nullptr);
- ASSERT_TRUE(mColorLayer->isValid());
- asTransaction([&](Transaction& t) {
- t.setLayerStack(mColorLayer, layerStack);
- t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
- t.setLayer(mColorLayer, INT32_MAX - 2);
- t.setColor(mColorLayer,
- half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
- mExpectedColor.b / 255.0f});
- t.show(mColorLayer);
- });
- }
-
- DisplayInfo mMainDisplayInfo;
- sp<IBinder> mMainDisplay;
- sp<IBinder> mVirtualDisplay;
- sp<IGraphicBufferProducer> mProducer;
- sp<SurfaceControl> mColorLayer;
- Color mExpectedColor = {63, 63, 195, 255};
-};
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
- createColorLayer(1 /* layerStack */);
-
- asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
- // Verify color layer does not render on main display.
- std::unique_ptr<ScreenCapture> sc;
- ScreenCapture::captureScreen(&sc, mMainDisplay);
- sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
- sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
- // Verify color layer renders correctly on virtual display.
- ScreenCapture::captureScreen(&sc, mVirtualDisplay);
- sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
- sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
-}
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
- // Create a display and set its layer stack to the main display's layer stack so
- // the contents of the main display are mirrored on to the virtual display.
-
- // Assumption here is that the new mirrored display has the same viewport as the
- // primary display that it is mirroring.
- createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
- createColorLayer(0 /* layerStack */);
-
- asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
- // Verify color layer renders correctly on main display and it is mirrored on the
- // virtual display.
- std::unique_ptr<ScreenCapture> sc;
- ScreenCapture::captureScreen(&sc, mMainDisplay);
- sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
- sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
- ScreenCapture::captureScreen(&sc, mVirtualDisplay);
- sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
- sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-}
-
-class DisplayActiveConfigTest : public ::testing::Test {
-protected:
- void SetUp() override {
- mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
- SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
- EXPECT_GT(mDisplayconfigs.size(), 0);
-
- // set display power to on to make sure config can be changed
- SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
- }
-
- sp<IBinder> mDisplayToken;
- Vector<DisplayInfo> mDisplayconfigs;
-};
-
-TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
- std::vector<int32_t> allowedConfigs;
-
- // Add all configs to the allowed configs
- for (int i = 0; i < mDisplayconfigs.size(); i++) {
- allowedConfigs.push_back(i);
- }
-
- status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
- EXPECT_EQ(res, NO_ERROR);
-
- std::vector<int32_t> outConfigs;
- res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
- EXPECT_EQ(res, NO_ERROR);
- EXPECT_EQ(allowedConfigs, outConfigs);
-}
-
-TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
- // we need at least 2 configs available for this test
- if (mDisplayconfigs.size() <= 1) return;
-
- int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-
- // We want to set the allowed config to everything but the active config
- std::vector<int32_t> allowedConfigs;
- for (int i = 0; i < mDisplayconfigs.size(); i++) {
- if (i != activeConfig) {
- allowedConfigs.push_back(i);
- }
- }
-
- status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
- EXPECT_EQ(res, NO_ERROR);
-
- // Allow some time for the config change
- std::this_thread::sleep_for(200ms);
-
- int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
- EXPECT_NE(activeConfig, newActiveConfig);
-
- // Make sure the new config is part of allowed config
- EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
- allowedConfigs.end());
-}
-
-class RelativeZTest : public LayerTransactionTest {
-protected:
- virtual void SetUp() {
- LayerTransactionTest::SetUp();
- ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
-
- // Back layer
- mBackgroundLayer = createColorLayer("Background layer", Color::RED);
-
- // Front layer
- mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
-
- asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
- t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
- t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
- });
- }
-
- virtual void TearDown() {
- LayerTransactionTest::TearDown();
- mBackgroundLayer = 0;
- mForegroundLayer = 0;
- }
-
- sp<SurfaceControl> mBackgroundLayer;
- sp<SurfaceControl> mForegroundLayer;
-};
-
-// When a layer is reparented offscreen, remove relative z order if the relative parent
-// is still onscreen so that the layer is not drawn.
-TEST_F(RelativeZTest, LayerRemoved) {
- std::unique_ptr<ScreenCapture> sc;
-
- // Background layer (RED)
- // Child layer (WHITE) (relative to foregroud layer)
- // Foregroud layer (GREEN)
- sp<SurfaceControl> childLayer =
- createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
-
- Transaction{}
- .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
- .show(childLayer)
- .apply();
-
- {
- // The childLayer should be in front of the FG control.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
- }
-
- // Background layer (RED)
- // Foregroud layer (GREEN)
- Transaction{}.reparent(childLayer, nullptr).apply();
-
- // Background layer (RED)
- // Child layer (WHITE)
- // Foregroud layer (GREEN)
- Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
-
- {
- // The relative z info for child layer should be reset, leaving FG control on top.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
- }
-}
-
-// When a layer is reparented offscreen, preseve relative z order if the relative parent
-// is also offscreen. Regression test b/132613412
-TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
- std::unique_ptr<ScreenCapture> sc;
-
- // Background layer (RED)
- // Foregroud layer (GREEN)
- // child level 1 (WHITE)
- // child level 2a (BLUE)
- // child level 3 (GREEN) (relative to child level 2b)
- // child level 2b (BLACK)
- sp<SurfaceControl> childLevel1 =
- createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
- sp<SurfaceControl> childLevel2a =
- createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
- sp<SurfaceControl> childLevel2b =
- createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
- sp<SurfaceControl> childLevel3 =
- createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
-
- Transaction{}
- .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
- .show(childLevel2a)
- .show(childLevel2b)
- .show(childLevel3)
- .apply();
-
- {
- // The childLevel3 should be in front of childLevel2b.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
- }
-
- // Background layer (RED)
- // Foregroud layer (GREEN)
- Transaction{}.reparent(childLevel1, nullptr).apply();
-
- // Background layer (RED)
- // Foregroud layer (GREEN)
- // child level 1 (WHITE)
- // child level 2 back (BLUE)
- // child level 3 (GREEN) (relative to child level 2b)
- // child level 2 front (BLACK)
- Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
-
- {
- // Nothing should change at this point since relative z info was preserved.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
- }
-}
-
-// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
-// the dropped buffer's damage region into the next buffer's damage region. If
-// we don't do this, we'll report an incorrect damage region to hardware
-// composer, resulting in broken rendering. This test checks the BufferQueue
-// case.
-//
-// Unfortunately, we don't currently have a way to inspect the damage region
-// SurfaceFlinger sends to hardware composer from a test, so this test requires
-// the dev to manually watch the device's screen during the test to spot broken
-// rendering. Because the results can't be automatically verified, this test is
-// marked disabled.
-TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
- const int width = mDisplayWidth;
- const int height = mDisplayHeight;
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
- const auto producer = layer->getIGraphicBufferProducer();
- const sp<IProducerListener> dummyListener(new DummyProducerListener);
- IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
- ASSERT_EQ(OK,
- producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
-
- std::map<int, sp<GraphicBuffer>> slotMap;
- auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
- ASSERT_NE(nullptr, buf);
- const auto iter = slotMap.find(slot);
- ASSERT_NE(slotMap.end(), iter);
- *buf = iter->second;
- };
-
- auto dequeue = [&](int* outSlot) {
- ASSERT_NE(nullptr, outSlot);
- *outSlot = -1;
- int slot;
- sp<Fence> fence;
- uint64_t age;
- FrameEventHistoryDelta timestamps;
- const status_t dequeueResult =
- producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
- &age, ×tamps);
- if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
- sp<GraphicBuffer> newBuf;
- ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
- ASSERT_NE(nullptr, newBuf.get());
- slotMap[slot] = newBuf;
- } else {
- ASSERT_EQ(OK, dequeueResult);
- }
- *outSlot = slot;
- };
-
- auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
- IGraphicBufferProducer::QueueBufferInput input(
- /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
- /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
- /*transform=*/0, Fence::NO_FENCE);
- input.setSurfaceDamage(damage);
- IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
- };
-
- auto fillAndPostBuffers = [&](const Color& color) {
- int slot1;
- ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
- int slot2;
- ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
-
- sp<GraphicBuffer> buf1;
- ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
- sp<GraphicBuffer> buf2;
- ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
- TransactionUtils::fillGraphicBufferColor(buf1, Rect(width, height), color);
- TransactionUtils::fillGraphicBufferColor(buf2, Rect(width, height), color);
-
- const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
- ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
- ASSERT_NO_FATAL_FAILURE(
- queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
- displayTime));
- };
-
- const auto startTime = systemTime();
- const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
- int colorIndex = 0;
- while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
- ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
- std::this_thread::sleep_for(1s);
- }
-
- ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 51956ec..4d21468 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -167,7 +167,9 @@
}
// TODO: Try registering the mock as the default service instead.
property_set("debug.sf.hwc_service_name", "mock");
- // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+
+ // This allows tests/SF to register/load a HIDL service not listed in manifest files.
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
property_set("debug.sf.treble_testing_override", "true");
}
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 093bcf5..67faa57 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -127,7 +127,6 @@
public:
class MockComposerClient : public FakeComposerClient {
public:
- MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
MOCK_METHOD4(getDisplayAttribute,
Error(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue));
@@ -171,14 +170,11 @@
mMockComposer = new MockComposerClient;
sp<ComposerClient> client = new ComposerClient(mMockComposer);
mFakeService = new FakeComposerService(client);
- (void)mFakeService->registerAsService("mock");
+ ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
android::hardware::ProcessState::self()->startThreadPool();
android::ProcessState::self()->startThreadPool();
- EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
- .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
- Return(Error::NONE)));
// Primary display will be queried twice for all 5 attributes. One
// set of queries comes from the SurfaceFlinger proper an the
// other set from the VR composer.
@@ -270,10 +266,6 @@
TEST_F(DisplayTest, Hotplug) {
ALOGD("DisplayTest::Hotplug");
- EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
- Return(Error::NONE)));
// The attribute queries will get done twice. This is for defaults
EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
.Times(2 * 3)
@@ -381,10 +373,6 @@
mMockComposer->clearFrames();
- EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
- Return(Error::NONE)));
// The attribute queries will get done twice. This is for defaults
EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
.Times(2 * 3)
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 20dfed6..8aff096 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -812,14 +812,10 @@
EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
.WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
- std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers;
- outputLayers.emplace_back(test->mDisplay->getCompositionDisplay()
- ->createOutputLayer(layer->getCompositionLayer(), layer));
-
- outputLayers.back()->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
- outputLayers.back()->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
-
- test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers));
+ auto outputLayer = test->mDisplay->getCompositionDisplay()
+ ->injectOutputLayerForTest(layer->getCompositionLayer(), layer);
+ outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
+ outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
Mock::VerifyAndClear(test->mComposer);
@@ -830,8 +826,7 @@
EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
.WillOnce(Return(Error::NONE));
- test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(
- std::vector<std::unique_ptr<compositionengine::OutputLayer>>());
+ test->mDisplay->getCompositionDisplay()->clearOutputLayers();
test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
}
};
@@ -883,11 +878,14 @@
FlingerLayerType layer =
Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
- return new BufferQueueLayer(
- LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
- String8("test-layer"), LayerProperties::WIDTH,
- LayerProperties::HEIGHT,
- LayerProperties::LAYER_FLAGS, LayerMetadata()));
+ sp<Client> client;
+ String8 name("test-layer");
+ LayerCreationArgs args =
+ LayerCreationArgs(test->mFlinger.mFlinger.get(), client, name,
+ LayerProperties::WIDTH, LayerProperties::HEIGHT,
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ args.textureName = test->mFlinger.mutableTexturePool().back();
+ return new BufferQueueLayer(args);
});
LayerProperties::setupLayerState(test, layer);
@@ -1041,6 +1039,26 @@
static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
};
+struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant {
+ static void setupLayerState(CompositionTest* test, sp<Layer>) {
+ test->mFlinger.mutableDebugDisableHWC() = true;
+ }
+
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest* test) {
+ Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+ Case::Display::setupHwcForcedClientCompositionCallExpectations(test);
+ Case::Display::setupRECompositionCallExpectations(test);
+ Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
+ }
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
struct EmptyScreenshotResultVariant {
static void setupLayerState(CompositionTest*, sp<Layer>) {}
@@ -1352,5 +1370,23 @@
NoCompositionTypeVariant, REScreenshotResultVariant>>();
}
+/* ------------------------------------------------------------------------
+ * Client composition forced through debug/developer settings
+ */
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index fcce57b..ee1f3aa 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -445,10 +445,6 @@
}
static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>(
- HWC_DISPLAY_TYPE)),
- Return(Error::NONE)));
EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index dbd9b84..2662f52 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -42,6 +42,8 @@
class MockVSyncSource : public VSyncSource {
public:
+ const char* getName() const override { return "test"; }
+
MOCK_METHOD1(setVSyncEnabled, void(bool));
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
@@ -54,8 +56,7 @@
protected:
class MockEventThreadConnection : public EventThreadConnection {
public:
- MockEventThreadConnection(android::impl::EventThread* eventThread,
- ResyncCallback&& resyncCallback,
+ MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
: EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
@@ -67,7 +68,7 @@
EventThreadTest();
~EventThreadTest() override;
- void createThread();
+ void createThread(std::unique_ptr<VSyncSource>);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
ISurfaceComposer::ConfigChanged configChanged);
@@ -91,9 +92,9 @@
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
- MockVSyncSource mVSyncSource;
+ MockVSyncSource* mVSyncSource;
VSyncSource::Callback* mCallback = nullptr;
- std::unique_ptr<android::impl::EventThread> mThread;
+ std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
};
@@ -102,16 +103,19 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+ auto vsyncSource = std::make_unique<MockVSyncSource>();
+ mVSyncSource = vsyncSource.get();
+
+ EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
.WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
- EXPECT_CALL(mVSyncSource, setCallback(_))
+ EXPECT_CALL(*mVSyncSource, setCallback(_))
.WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
- EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+ EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
.WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
- createThread();
+ createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
ISurfaceComposer::eConfigChangedDispatch);
@@ -129,11 +133,9 @@
EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
}
-void EventThreadTest::createThread() {
- mThread =
- std::make_unique<android::impl::EventThread>(&mVSyncSource,
- mInterceptVSyncCallRecorder.getInvocable(),
- "unit-test-event-thread");
+void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+ mThread = std::make_unique<impl::EventThread>(std::move(source),
+ mInterceptVSyncCallRecorder.getInvocable());
// EventThread should register itself as VSyncSource callback.
mCallback = expectVSyncSetCallbackCallReceived();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index bf53124..b4cc1e1 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -92,7 +92,6 @@
ISurfaceComposer::
eConfigChangedSuppress));
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler->getEventThread(handle));
EXPECT_FALSE(mScheduler->getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
@@ -121,8 +120,6 @@
ISurfaceComposer::
eConfigChangedSuppress));
ASSERT_EQ(mEventThreadConnection, connection);
-
- EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle));
EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b77f82a..926b8ac 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -356,6 +356,7 @@
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
+ auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 3c7e1da..98c6aa0 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -41,7 +41,7 @@
using android::hardware::graphics::composer::V2_1::IComposer;
using android::hardware::graphics::composer::V2_1::IComposerCallback;
using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
+using android::hardware::graphics::composer::V2_4::IComposerClient;
class Composer : public Hwc2::Composer {
public:
@@ -71,7 +71,6 @@
MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
MOCK_METHOD4(getDisplayRequests,
Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
- MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*));
MOCK_METHOD2(getDozeSupport, Error(Display, bool*));
MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*));
MOCK_METHOD1(getPerFrameMetadataKeys,
@@ -118,10 +117,11 @@
MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t));
MOCK_METHOD4(getDisplayedContentSample,
Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
- MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+ MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+ MOCK_METHOD2(getDisplayConnectionType, Error(Display, IComposerClient::DisplayConnectionType*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 1b1c1a7..e781c0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,6 @@
~MessageQueue() override;
MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
- MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
MOCK_METHOD0(waitMessage, void());
MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 83a5250..5686891 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -46,6 +46,7 @@
"-Wno-padded",
"-Wno-switch-enum",
"-Wno-undef",
+ "-Wno-format-pedantic",
// Have clang emit complete debug_info.
"-fstandalone-debug",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9a670f6..b413ac9 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1313,5 +1313,16 @@
return result;
}
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence) {
+ ATRACE_CALL();
+
+ const auto& data = GetData(queue);
+
+ return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 57c956d..f058c47 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -131,6 +131,7 @@
VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
// clang-format on
template <typename DispatchableType>
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 3495861..d829e41 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -444,6 +444,13 @@
nullptr,
},
{
+ "vkQueueSubmit",
+ ProcHook::DEVICE,
+ ProcHook::EXTENSION_CORE,
+ reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit),
+ nullptr,
+ },
+ {
"vkSetHdrMetadataEXT",
ProcHook::DEVICE,
ProcHook::EXT_hdr_metadata,
@@ -537,6 +544,7 @@
INIT_PROC(true, dev, GetDeviceProcAddr);
INIT_PROC(true, dev, DestroyDevice);
INIT_PROC(true, dev, GetDeviceQueue);
+ INIT_PROC(true, dev, QueueSubmit);
INIT_PROC(true, dev, CreateImage);
INIT_PROC(true, dev, DestroyImage);
INIT_PROC(true, dev, AllocateCommandBuffers);
@@ -544,9 +552,9 @@
INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
- INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
// clang-format on
return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 79f070c..fb2f257 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -84,6 +84,7 @@
PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
PFN_vkDestroyDevice DestroyDevice;
PFN_vkGetDeviceQueue GetDeviceQueue;
+ PFN_vkQueueSubmit QueueSubmit;
PFN_vkCreateImage CreateImage;
PFN_vkDestroyImage DestroyImage;
PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
@@ -91,9 +92,9 @@
PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
+ PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
- PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
// clang-format on
};
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 7c9b0c0..b8d7d2b 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -16,10 +16,10 @@
// WARNING: This file is generated. See ../README.md for instructions.
-#include "null_driver_gen.h"
-
#include <algorithm>
+#include "null_driver_gen.h"
+
using namespace null_driver;
namespace {
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 70ef340..668dc7d 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -204,9 +204,9 @@
VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
-VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 04d9f23..ef36f8c 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -91,6 +91,8 @@
'vkGetInstanceProcAddr' : True,
'vkGetDeviceProcAddr' : True,
+ 'vkQueueSubmit' : True,
+
# VK_KHR_swapchain->VK_ANDROID_native_buffer translation
'vkCreateImage' : True,
'vkDestroyImage' : True,
@@ -191,6 +193,8 @@
'vkGetInstanceProcAddr' : True,
'vkGetDeviceProcAddr' : True,
+ 'vkQueueSubmit' : True,
+
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2' : True,
'vkBindImageMemory2KHR' : True
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index d9f97e1..fe9dab4 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -127,7 +127,7 @@
'VkCommandBuffer ' : 'Device'
}
- if len(paramDict[functionName])>0:
+ if len(paramDict[functionName]) > 0:
return switchCase.get(paramDict[functionName][0][0], 'Global')
return 'Global'
@@ -150,7 +150,7 @@
def clang_off(f, indent):
f.write (clang_off_spaces * indent + '// clang-format off\n')
-clang_off_spaces = ' '*4
+clang_off_spaces = ' ' * 4
parametersList = []
paramDict = {}
@@ -166,16 +166,13 @@
vulkan_registry = os.path.join(os.path.dirname(__file__),'..','..','..','..','external','vulkan-headers','registry','vk.xml')
tree = ET.parse(vulkan_registry)
root = tree.getroot()
- protoset = False
- fnName = ""
- fnType = ""
for commands in root.iter('commands'):
for command in commands:
if command.tag == 'command':
- if protoset == True:
- paramDict[fnName] = parametersList.copy()
parametersList.clear()
protoset = False
+ fnName = ""
+ fnType = ""
if command.get('alias') != None:
alias = command.get('alias')
fnName = command.get('name')
@@ -184,20 +181,20 @@
paramDict[fnName] = paramDict[alias].copy()
returnTypeDict[fnName] = returnTypeDict[alias]
for params in command:
- if(params.tag == 'param'):
+ if params.tag == 'param':
paramtype = ""
- if params.text!=None:
- paramtype = params.text
+ if params.text != None and params.text.strip() != '':
+ paramtype = params.text.strip() + ' '
typeval = params.find('type')
paramtype = paramtype + typeval.text
- if typeval.tail!=None:
- paramtype = paramtype + typeval.tail
+ if typeval.tail != None:
+ paramtype += typeval.tail.strip() + ' '
pname = params.find('name')
paramname = pname.text
- if pname.tail != None:
- parametersList.append((paramtype,paramname,pname.tail))
+ if pname.tail != None and pname.tail.strip() != '':
+ parametersList.append((paramtype, paramname, pname.tail.strip()))
else:
- parametersList.append((paramtype,paramname))
+ parametersList.append((paramtype, paramname))
if params.tag == 'proto':
for c in params:
if c.tag == 'type':
@@ -207,6 +204,8 @@
protoset = True
allCommandsList.append(fnName)
returnTypeDict[fnName] = fnType
+ if protoset == True:
+ paramDict[fnName] = parametersList.copy()
for exts in root.iter('extensions'):
for extension in exts:
@@ -214,7 +213,7 @@
if extension.tag == 'extension':
extname = extension.get('name')
for req in extension:
- if req.get('feature')!=None:
+ if req.get('feature') != None:
apiversion = req.get('feature')
for commands in req:
if commands.tag == 'command':
@@ -224,19 +223,6 @@
if apiversion != "":
versionDict[commandname] = apiversion
- # TODO(adsrini): http://b/136570819
- extensionsDict['vkGetSwapchainGrallocUsage2ANDROID'] = 'VK_ANDROID_native_buffer'
- allCommandsList.append('vkGetSwapchainGrallocUsage2ANDROID')
- returnTypeDict['vkGetSwapchainGrallocUsage2ANDROID'] = 'VkResult'
- paramDict['vkGetSwapchainGrallocUsage2ANDROID'] = [
- ('VkDevice ', 'device'),
- ('VkFormat ', 'format'),
- ('VkImageUsageFlags ', 'imageUsage'),
- ('VkSwapchainImageUsageFlagsANDROID ', 'swapchainImageUsage'),
- ('uint64_t* ', 'grallocConsumerUsage'),
- ('uint64_t* ', 'grallocProducerUsage')
- ]
-
for feature in root.iter('feature'):
apiversion = feature.get('name')
for req in feature: