Merge "Revert "Don't force client composition when rounded corners are cached"" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 07d16f7..a4a22a0 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -22,5 +22,21 @@
{
"name": "SurfaceFlinger_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "SurfaceFlinger_test",
+ "keywords": [ "primary-device" ],
+ "options": [
+ // TODO(b/328119950) Known to be broken.
+ {
+ "exclude-filter": "LayerCallbackTest#SetNullBuffer"
+ },
+ // TODO(b/398306512) Flaky on real device.
+ {
+ "exclude-filter": "LayerRenderTypeTransactionTests/LayerRenderTypeTransactionTest#SetRelativeZBasic_BufferQueue/*"
+ }
+ ]
+ }
]
}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a5d176d..fdb032b 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -117,6 +117,7 @@
"libdumpsys",
"libserviceutils",
"android.tracing.flags_c_lib",
+ "perfetto_flags_c_lib",
],
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 888fb67..9e3e2b0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -57,6 +57,7 @@
#include <log/log_read.h>
#include <math.h>
#include <openssl/sha.h>
+#include <perfetto_flags.h>
#include <poll.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -190,7 +191,7 @@
#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
#define LINKERCONFIG_DIR "/linkerconfig"
#define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
-#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
+#define SYSTEM_TRACE_DIR "/data/misc/perfetto-traces/bugreport"
#define CGROUPFS_DIR "/sys/fs/cgroup"
#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
#define DROPBOX_DIR "/data/system/dropbox"
@@ -359,6 +360,31 @@
return CopyFileToFd(input_file, out_fd.get());
}
+template <typename Func>
+size_t ForEachTrace(Func func) {
+ std::unique_ptr<DIR, decltype(&closedir)> traces_dir(opendir(SYSTEM_TRACE_DIR), closedir);
+
+ if (traces_dir == nullptr) {
+ MYLOGW("Unable to open directory %s: %s\n", SYSTEM_TRACE_DIR, strerror(errno));
+ return 0;
+ }
+
+ size_t traces_found = 0;
+ struct dirent* entry = nullptr;
+ while ((entry = readdir(traces_dir.get()))) {
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+ std::string trace_path = std::string(SYSTEM_TRACE_DIR) + "/" + entry->d_name;
+ if (access(trace_path.c_str(), F_OK) != 0) {
+ continue;
+ }
+ ++traces_found;
+ func(trace_path);
+ }
+ return traces_found;
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -1101,20 +1127,16 @@
// This function copies into the .zip the system trace that was snapshotted
// by the early call to MaybeSnapshotSystemTraceAsync(), if any background
// tracing was happening.
- bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
- if (!system_trace_exists) {
- // No background trace was happening at the time MaybeSnapshotSystemTraceAsync() was invoked
- if (!PropertiesHelper::IsUserBuild()) {
- MYLOGI(
- "No system traces found. Check for previously uploaded traces by looking for "
- "go/trace-uuid in logcat")
- }
- return;
+ size_t traces_found = android::os::ForEachTrace([&](const std::string& trace_path) {
+ ds.AddZipEntry(ZIP_ROOT_DIR + trace_path, trace_path);
+ android::os::UnlinkAndLogOnError(trace_path);
+ });
+
+ if (traces_found == 0 && !PropertiesHelper::IsUserBuild()) {
+ MYLOGI(
+ "No system traces found. Check for previously uploaded traces by looking for "
+ "go/trace-uuid in logcat")
}
- ds.AddZipEntry(
- ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT,
- SYSTEM_TRACE_SNAPSHOT);
- android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT);
}
static void DumpVisibleWindowViews() {
@@ -3412,8 +3434,8 @@
// duration is logged into MYLOG instead.
PrintHeader();
- bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
- if (options_->use_predumped_ui_data && !system_trace_exists) {
+ size_t trace_count = android::os::ForEachTrace([](const std::string&) {});
+ if (options_->use_predumped_ui_data && trace_count == 0) {
MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
options_->use_predumped_ui_data = false;
}
@@ -3560,20 +3582,24 @@
}
// If a stale file exists already, remove it.
- unlink(SYSTEM_TRACE_SNAPSHOT);
+ android::os::ForEachTrace([&](const std::string& trace_path) { unlink(trace_path.c_str()); });
MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str())
+
return std::async(
std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] {
- // If a background system trace is happening and is marked as "suitable for
- // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
- // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
- // case that no trace is ongoing, this command is a no-op.
+ // If one or more background system traces are happening and are marked as
+ // "suitable for bugreport" (bugreport_score > 0 in the trace config), this command
+ // will snapshot them into SYSTEM_TRACE_DIR.
+ // In the (likely) case that no trace is ongoing, this command is a no-op.
// Note: this should not be enqueued as we need to freeze the trace before
// dumpstate starts. Otherwise the trace ring buffers will contain mostly
// the dumpstate's own activity which is irrelevant.
+ const char* cmd_arg = perfetto::flags::save_all_traces_in_bugreport()
+ ? "--save-all-for-bugreport"
+ : "--save-for-bugreport";
RunCommand(
- SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"},
+ SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", cmd_arg},
CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
false, outFd);
// MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
index 0aff200..7e3307d 100644
--- a/cmds/dumpstate/dumpstate_smoke_test.xml
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -22,7 +22,9 @@
<option name="cleanup" value="true" />
<option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
</target_preparer>
-
+ <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
+ <option name="flag-value" value="perfetto/perfetto.flags.save_all_traces_in_bugreport=true" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="dumpstate_smoke_test" />
diff --git a/cmds/dumpstate/res/default_screenshot.png b/cmds/dumpstate/res/default_screenshot.png
index 10f36aa..1e14306 100644
--- a/cmds/dumpstate/res/default_screenshot.png
+++ b/cmds/dumpstate/res/default_screenshot.png
Binary files differ
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index a29923a..c72847c 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -24,8 +24,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libgen.h>
+#include <signal.h>
#include <ziparchive/zip_archive.h>
+#include <cstdio>
#include <fstream>
#include <regex>
@@ -603,6 +605,93 @@
listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
}
+class DumpstateTracingTest : public Test {
+ protected:
+ void TearDown() override {
+ for (int pid : bg_process_pids) {
+ kill(pid, SIGKILL);
+ }
+ }
+
+ void StartTracing(const std::string& config) {
+ // Write the perfetto config into a file.
+ const int id = static_cast<int>(bg_process_pids.size());
+ char cfg[64];
+ snprintf(cfg, sizeof(cfg), "/data/misc/perfetto-configs/br-%d", id);
+ unlink(cfg); // Remove the config file if it exists already.
+ FILE* f = fopen(cfg, "w");
+ ASSERT_NE(f, nullptr);
+ fputs(config.c_str(), f);
+ fclose(f);
+
+ // Invoke perfetto to start tracing.
+ char cmd[255];
+ snprintf(cmd, sizeof(cmd), "perfetto --background-wait --txt -o /dev/null -c %s", cfg);
+ FILE* proc = popen(cmd, "r");
+ ASSERT_NE(proc, nullptr);
+
+ // Read back the PID of the background process. We will use it to kill
+ // all tracing sessions when the test ends or fails.
+ char pid_str[32]{};
+ ASSERT_NE(fgets(pid_str, sizeof(pid_str), proc), nullptr);
+ int pid = atoi(pid_str);
+ bg_process_pids.push_back(pid);
+
+ pclose(proc);
+ unlink(cfg);
+ }
+
+ std::vector<int> bg_process_pids;
+};
+
+TEST_F(DumpstateTracingTest, ManyTracesInBugreport) {
+ // Note the trace duration is irrelevant and is only an upper bound.
+ // Tracing is stopped as soon as the bugreport.zip creation ends.
+ StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+ config {
+ name: "linux.ftrace"
+ }
+}
+
+duration_ms: 120000
+bugreport_filename: "sys.pftrace"
+bugreport_score: 100
+)");
+
+ StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+ config {
+ name: "linux.ftrace"
+ }
+}
+
+duration_ms: 120000
+bugreport_score: 50
+bugreport_filename: "mem.pftrace"
+)");
+
+ ZippedBugreportGenerationTest::GenerateBugreport();
+ std::string zip_path = ZippedBugreportGenerationTest::getZipFilePath();
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
+
+ const char* kExpectedEntries[]{
+ "FS/data/misc/perfetto-traces/bugreport/sys.pftrace",
+ "FS/data/misc/perfetto-traces/bugreport/mem.pftrace",
+ };
+
+ // Check that the bugreport contains both traces.
+ for (const char* file_path : kExpectedEntries) {
+ ZipEntry entry{};
+ GetEntry(handle, file_path, &entry);
+ EXPECT_GT(entry.uncompressed_length, 100);
+ }
+ CloseArchive(handle);
+}
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 77e7328..6e6d27d 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -202,26 +202,13 @@
}
bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
- sp<GLConsumer>* glConsumer, EGLSurface* surface) {
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true);
+ sp<GLConsumer>* glConsumer, EGLSurface* surface) {
+ auto [glc, surf] = GLConsumer::create(name, GL_TEXTURE_EXTERNAL_OES, false, true);
glc->setDefaultBufferSize(w, h);
- glc->getSurface()->setMaxDequeuedBufferCount(2);
glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+ surf->setMaxDequeuedBufferCount(2);
+ sp<ANativeWindow> anw = surf;
- sp<ANativeWindow> anw = glc->getSurface();
-#else
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<GLConsumer> glc = new GLConsumer(consumer, name,
- GL_TEXTURE_EXTERNAL_OES, false, true);
- glc->setDefaultBufferSize(w, h);
- producer->setMaxDequeuedBufferCount(2);
- glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
-
- sp<ANativeWindow> anw = new Surface(producer);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr);
if (s == EGL_NO_SURFACE) {
fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 36dcbca..b87ef2d 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -25,13 +25,8 @@
name: "idlcli-defaults",
shared_libs: [
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
"libbase",
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libutils",
],
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index 262f2e5..dc52c57 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -18,7 +18,6 @@
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#include <android/binder_enums.h>
-#include <hidl/HidlSupport.h>
#include <iomanip>
#include <iostream>
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index b943495..1a9993e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -22,102 +22,30 @@
#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "IdlCli.h"
#include "utils.h"
namespace android {
-using hardware::Return;
+using ::aidl::android::hardware::vibrator::IVibrator;
using idlcli::IdlCli;
-static constexpr int NUM_TRIES = 2;
-
-// Creates a Return<R> with STATUS::EX_NULL_POINTER.
-template <class R>
-inline R NullptrStatus() {
- using ::android::hardware::Status;
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
-}
-
-template <>
-inline ndk::ScopedAStatus NullptrStatus() {
- return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
-}
-
-template <typename I>
inline auto getService(std::string name) {
- const auto instance = std::string() + I::descriptor + "/" + name;
+ const auto instance = std::string() + IVibrator::descriptor + "/" + name;
auto vibBinder = ndk::SpAIBinder(AServiceManager_checkService(instance.c_str()));
- return I::fromBinder(vibBinder);
+ return IVibrator::fromBinder(vibBinder);
}
-template <>
-inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) {
- return android::hardware::vibrator::V1_0::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) {
- return android::hardware::vibrator::V1_1::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) {
- return android::hardware::vibrator::V1_2::IVibrator::getService(name);
-}
-
-template <>
-inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) {
- return android::hardware::vibrator::V1_3::IVibrator::getService(name);
-}
-
-template <typename I>
-using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>;
-
-template <typename I>
-class HalWrapper {
-public:
- static std::unique_ptr<HalWrapper> Create() {
- // Assume that if getService returns a nullptr, HAL is not available on the
- // device.
- const auto name = IdlCli::Get().getName();
- auto hal = getService<I>(name.empty() ? "default" : name);
- return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
- }
-
- template <class R, class... Args0, class... Args1>
- R call(R (I::*fn)(Args0...), Args1&&... args1) {
- return (*mHal.*fn)(std::forward<Args1>(args1)...);
- }
-
-private:
- HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
-
-private:
- shared_ptr<I> mHal;
-};
-
-template <typename I>
static auto getHal() {
- static auto sHalWrapper = HalWrapper<I>::Create();
- return sHalWrapper.get();
-}
-
-template <class R, class I, class... Args0, class... Args1>
-R halCall(R (I::*fn)(Args0...), Args1&&... args1) {
- auto hal = getHal<I>();
- return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
+ // Assume that if getService returns a nullptr, HAL is not available on the device.
+ const auto name = IdlCli::Get().getName();
+ return getService(name.empty() ? "default" : name);
}
namespace idlcli {
namespace vibrator {
-namespace V1_0 = ::android::hardware::vibrator::V1_0;
-namespace V1_1 = ::android::hardware::vibrator::V1_1;
-namespace V1_2 = ::android::hardware::vibrator::V1_2;
-namespace V1_3 = ::android::hardware::vibrator::V1_3;
namespace aidl = ::aidl::android::hardware::vibrator;
class VibratorCallback : public aidl::BnVibratorCallback {
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
index 9afa300..cae6909 100644
--- a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -51,21 +51,17 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
-
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ auto status = hal->alwaysOnDisable(mId);
- return ret;
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
int32_t mId;
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
index bb7f9f2..410ca52 100644
--- a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -72,21 +72,17 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
-
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ auto status = hal->alwaysOnEnable(mId, mEffect, mStrength);
- return ret;
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
int32_t mId;
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index eb9008b..41acb98 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -89,7 +89,7 @@
}
Status doMain(Args && /*args*/) override {
- auto hal = getHal<aidl::IVibrator>();
+ auto hal = getHal();
if (!hal) {
return UNAVAILABLE;
@@ -104,7 +104,7 @@
callback = ndk::SharedRefBase::make<VibratorCallback>();
}
- auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+ auto status = hal->compose(mComposite, callback);
if (status.isOk() && callback) {
callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp
index b8308ce..5f6bf86 100644
--- a/cmds/idlcli/vibrator/CommandComposePwle.cpp
+++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp
@@ -163,7 +163,7 @@
}
Status doMain(Args && /*args*/) override {
- auto hal = getHal<aidl::IVibrator>();
+ auto hal = getHal();
if (!hal) {
return UNAVAILABLE;
@@ -178,7 +178,7 @@
callback = ndk::SharedRefBase::make<VibratorCallback>();
}
- auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback);
+ auto status = hal->composePwle(mCompositePwle, callback);
if (status.isOk() && callback) {
callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
index 6d3cf84..bd682ea 100644
--- a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
+++ b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
@@ -108,7 +108,7 @@
}
Status doMain(Args&& /*args*/) override {
- auto hal = getHal<aidl::IVibrator>();
+ auto hal = getHal();
if (!hal) {
return UNAVAILABLE;
@@ -123,7 +123,7 @@
callback = ndk::SharedRefBase::make<VibratorCallback>();
}
- auto status = hal->call(&aidl::IVibrator::composePwleV2, mCompositePwle, callback);
+ auto status = hal->composePwleV2(mCompositePwle, callback);
if (status.isOk() && callback) {
callback->waitForComplete();
diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
index aa01a11..44115e9 100644
--- a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
+++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp
@@ -44,29 +44,38 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- std::vector<float> bandwidthAmplitude;
- float frequencyMinimumHz;
- float frequencyResolutionHz;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status =
- hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude);
- statusStr = status.getDescription();
- ret = (status.isOk() ? OK : ERROR);
-
- status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
- ret = (status.isOk() ? OK : ERROR);
-
- status =
- hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
- ret = (status.isOk() ? OK : ERROR);
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<float> bandwidthAmplitude;
+ float frequencyMinimumHz;
+ float frequencyResolutionHz;
+
+ auto status = hal->getBandwidthAmplitudeMap(&bandwidthAmplitude);
+
+ if (!status.isOk()) {
+ std::cout << "Status: " << status.getDescription() << std::endl;
+ return ERROR;
+ }
+
+ status = hal->getFrequencyMinimum(&frequencyMinimumHz);
+
+ if (!status.isOk()) {
+ std::cout << "Status: " << status.getDescription() << std::endl;
+ return ERROR;
+ }
+
+ status = hal->getFrequencyResolution(&frequencyResolutionHz);
+
+ if (!status.isOk()) {
+ std::cout << "Status: " << status.getDescription() << std::endl;
+ return ERROR;
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Bandwidth Amplitude Map: " << std::endl;
float frequency = frequencyMinimumHz;
for (auto &e : bandwidthAmplitude) {
@@ -74,7 +83,7 @@
frequency += frequencyResolutionHz;
}
- return ret;
+ return OK;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 303a989..507d871 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -42,22 +42,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t cap;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t cap;
+ auto status = hal->getCapabilities(&cap);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index 10508bd..1c1eb3c 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t maxDelayMs;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxDelayMs;
+ auto status = hal->getCompositionDelayMax(&maxDelayMs);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 900cb18..cfd4c53 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t maxSize;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxSize;
+ auto status = hal->getCompositionSizeMax(&maxSize);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Max Size: " << maxSize << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
index 504c648..2a61446 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- float frequencyMinimumHz;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ float frequencyMinimumHz;
+ auto status = hal->getFrequencyMinimum(&frequencyMinimumHz);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
index de35838..157d6bf 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp
@@ -44,23 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- float frequencyResolutionHz;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status =
- hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ float frequencyResolutionHz;
+ auto status = hal->getFrequencyResolution(&frequencyResolutionHz);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
index 2edd0ca..2eb4510 100644
--- a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
@@ -46,26 +46,22 @@
}
Status doMain(Args&& /*args*/) override {
- std::string statusStr;
- std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getFrequencyToOutputAccelerationMap,
- &frequencyToOutputAccelerationMap);
- statusStr = status.getDescription();
- ret = (status.isOk() ? OK : ERROR);
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
+ auto status = hal->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Frequency to Output Amplitude Map: " << std::endl;
for (auto& entry : frequencyToOutputAccelerationMap) {
std::cout << entry.frequencyHz << " " << entry.maxOutputAccelerationGs << std::endl;
}
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
index 460d39e..c957f6b 100644
--- a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -57,22 +57,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t duration;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t duration;
+ auto status = hal->getPrimitiveDuration(mPrimitive, &duration);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Duration: " << duration << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
CompositePrimitive mPrimitive;
diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
index b2c3551..c1b0278 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t maxSize;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxSize;
+ auto status = hal->getPwleCompositionSizeMax(&maxSize);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Max Size: " << maxSize << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
index 9081973..ed00ba0 100644
--- a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- int32_t maxDurationMs;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxDurationMs;
+ auto status = hal->getPwlePrimitiveDurationMax(&maxDurationMs);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
index cca072c..f780b8b 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args&& /*args*/) override {
- std::string statusStr;
- int32_t maxSize;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPwleV2CompositionSizeMax, &maxSize);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxSize;
+ auto status = hal->getPwleV2CompositionSizeMax(&maxSize);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Max Size: " << maxSize << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
index dbbfe1a..e84e969 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
@@ -44,23 +44,19 @@
}
Status doMain(Args&& /*args*/) override {
- std::string statusStr;
- int32_t maxDurationMs;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMaxMillis,
- &maxDurationMs);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t maxDurationMs;
+ auto status = hal->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
index 09225c4..448fd2a 100644
--- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
@@ -44,23 +44,19 @@
}
Status doMain(Args&& /*args*/) override {
- std::string statusStr;
- int32_t minDurationMs;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMinMillis,
- &minDurationMs);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ int32_t minDurationMs;
+ auto status = hal->getPwleV2PrimitiveDurationMinMillis(&minDurationMs);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Primitive duration min: " << minDurationMs << " ms" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetQFactor.cpp b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
index a2681e9..e04bad9 100644
--- a/cmds/idlcli/vibrator/CommandGetQFactor.cpp
+++ b/cmds/idlcli/vibrator/CommandGetQFactor.cpp
@@ -42,22 +42,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- float qFactor;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getQFactor, &qFactor);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ float qFactor;
+ auto status = hal->getQFactor(&qFactor);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Q Factor: " << qFactor << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
index 81a6391..e222ea6 100644
--- a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
+++ b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp
@@ -44,22 +44,19 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- float resonantFrequencyHz;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getResonantFrequency, &resonantFrequencyHz);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ float resonantFrequencyHz;
+ auto status = hal->getResonantFrequency(&resonantFrequencyHz);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Resonant Frequency: " << resonantFrequencyHz << " Hz" << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
index edfcd91..9b05540 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -44,25 +44,22 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- std::vector<Effect> effects;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<Effect> effects;
+ auto status = hal->getSupportedAlwaysOnEffects(&effects);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Effects:" << std::endl;
for (auto &e : effects) {
std::cout << " " << toString(e) << std::endl;
}
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
index b326e07..f95f682 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp
@@ -44,25 +44,22 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- std::vector<Braking> braking;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<Braking> braking;
+ auto status = hal->getSupportedBraking(&braking);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Braking Mechanisms:" << std::endl;
for (auto &e : braking) {
std::cout << " " << toString(e) << std::endl;
}
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
index 7658f22..05de1b8 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -44,25 +44,22 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- std::vector<Effect> effects;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<Effect> effects;
+ auto status = hal->getSupportedEffects(&effects);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Effects:" << std::endl;
for (auto &e : effects) {
std::cout << " " << toString(e) << std::endl;
}
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
index d101681..0f33f0f 100644
--- a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -44,25 +44,22 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- std::vector<CompositePrimitive> primitives;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::vector<CompositePrimitive> primitives;
+ auto status = hal->getSupportedPrimitives(&primitives);
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Primitives:" << std::endl;
for (auto &e : primitives) {
std::cout << " " << toString(e) << std::endl;
}
- return ret;
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index cedb9fe..e55b44a 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -42,24 +42,17 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::off);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else if (auto hal = getHal<V1_0::IVibrator>()) {
- auto status = hal->call(&V1_0::IVibrator::off);
- statusStr = toString(status);
- ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ auto status = hal->off();
- return ret;
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 8212fc1..856c219 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -67,34 +67,27 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
- std::shared_ptr<VibratorCallback> callback;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- ABinderProcess_setThreadPoolMaxThreadCount(1);
- ABinderProcess_startThreadPool();
-
- int32_t cap;
- hal->call(&aidl::IVibrator::getCapabilities, &cap);
-
- if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
- callback = ndk::SharedRefBase::make<VibratorCallback>();
- }
-
- auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
-
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else if (auto hal = getHal<V1_0::IVibrator>()) {
- auto status = hal->call(&V1_0::IVibrator::on, mDuration);
- statusStr = toString(status);
- ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- if (ret == OK && mBlocking) {
+ std::shared_ptr<VibratorCallback> callback;
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->getCapabilities(&cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->on(mDuration, callback);
+
+ if (status.isOk() && mBlocking) {
if (callback) {
callback->waitForComplete();
} else {
@@ -102,9 +95,9 @@
}
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Status: " << status.getDescription() << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
bool mBlocking;
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index c897686..0a354e2 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -28,34 +28,6 @@
namespace vibrator {
-/*
- * The following static asserts are only relevant here because the argument
- * parser uses a single implementation for determining the string names.
- */
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
- static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
- static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
- static_cast<uint8_t>(aidl::EffectStrength::STRONG));
-static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
- static_cast<uint8_t>(aidl::Effect::CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
- static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD));
-static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP));
-static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
- static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
- static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
-
using aidl::Effect;
using aidl::EffectStrength;
@@ -107,61 +79,31 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- uint32_t lengthMs;
- Status ret;
- std::shared_ptr<VibratorCallback> callback;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- ABinderProcess_setThreadPoolMaxThreadCount(1);
- ABinderProcess_startThreadPool();
-
- int32_t cap;
- hal->call(&aidl::IVibrator::getCapabilities, &cap);
-
- if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
- callback = ndk::SharedRefBase::make<VibratorCallback>();
- }
-
- int32_t aidlLengthMs;
- auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
- &aidlLengthMs);
-
- statusStr = status.getDescription();
- lengthMs = static_cast<uint32_t>(aidlLengthMs);
- ret = status.isOk() ? OK : ERROR;
- } else {
- Return<void> hidlRet;
- V1_0::Status status;
- auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
- status = retStatus;
- lengthMs = retLengthMs;
- };
-
- if (auto hal = getHal<V1_3::IVibrator>()) {
- hidlRet =
- hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
- static_cast<V1_0::EffectStrength>(mStrength), callback);
- } else if (auto hal = getHal<V1_2::IVibrator>()) {
- hidlRet =
- hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
- static_cast<V1_0::EffectStrength>(mStrength), callback);
- } else if (auto hal = getHal<V1_1::IVibrator>()) {
- hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
- static_cast<V1_1::Effect_1_1>(mEffect),
- static_cast<V1_0::EffectStrength>(mStrength), callback);
- } else if (auto hal = getHal<V1_0::IVibrator>()) {
- hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
- static_cast<V1_0::EffectStrength>(mStrength), callback);
- } else {
- return UNAVAILABLE;
- }
-
- statusStr = toString(status);
- ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+ if (!hal) {
+ return UNAVAILABLE;
}
- if (ret == OK && mBlocking) {
+ uint32_t lengthMs;
+ std::shared_ptr<VibratorCallback> callback;
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->getCapabilities(&cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ int32_t aidlLengthMs;
+ auto status = hal->perform(mEffect, mStrength, callback, &aidlLengthMs);
+
+ lengthMs = static_cast<uint32_t>(aidlLengthMs);
+
+ if (status.isOk() && mBlocking) {
if (callback) {
callback->waitForComplete();
} else {
@@ -169,10 +111,10 @@
}
}
- std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Status: " << status.getDescription() << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
- return ret;
+ return status.isOk() ? OK : ERROR;
}
bool mBlocking;
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 8b8058c..8050723 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -50,25 +50,17 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::setAmplitude,
- static_cast<float>(mAmplitude) / UINT8_MAX);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else if (auto hal = getHal<V1_0::IVibrator>()) {
- auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
- statusStr = toString(status);
- ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ auto status = hal->setAmplitude(static_cast<float>(mAmplitude) / UINT8_MAX);
- return ret;
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
uint8_t mAmplitude;
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 1795793..8f8d4b7 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -48,24 +48,17 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
+ auto hal = getHal();
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else if (auto hal = getHal<V1_3::IVibrator>()) {
- auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
- statusStr = toString(status);
- ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
- } else {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ auto status = hal->setExternalControl(mEnable);
- return ret;
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
bool mEnable;
diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
index cdc529a..31ee954 100644
--- a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
@@ -42,15 +42,22 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl);
+ auto hal = getHal();
- if (!ret.isOk()) {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Result: " << std::boolalpha << ret << std::endl;
+ int32_t cap;
- return OK;
+ auto status = hal->getCapabilities(&cap);
+
+ bool hasAmplitudeControl = cap & IVibrator::CAP_AMPLITUDE_CONTROL;
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+ std::cout << "Result: " << std::boolalpha << hasAmplitudeControl << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
index ed15d76..f0c542c 100644
--- a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
@@ -42,15 +42,22 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_3::IVibrator::supportsExternalControl);
+ auto hal = getHal();
- if (!ret.isOk()) {
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Result: " << std::boolalpha << ret << std::endl;
+ int32_t cap;
- return OK;
+ auto status = hal->getCapabilities(&cap);
+
+ bool hasExternalControl = cap & IVibrator::CAP_EXTERNAL_CONTROL;
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+ std::cout << "Result: " << std::boolalpha << hasExternalControl << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
};
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index b7ad331..5690e2f 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -23,6 +23,9 @@
TARGET_SLOT="$1"
STATUS_FD="$2"
+# "1" if the script is triggered by the `UpdateEngine.triggerPostinstall` API. Empty otherwise.
+TRIGGERED_BY_API="$3"
+
# Maximum number of packages/steps.
MAXIMUM_PACKAGES=1000
@@ -53,25 +56,43 @@
# A source that infinitely emits arbitrary lines.
# When connected to STDIN of another process, this source keeps STDIN open until
# the consumer process closes STDIN or this script dies.
+# In practice, the pm command keeps consuming STDIN, so we don't need to worry
+# about running out of buffer space.
function infinite_source {
while echo .; do
sleep 1
done
}
+if [[ "$TRIGGERED_BY_API" = "1" ]]; then
+ # During OTA installation, the script is called the first time, and
+ # `TRIGGERED_BY_API` can never be "1". `TRIGGERED_BY_API` being "1" means this
+ # is the second call to this script, through the
+ # `UpdateEngine.triggerPostinstall` API.
+ # When we reach here, it means Pre-reboot Dexopt is enabled in asynchronous
+ # mode and the job scheduler determined that it's the time to run the job.
+ # Start Pre-reboot Dexopt now and wait for it to finish.
+ infinite_source | pm art on-ota-staged --start
+ exit $?
+fi
+
PR_DEXOPT_JOB_VERSION="$(pm art pr-dexopt-job --version)"
if (( $? == 0 )) && (( $PR_DEXOPT_JOB_VERSION >= 3 )); then
# Delegate to Pre-reboot Dexopt, a feature of ART Service.
# ART Service decides what to do with this request:
# - If Pre-reboot Dexopt is disabled or unsupported, the command returns
- # non-zero. This is always the case if the current system is Android 14 or
- # earlier.
+ # non-zero.
+ # This is always the case if the current system is Android 14 or earlier.
# - If Pre-reboot Dexopt is enabled in synchronous mode, the command blocks
- # until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds or
- # not. This is the default behavior if the current system is Android 15.
- # - If Pre-reboot Dexopt is enabled in asynchronous mode, the command schedules
- # an asynchronous job and returns 0 immediately. The job will then run by the
- # job scheduler when the device is idle and charging.
+ # until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds
+ # or not.
+ # This is the default behavior if the current system is Android 15.
+ # - If Pre-reboot Dexopt is enabled in asynchronous mode, the command
+ # schedules an asynchronous job and returns 0 immediately.
+ # Later, when the device is idle and charging, the job will be run by the
+ # job scheduler. It will call this script again through the
+ # `UpdateEngine.triggerPostinstall` API, with `TRIGGERED_BY_API` being "1".
+ # This is always the case if the current system is Android 16 or later.
if infinite_source | pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
# Handled by Pre-reboot Dexopt.
exit 0
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index cd8ca89..f320504 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -119,12 +119,24 @@
}
prebuilt_etc {
+ name: "android.hardware.nfc.ese.prebuilt.xml",
+ src: "android.hardware.nfc.ese.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.nfc.hce.prebuilt.xml",
src: "android.hardware.nfc.hce.xml",
defaults: ["frameworks_native_data_etc_defaults"],
}
prebuilt_etc {
+ name: "android.hardware.nfc.hcef.prebuilt.xml",
+ src: "android.hardware.nfc.hcef.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.reboot_escrow.prebuilt.xml",
src: "android.hardware.reboot_escrow.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -317,6 +329,12 @@
}
prebuilt_etc {
+ name: "android.hardware.telephony.messaging.prebuilt.xml",
+ src: "android.hardware.telephony.messaging.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.thread_network.prebuilt.xml",
src: "android.hardware.thread_network.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -505,6 +523,12 @@
}
prebuilt_etc {
+ name: "com.nxp.mifare.prebuilt.xml",
+ src: "com.nxp.mifare.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "go_handheld_core_hardware.prebuilt.xml",
src: "go_handheld_core_hardware.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.messaging.xml b/data/etc/android.hardware.telephony.messaging.xml
new file mode 100644
index 0000000..0e96123
--- /dev/null
+++ b/data/etc/android.hardware.telephony.messaging.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 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.
+-->
+
+<!-- Feature for devices with messaging. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.radio.access" />
+ <feature name="android.hardware.telephony.subscription" />
+ <feature name="android.hardware.telephony.messaging" />
+</permissions>
diff --git a/include/OWNERS b/include/OWNERS
index c98e87a..7f847e8 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -1,5 +1,4 @@
alecmouri@google.com
-alexeykuzmin@google.com
dangittik@google.com
jreck@google.com
lajos@google.com
diff --git a/include/android/input.h b/include/android/input.h
index 5f44550..2f6c5b5 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -862,7 +862,7 @@
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5,
AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6,
- // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs)
+ // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs,/frameworks/native/services/inputflinger/tests/fuzzers/FuzzedInputStream.h)
};
/**
diff --git a/include/android/system_health.h b/include/android/system_health.h
index bdb1413..4494e32 100644
--- a/include/android/system_health.h
+++ b/include/android/system_health.h
@@ -316,8 +316,6 @@
/**
* Gets the range of the calculation window size for CPU headroom.
*
- * In API version 36, the range will be a superset of [50, 10000].
- *
* Available since API level 36.
*
* @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
@@ -332,8 +330,6 @@
/**
* Gets the range of the calculation window size for GPU headroom.
*
- * In API version 36, the range will be a superset of [50, 10000].
- *
* Available since API level 36.
*
* @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
diff --git a/include/ftl/ignore.h b/include/ftl/ignore.h
new file mode 100644
index 0000000..1468fa2
--- /dev/null
+++ b/include/ftl/ignore.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::ftl {
+
+// An alternative to `std::ignore` that makes it easy to ignore multiple values.
+//
+// Examples:
+//
+// void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) {
+// // When invoked, all the arguments are ignored.
+// ftl::ignore(arg1, arg2, arg3);
+// }
+//
+// void ftl_ignore_single(int arg) {
+// // It can be used like std::ignore to ignore a single value
+// ftl::ignore = arg;
+// }
+//
+inline constexpr struct {
+ // NOLINTNEXTLINE(misc-unconventional-assign-operator, readability-named-parameter)
+ constexpr auto operator=(auto&&) const -> decltype(*this) { return *this; }
+ // NOLINTNEXTLINE(readability-named-parameter)
+ constexpr void operator()(auto&&...) const {}
+} ignore;
+
+} // namespace android::ftl
\ No newline at end of file
diff --git a/include/input/BlockingQueue.h b/include/input/BlockingQueue.h
index f848c82..6e32de6 100644
--- a/include/input/BlockingQueue.h
+++ b/include/input/BlockingQueue.h
@@ -16,6 +16,7 @@
#pragma once
+#include <input/PrintTools.h>
#include <condition_variable>
#include <functional>
#include <list>
@@ -126,11 +127,21 @@
* Primary used for debugging.
* Does not block.
*/
- size_t size() {
+ size_t size() const {
std::scoped_lock lock(mLock);
return mQueue.size();
}
+ bool empty() const {
+ std::scoped_lock lock(mLock);
+ return mQueue.empty();
+ }
+
+ std::string dump(std::string (*toString)(const T&) = constToString) const {
+ std::scoped_lock lock(mLock);
+ return dumpContainer(mQueue, toString);
+ }
+
private:
const std::optional<size_t> mCapacity;
/**
@@ -140,7 +151,7 @@
/**
* Lock for accessing and waiting on elements.
*/
- std::mutex mLock;
+ mutable std::mutex mLock;
std::list<T> mQueue GUARDED_BY(mLock);
};
diff --git a/include/input/Input.h b/include/input/Input.h
index e84023e..002b3a7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -316,6 +316,19 @@
bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties);
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+ int32_t action);
+
+bool isFromMouse(uint32_t source, ToolType tooltype);
+
+bool isFromTouchpad(uint32_t source, ToolType tooltype);
+
+bool isFromDrawingTablet(uint32_t source, ToolType tooltype);
+
+bool isHoverAction(int32_t action);
+
+bool isMouseOrTouchpad(uint32_t sources);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
diff --git a/include/input/InputFlags.h b/include/input/InputFlags.h
new file mode 100644
index 0000000..4b42f77
--- /dev/null
+++ b/include/input/InputFlags.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+class InputFlags {
+public:
+ /**
+ * Check if connected displays feature is enabled, either via the feature flag or settings
+ * override.
+ */
+ static bool connectedDisplaysCursorEnabled();
+
+ /**
+ * Check if both connectedDisplaysCursor and associatedDisplayCursorBugfix is enabled.
+ */
+ static bool connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled();
+};
+
+} // namespace android
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
index 4dbb0ea..76811f2 100644
--- a/libs/attestation/OWNERS
+++ b/libs/attestation/OWNERS
@@ -1,2 +1 @@
-chaviw@google.com
-svv@google.com
\ No newline at end of file
+svv@google.com
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index fb00d4f..ea5343a 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -827,6 +827,7 @@
// so we restrict its visibility to the Trusty-specific packages.
visibility: [
":__subpackages__",
+ "//hardware/interfaces/security/see:__subpackages__",
"//system/core/trusty:__subpackages__",
"//vendor:__subpackages__",
],
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 16023ff..1f3a45a 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -188,7 +188,9 @@
}
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
- return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
+ return setupClient([&, fd = std::move(fd),
+ request = std::move(request)](const std::vector<uint8_t>& sessionId,
+ bool incoming) mutable -> status_t {
if (!fd.ok()) {
fd = request();
if (!fd.ok()) return BAD_VALUE;
@@ -476,8 +478,10 @@
return server;
}
-status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
- bool incoming)>& connectAndInit) {
+template <typename Fn,
+ typename /* = std::enable_if_t<std::is_invocable_r_v<
+ status_t, Fn, const std::vector<uint8_t>&, bool>> */>
+status_t RpcSession::setupClient(Fn&& connectAndInit) {
{
RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once");
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 3819fb6..14c0bde 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <poll.h>
#include <trusty/tipc.h>
+#include <type_traits>
#include "FdTrigger.h"
#include "RpcState.h"
@@ -32,6 +33,9 @@
namespace android {
+// Corresponds to IPC_MAX_MSG_HANDLES in the Trusty kernel
+constexpr size_t kMaxTipcHandles = 8;
+
// RpcTransport for writing Trusty IPC clients in Android.
class RpcTransportTipcAndroid : public RpcTransport {
public:
@@ -78,12 +82,28 @@
FdTrigger* fdTrigger, iovec* iovs, int niovs,
const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
+ bool sentFds = false;
auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
- // TODO: send ancillaryFds. For now, we just abort if anyone tries
- // to send any.
- LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(),
- "File descriptors are not supported on Trusty yet");
- return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0));
+ trusty_shm shms[kMaxTipcHandles] = {{0}};
+ ssize_t shm_count = 0;
+
+ if (!sentFds && ancillaryFds != nullptr && !ancillaryFds->empty()) {
+ if (ancillaryFds->size() > kMaxTipcHandles) {
+ ALOGE("Too many file descriptors for TIPC: %zu", ancillaryFds->size());
+ errno = EINVAL;
+ return -1;
+ }
+ for (const auto& fdVariant : *ancillaryFds) {
+ shms[shm_count++] = {std::visit([](const auto& fd) { return fd.get(); },
+ fdVariant),
+ TRUSTY_SEND_SECURE_OR_SHARE};
+ }
+ }
+
+ auto ret = TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs,
+ (shm_count == 0) ? nullptr : shms, shm_count));
+ sentFds |= ret >= 0;
+ return ret;
};
status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn,
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS
index 3100518..2617a16 100644
--- a/libs/binder/aidl/android/content/pm/OWNERS
+++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -1,5 +1,4 @@
+michaelwr@google.com
narayan@google.com
patb@google.com
-svetoslavganov@google.com
-toddke@google.com
-patb@google.com
\ No newline at end of file
+schfan@google.com
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index bb45ad2..993ad82 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -263,7 +263,6 @@
"android.utils.IMemory",
"android.utils.IMemoryHeap",
"com.android.car.procfsinspector.IProcfsInspector",
- "com.android.internal.app.IAppOpsCallback",
"com.android.internal.app.IAppOpsService",
"com.android.internal.app.IBatteryStats",
"com.android.internal.os.IResultReceiver",
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index af37bf2..c9f92da 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -25,6 +25,7 @@
#include <map>
#include <optional>
+#include <type_traits>
#include <vector>
namespace android {
@@ -291,9 +292,14 @@
// join on thread passed to preJoinThreadOwnership
static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
- [[nodiscard]] status_t setupClient(
- const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
- connectAndInit);
+ // This is a workaround to support move-only functors.
+ // TODO: use std::move_only_function when it becomes available.
+ template <typename Fn,
+ // Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning
+ // status_t
+ typename = std::enable_if_t<
+ std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>>
+ [[nodiscard]] status_t setupClient(Fn&& connectAndInit);
[[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
[[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
const std::vector<uint8_t>& sessionId,
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index bcbd14f..e848385 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -79,7 +79,7 @@
template <typename T>
typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
const Parcel& parcel, sp<T>* t) const {
- *t = new T{};
+ *t = sp<T>::make();
return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
}
template <typename T>
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 48c0ea6..1949cbb 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -134,9 +134,14 @@
//
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
+//
+// paramDeleteFd will be called when requestFd and param are no longer in use.
+// Callers can pass a function deleting param if necessary. Callers can set
+// paramDeleteFd to null if callers doesn't need to clean up param.
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
int (*requestFd)(void* param),
- void* param);
+ void* param,
+ void (*paramDeleteFd)(void* param));
// Sets the file descriptor transport mode for this session.
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 64b1be2..8177fb0 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -248,9 +248,18 @@
#endif // __TRUSTY__
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
- void* param) {
+ void* param, void (*paramDeleteFd)(void* param)) {
auto session = handleToStrongPointer<RpcSession>(handle);
- auto request = [=] { return unique_fd{requestFd(param)}; };
+ auto deleter = [=](void* param) {
+ if (paramDeleteFd) {
+ paramDeleteFd(param);
+ }
+ };
+ // TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function.
+ std::shared_ptr<void> sharedParam(param, deleter);
+ auto request = [=, sharedParam = std::move(sharedParam)] {
+ return unique_fd{requestFd(sharedParam.get())};
+ };
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str());
return nullptr;
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 89f21dd..783e11f 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -62,6 +62,8 @@
* This must be called before the object is sent to another process.
* Aborts on invalid values. Not thread safe.
*
+ * This overrides the setting in ABinderProcess_disableBackgroundScheduling.
+ *
* \param binder local server binder to set the policy for
* \param policy scheduler policy as defined in linux UAPI
* \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 6aff994..2432099 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -75,6 +75,19 @@
void ABinderProcess_joinThreadPool(void);
/**
+ * Disables (or enables) background scheduling.
+ *
+ * By default, binder threads execute at a lower priority. However, this can cause
+ * priority inversion, so it is recommended to be disabled in high priority
+ * or in system processes.
+ *
+ * See also AIBinder_setMinSchedulerPolicy, which overrides this setting.
+ *
+ * \param disable whether to disable background scheduling
+ */
+void ABinderProcess_disableBackgroundScheduling(bool disable);
+
+/**
* This gives you an fd to wait on. Whenever data is available on the fd,
* ABinderProcess_handlePolledCommands can be called to handle binder queries.
* This is expected to be used in a single threaded process which waits on
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 089c775..8050205 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -27,6 +27,10 @@
#if defined(__ANDROID_VENDOR__)
+#if defined(__ANDROID_PRODUCT__)
+#error "build bug: product is not part of the vendor half of the Treble system/vendor split"
+#endif
+
/**
* Private addition to binder_flag_t.
*/
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index a637165..d4eb8c7 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -229,6 +229,7 @@
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
AParcel_viewPlatformParcel*;
+ ABinderProcess_disableBackgroundScheduling;
};
local:
*;
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0072ac3..bcdb959 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -36,6 +36,10 @@
IPCThreadState::self()->joinThreadPool();
}
+void ABinderProcess_disableBackgroundScheduling(bool disable) {
+ IPCThreadState::disableBackgroundScheduling(disable);
+}
+
binder_status_t ABinderProcess_setupPolling(int* fd) {
return IPCThreadState::self()->setupPolling(fd);
}
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 09688a2..411b9de 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -195,11 +195,13 @@
/// take ownership of) file descriptors already connected to it.
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
&self,
- mut request_fd: impl FnMut() -> Option<RawFd>,
+ request_fd: impl FnMut() -> Option<RawFd>,
) -> Result<Strong<T>, StatusCode> {
- // Double reference the factory because trait objects aren't FFI safe.
- let mut request_fd_ref: RequestFd = &mut request_fd;
- let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+ // Trait objects aren't FFI safe, so *mut c_void can't be converted back to
+ // *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get
+ // the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks.
+ let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd);
+ let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void;
// SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
// ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
@@ -209,6 +211,7 @@
self.as_ptr(),
Some(request_fd_wrapper),
param,
+ Some(param_delete_fd_wrapper),
))
};
Self::get_interface(service)
@@ -225,13 +228,18 @@
}
}
-type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
-
unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
- let request_fd_ptr = param as *mut RequestFd;
+ let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>;
// SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
// BinderFdFactory reference, with param being a properly aligned non-null pointer to an
// initialized instance.
let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
request_fd().unwrap_or(-1)
}
+
+unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) {
+ // SAFETY: This is only ever called by RpcPreconnectedClient, with param being the
+ // pointer returned from Box::into_raw.
+ let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) };
+ drop(request_fd_box);
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 8c0501b..6a8a698 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1201,5 +1201,45 @@
Ok(v.map(|v| v.into_iter().map(Self).collect()))
}
}
+
+ impl std::ops::BitOr for $enum {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl std::ops::BitOrAssign for $enum {
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 | rhs.0;
+ }
+ }
+
+ impl std::ops::BitAnd for $enum {
+ type Output = Self;
+ fn bitand(self, rhs: Self) -> Self {
+ Self(self.0 & rhs.0)
+ }
+ }
+
+ impl std::ops::BitAndAssign for $enum {
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.0 = self.0 & rhs.0;
+ }
+ }
+
+ impl std::ops::BitXor for $enum {
+ type Output = Self;
+ fn bitxor(self, rhs: Self) -> Self {
+ Self(self.0 ^ rhs.0)
+ }
+ }
+
+ impl std::ops::BitXorAssign for $enum {
+ fn bitxor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 ^ rhs.0;
+ }
+ }
+
};
}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index c0cac83..609334e 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -28,8 +28,9 @@
/// `num_threads` additional threads as specified by
/// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
///
- /// This should be done before creating any Binder client or server. If
- /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+ /// If this is called, it must be done before creating any Binder client or server.
+ ///
+ /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are
/// called, then some things (such as callbacks and
/// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
/// not work: the callbacks will be queued but never called as there is no
diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp
index a3fc9cc..cbc4180 100644
--- a/libs/binder/tests/binderStabilityIntegrationTest.cpp
+++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp
@@ -47,6 +47,7 @@
Stability::Level level = Stability::Level::UNDECLARED;
switch (partition) {
+ case Partition::PRODUCT:
case Partition::SYSTEM:
case Partition::SYSTEM_EXT:
level = Stability::Level::SYSTEM;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index cac054e..457eaa5 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -109,6 +109,9 @@
"libcutils",
"libutils",
],
+ header_libs: [
+ "libaidl_transactions",
+ ],
local_include_dirs: ["include_random_parcel"],
export_include_dirs: ["include_random_parcel"],
}
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 02e69cc..11aa768 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include <aidl/transaction_ids.h>
#include <fuzzbinder/libbinder_driver.h>
#include <fuzzbinder/random_parcel.h>
@@ -31,6 +33,28 @@
fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
}
+uint32_t getCode(FuzzedDataProvider& provider) {
+ if (provider.ConsumeBool()) {
+ return provider.ConsumeIntegral<uint32_t>();
+ }
+
+ // Most of the AIDL services will have small set of transaction codes.
+ if (provider.ConsumeBool()) {
+ return provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ }
+
+ if (provider.ConsumeBool()) {
+ return provider.PickValueInArray<uint32_t>(
+ {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+ IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+ IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+ IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+ }
+
+ return provider.ConsumeIntegralInRange<uint32_t>(aidl::kLastMetaMethodId,
+ aidl::kFirstMetaMethodId);
+}
+
void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
RandomParcelOptions options{
.extraBinders = binders,
@@ -61,16 +85,7 @@
}
while (provider.remaining_bytes() > 0) {
- // Most of the AIDL services will have small set of transaction codes.
- // TODO(b/295942369) : Add remaining transact codes from IBinder.h
- uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
- : provider.ConsumeBool()
- ? provider.ConsumeIntegralInRange<uint32_t>(0, 100)
- : provider.PickValueInArray<uint32_t>(
- {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
- IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
- IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
- IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
+ uint32_t code = getCode(provider);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
index ef1b7c3..40fc218 100644
--- a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
@@ -30,6 +30,8 @@
trusty/user/base/lib/libstdc++-trusty \
trusty/user/base/lib/trusty-sys \
+MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp
+
MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
MODULE_BINDGEN_FLAGS += \
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
index a8acded..16147dd 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -48,7 +48,7 @@
///
/// This constant is used as a part of the debug store's data format,
/// allowing for version tracking and compatibility checks.
- const ENCODE_VERSION: u32 = 2;
+ const ENCODE_VERSION: u32 = 3;
/// Creates a new instance of `DebugStore` with specified event limit and maximum delay.
fn new() -> Self {
@@ -123,9 +123,11 @@
impl fmt::Display for DebugStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Write the debug store header information
let uptime_now = uptimeMillis();
write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?;
+ // Join events with a separator
write!(
f,
"{}",
@@ -136,7 +138,10 @@
acc.push_str(&event.to_string());
acc
})
- )
+ )?;
+
+ // Write the debug store footer
+ write!(f, ";;")
}
}
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index a9bd11e..43ee33e 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -101,7 +101,7 @@
"android.hardware.automotive.remoteaccess.IRemoteAccess",
"android.hardware.automotive.vehicle.IVehicle",
"android.hardware.biometrics.face.IBiometricsFace",
- "android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
+ "android.hardware.biometrics.fingerprint.IFingerprint",
"android.hardware.camera.provider.ICameraProvider",
"android.hardware.drm.IDrmFactory",
"android.hardware.graphics.allocator.IAllocator",
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 08ce855..5244442 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -26,6 +26,7 @@
"function_test.cpp",
"future_test.cpp",
"hash_test.cpp",
+ "ignore_test.cpp",
"match_test.cpp",
"mixins_test.cpp",
"non_null_test.cpp",
diff --git a/libs/ftl/ignore_test.cpp b/libs/ftl/ignore_test.cpp
new file mode 100644
index 0000000..5d5c67b
--- /dev/null
+++ b/libs/ftl/ignore_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2025 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 <string>
+
+#include <ftl/ignore.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+namespace {
+
+// Keep in sync with the example usage in the header file.
+
+void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) {
+ // When invoked, all the arguments are ignored.
+ ftl::ignore(arg1, arg2, arg3);
+}
+
+void ftl_ignore_single(int arg) {
+ // It can be used like std::ignore to ignore a single value
+ ftl::ignore = arg;
+}
+
+} // namespace
+
+TEST(Ignore, Example) {
+ // The real example test is that there are no compiler warnings for unused arguments above.
+
+ // Use the example functions to avoid a compiler warning about unused functions.
+ ftl_ignore_multiple(0, "a", "b");
+ ftl_ignore_single(0);
+}
+
+} // namespace android::test
diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp
index 6974da9..9e7a4cf 100644
--- a/libs/graphicsenv/FeatureOverrides.cpp
+++ b/libs/graphicsenv/FeatureOverrides.cpp
@@ -14,22 +14,179 @@
* limitations under the License.
*/
-#include <graphicsenv/FeatureOverrides.h>
+#include <cinttypes>
#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <graphicsenv/FeatureOverrides.h>
namespace android {
using base::StringAppendF;
+status_t FeatureConfig::writeToParcel(Parcel* parcel) const {
+ status_t status;
+
+ status = parcel->writeUtf8AsUtf16(mFeatureName);
+ if (status != OK) {
+ return status;
+ }
+ status = parcel->writeBool(mEnabled);
+ if (status != OK) {
+ return status;
+ }
+ // Number of GPU vendor IDs.
+ status = parcel->writeVectorSize(mGpuVendorIDs);
+ if (status != OK) {
+ return status;
+ }
+ // GPU vendor IDs.
+ for (const auto& vendorID : mGpuVendorIDs) {
+ status = parcel->writeUint32(vendorID);
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ return OK;
+}
+
+status_t FeatureConfig::readFromParcel(const Parcel* parcel) {
+ status_t status;
+
+ status = parcel->readUtf8FromUtf16(&mFeatureName);
+ if (status != OK) {
+ return status;
+ }
+ status = parcel->readBool(&mEnabled);
+ if (status != OK) {
+ return status;
+ }
+ // Number of GPU vendor IDs.
+ int numGpuVendorIDs;
+ status = parcel->readInt32(&numGpuVendorIDs);
+ if (status != OK) {
+ return status;
+ }
+ // GPU vendor IDs.
+ for (int i = 0; i < numGpuVendorIDs; i++) {
+ uint32_t gpuVendorIdUint;
+ status = parcel->readUint32(&gpuVendorIdUint);
+ if (status != OK) {
+ return status;
+ }
+ mGpuVendorIDs.emplace_back(gpuVendorIdUint);
+ }
+
+ return OK;
+}
+
std::string FeatureConfig::toString() const {
std::string result;
StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str());
StringAppendF(&result, " Status: %s\n", mEnabled ? "enabled" : "disabled");
+ for (const auto& vendorID : mGpuVendorIDs) {
+ // vkjson outputs decimal, so print both formats.
+ StringAppendF(&result, " GPU Vendor ID: 0x%04X (%d)\n", vendorID, vendorID);
+ }
return result;
}
+status_t FeatureOverrides::writeToParcel(Parcel* parcel) const {
+ status_t status;
+ // Number of global feature configs.
+ status = parcel->writeVectorSize(mGlobalFeatures);
+ if (status != OK) {
+ return status;
+ }
+ // Global feature configs.
+ for (const auto& cfg : mGlobalFeatures) {
+ status = cfg.writeToParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+ // Number of package feature overrides.
+ status = parcel->writeInt32(static_cast<int32_t>(mPackageFeatures.size()));
+ if (status != OK) {
+ return status;
+ }
+ for (const auto& feature : mPackageFeatures) {
+ // Package name.
+ status = parcel->writeUtf8AsUtf16(feature.first);
+ if (status != OK) {
+ return status;
+ }
+ // Number of package feature configs.
+ status = parcel->writeVectorSize(feature.second);
+ if (status != OK) {
+ return status;
+ }
+ // Package feature configs.
+ for (const auto& cfg : feature.second) {
+ status = cfg.writeToParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+ }
+
+ return OK;
+}
+
+status_t FeatureOverrides::readFromParcel(const Parcel* parcel) {
+ status_t status;
+
+ // Number of global feature configs.
+ status = parcel->resizeOutVector(&mGlobalFeatures);
+ if (status != OK) {
+ return status;
+ }
+ // Global feature configs.
+ for (FeatureConfig& cfg : mGlobalFeatures) {
+ status = cfg.readFromParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ // Number of package feature overrides.
+ int numPkgOverrides;
+ status = parcel->readInt32(&numPkgOverrides);
+ if (status != OK) {
+ return status;
+ }
+ // Package feature overrides.
+ for (int i = 0; i < numPkgOverrides; i++) {
+ // Package name.
+ std::string name;
+ status = parcel->readUtf8FromUtf16(&name);
+ if (status != OK) {
+ return status;
+ }
+ std::vector<FeatureConfig> cfgs;
+ // Number of package feature configs.
+ int numCfgs;
+ status = parcel->readInt32(&numCfgs);
+ if (status != OK) {
+ return status;
+ }
+ // Package feature configs.
+ for (int j = 0; j < numCfgs; j++) {
+ FeatureConfig cfg;
+ status = cfg.readFromParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ cfgs.emplace_back(cfg);
+ }
+ mPackageFeatures[name] = cfgs;
+ }
+
+ return OK;
+}
+
std::string FeatureOverrides::toString() const {
std::string result;
result.append("Global Features:\n");
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4bc2611..626581c 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -621,6 +621,10 @@
mShouldUseAngle = true;
}
mShouldUseNativeDriver = shouldUseNativeDriver;
+
+ if (mShouldUseAngle) {
+ updateAngleFeatureOverrides();
+ }
}
std::string& GraphicsEnv::getPackageName() {
@@ -632,6 +636,22 @@
return mAngleEglFeatures;
}
+// List of ANGLE features to override (enabled or disable).
+// The list of overrides is loaded and parsed by GpuService.
+void GraphicsEnv::updateAngleFeatureOverrides() {
+ if (!graphicsenv_flags::feature_overrides()) {
+ return;
+ }
+
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ ALOGE("No GPU service");
+ return;
+ }
+
+ mFeatureOverrides = gpuService->getFeatureOverrides();
+}
+
void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled,
std::vector<const char*>& disabled) {
if (!graphicsenv_flags::feature_overrides()) {
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 42e7c37..9a34aff 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -119,6 +119,21 @@
}
return driverPath;
}
+
+ FeatureOverrides getFeatureOverrides() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ FeatureOverrides featureOverrides;
+ status_t error =
+ remote()->transact(BnGpuService::GET_FEATURE_CONFIG_OVERRIDES, data, &reply);
+ if (error != OK) {
+ return featureOverrides;
+ }
+
+ featureOverrides.readFromParcel(&reply);
+ return featureOverrides;
+ }
};
IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -271,6 +286,15 @@
toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
return OK;
}
+ case GET_FEATURE_CONFIG_OVERRIDES: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ // Get the FeatureOverrides from gpuservice, which implements the IGpuService interface
+ // with GpuService::getFeatureOverrides().
+ FeatureOverrides featureOverrides = getFeatureOverrides();
+ featureOverrides.writeToParcel(reply);
+ return OK;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
index 2b94187..5dc613b 100644
--- a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
+++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
@@ -20,28 +20,35 @@
#include <string>
#include <vector>
+#include <binder/Parcelable.h>
+
namespace android {
-class FeatureConfig {
+class FeatureConfig : public Parcelable {
public:
FeatureConfig() = default;
FeatureConfig(const FeatureConfig&) = default;
virtual ~FeatureConfig() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
std::string toString() const;
std::string mFeatureName;
bool mEnabled;
+ std::vector<uint32_t> mGpuVendorIDs;
};
/*
* Class for transporting OpenGL ES Feature configurations from GpuService to authorized
* recipients.
*/
-class FeatureOverrides {
+class FeatureOverrides : public Parcelable {
public:
FeatureOverrides() = default;
FeatureOverrides(const FeatureOverrides&) = default;
virtual ~FeatureOverrides() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
std::string toString() const;
std::vector<FeatureConfig> mGlobalFeatures;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 55fa13a..6821900 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -121,6 +121,7 @@
// Get the app package name.
std::string& getPackageName();
const std::vector<std::string>& getAngleEglFeatures();
+ void updateAngleFeatureOverrides();
void getAngleFeatureOverrides(std::vector<const char*>& enabled,
std::vector<const char*>& disabled);
// Set the persist.graphics.egl system property value.
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index a0d6e37..442683a 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -18,6 +18,7 @@
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <vector>
@@ -55,6 +56,9 @@
// sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true.
virtual void toggleAngleAsSystemDriver(bool enabled) = 0;
+
+ // Get the list of features to override.
+ virtual FeatureOverrides getFeatureOverrides() = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
@@ -67,6 +71,7 @@
TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
SET_TARGET_STATS_ARRAY,
ADD_VULKAN_ENGINE_NAME,
+ GET_FEATURE_CONFIG_OVERRIDES,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 5ab31db..b5fa321 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -281,6 +281,7 @@
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
+ "TransactionState.cpp",
"VsyncEventData.cpp",
"view/Surface.cpp",
"WindowInfosListenerReporter.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c770db9..fa97142 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -197,15 +197,15 @@
mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_HW_TEXTURE,
- 1, false, this);
+ mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
#else
- mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_HW_TEXTURE,
- 1, false, this);
+ mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -222,8 +222,6 @@
ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
- mNumAcquired = 0;
- mNumFrameAvailable = 0;
TransactionCompletedListener::getInstance()->addQueueStallListener(
[&](const std::string& reason) {
@@ -439,7 +437,7 @@
void BLASTBufferQueue::flushShadowQueue() {
BQA_LOGV("flushShadowQueue");
- int numFramesToFlush = mNumFrameAvailable;
+ int32_t numFramesToFlush = mNumFrameAvailable;
while (numFramesToFlush > 0) {
acquireNextBufferLocked(std::nullopt);
numFramesToFlush--;
@@ -639,7 +637,8 @@
bufferItem.mScalingMode, crop);
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
- sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+ sp<Fence> fence =
+ bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE;
nsecs_t dequeueTime = -1;
{
@@ -1016,7 +1015,7 @@
if (includeSurfaceControlHandle && mSurfaceControl) {
scHandle = mSurfaceControl->getHandle();
}
- return new BBQSurface(mProducer, true, scHandle, this);
+ return sp<BBQSurface>::make(mProducer, true, scHandle, this);
}
void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
@@ -1122,10 +1121,10 @@
class AsyncProducerListener : public BnProducerListener {
private:
const sp<IProducerListener> mListener;
+ AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+ friend class sp<AsyncProducerListener>;
public:
- AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
-
void onBufferReleased() override {
AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
}
@@ -1179,7 +1178,7 @@
return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
}
- return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+ return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api,
producerControlledByApp, output);
}
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 3b2d337..9dcd5dc 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -215,14 +215,14 @@
FlattenableUtils::read(buffer, size, flags);
if (flags & 1) {
- mGraphicBuffer = new GraphicBuffer();
+ mGraphicBuffer = sp<GraphicBuffer>::make();
status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
}
if (flags & 2) {
- mFence = new Fence();
+ mFence = sp<Fence>::make();
status_t err = mFence->unflatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 8566419..1585aae 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -24,6 +24,7 @@
#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
@@ -35,6 +36,30 @@
namespace android {
+std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create(
+ uint64_t consumerUsage, int bufferCount, bool controlledByApp,
+ bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp,
+ isConsumerSurfaceFlinger);
+ return {bufferItemConsumer, bufferItemConsumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+ sp<BufferItemConsumer> bufferItemConsumer =
+ sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp);
+ return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp) {
+ return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp);
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
bool controlledByApp, bool isConsumerSurfaceFlinger)
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index f1374e2..f21ac18 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -125,15 +125,16 @@
LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
"BufferQueue: outConsumer must not be NULL");
- sp<BufferQueueCore> core(new BufferQueueCore());
+ sp<BufferQueueCore> core = sp<BufferQueueCore>::make();
LOG_ALWAYS_FATAL_IF(core == nullptr,
"BufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
+ sp<IGraphicBufferProducer> producer =
+ sp<BufferQueueProducer>::make(core, consumerIsSurfaceFlinger);
LOG_ALWAYS_FATAL_IF(producer == nullptr,
"BufferQueue: failed to create BufferQueueProducer");
- sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+ sp<IGraphicBufferConsumer> consumer = sp<BufferQueueConsumer>::make(core);
LOG_ALWAYS_FATAL_IF(consumer == nullptr,
"BufferQueue: failed to create BufferQueueConsumer");
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index c241482..5961b41 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -693,11 +693,11 @@
.requestorName = {mConsumerName.c_str(), mConsumerName.size()},
.extras = std::move(tempOptions),
};
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+ sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
#else
sp<GraphicBuffer> graphicBuffer =
- new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
- {mConsumerName.c_str(), mConsumerName.size()});
+ sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage,
+ std::string{mConsumerName.c_str(), mConsumerName.size()});
#endif
status_t error = graphicBuffer->initCheck();
@@ -1464,7 +1464,7 @@
#ifndef NO_BINDER
if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
status = IInterface::asBinder(listener)->linkToDeath(
- static_cast<IBinder::DeathRecipient*>(this));
+ sp<IBinder::DeathRecipient>::fromExisting(this));
if (status != NO_ERROR) {
BQ_LOGE("connect: linkToDeath failed: %s (%d)",
strerror(-status), status);
@@ -1553,8 +1553,7 @@
IInterface::asBinder(mCore->mLinkedToDeath);
// This can fail if we're here because of the death
// notification, but we just ignore it
- token->unlinkToDeath(
- static_cast<IBinder::DeathRecipient*>(this));
+ token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
#endif
mCore->mSharedBufferSlot =
@@ -1685,11 +1684,11 @@
#endif
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+ sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
#else
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
- allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
- allocUsage, allocName);
+ sp<GraphicBuffer> graphicBuffer =
+ sp<GraphicBuffer>::make(allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+ allocUsage, allocName);
#endif
status_t result = graphicBuffer->initCheck();
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9cb013..4f495d0 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -108,7 +108,7 @@
status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
int const*& fds, size_t& count) {
- releaseFence = new Fence();
+ releaseFence = sp<Fence>::make();
if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
return err;
}
@@ -344,4 +344,4 @@
return STATUS_OK;
}
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index ba50bf8..80a3543 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -20,6 +20,7 @@
#include <gui/Choreographer.h>
#include <gui/TraceUtils.h>
#include <jni.h>
+#include <utils/Looper.h>
#undef LOG_TAG
#define LOG_TAG "AChoreographer"
@@ -69,7 +70,7 @@
Choreographer::Context Choreographer::gChoreographers;
-static thread_local Choreographer* gChoreographer;
+static thread_local sp<Choreographer> gChoreographer;
void Choreographer::initJVM(JNIEnv* env) {
env->GetJavaVM(&gJni.jvm);
@@ -86,14 +87,14 @@
"()V");
}
-Choreographer* Choreographer::getForThread() {
+sp<Choreographer> Choreographer::getForThread() {
if (gChoreographer == nullptr) {
sp<Looper> looper = Looper::getForThread();
if (!looper.get()) {
ALOGW("No looper prepared for thread");
return nullptr;
}
- gChoreographer = new Choreographer(looper);
+ gChoreographer = sp<Choreographer>::make(looper);
status_t result = gChoreographer->initialize();
if (result != OK) {
ALOGW("Failed to initialize");
@@ -238,7 +239,7 @@
// socket should be atomic across processes.
DisplayEventReceiver::Event event;
event.header =
- DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ DisplayEventReceiver::Event::Header{DisplayEventType::DISPLAY_EVENT_NULL,
PhysicalDisplayId::fromPort(0), systemTime()};
injectEvent(event);
}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3ad0e52..117a362 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -31,6 +31,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
+#include <gui/IConsumerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -68,8 +69,8 @@
#endif
mAbandoned(false),
mConsumer(bufferQueue),
- mPrevFinalReleaseFence(Fence::NO_FENCE) {
- initialize(controlledByApp);
+ mPrevFinalReleaseFence(Fence::NO_FENCE),
+ mIsControlledByApp(controlledByApp) {
}
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
@@ -79,11 +80,11 @@
mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
#endif
mAbandoned(false),
- mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ mPrevFinalReleaseFence(Fence::NO_FENCE),
+ mIsControlledByApp(controlledByApp) {
sp<IGraphicBufferProducer> producer;
BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
mSurface = sp<Surface>::make(producer, controlledByApp);
- initialize(controlledByApp);
}
ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
@@ -95,24 +96,27 @@
mAbandoned(false),
mConsumer(consumer),
mSurface(sp<Surface>::make(producer, controlledByApp)),
- mPrevFinalReleaseFence(Fence::NO_FENCE) {
- initialize(controlledByApp);
+ mPrevFinalReleaseFence(Fence::NO_FENCE),
+ mIsControlledByApp(controlledByApp) {
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-void ConsumerBase::initialize(bool controlledByApp) {
+void ConsumerBase::onFirstRef() {
+ ConsumerListener::onFirstRef();
+ initialize();
+}
+
+void ConsumerBase::initialize() {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
- // Note that we can't create an sp<...>(this) in a ctor that will not keep a
- // reference once the ctor ends, as that would cause the refcount of 'this'
- // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
- // that's what we create.
- wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
- sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
+ // Here we depend on an sp/wp having been created for `this`. For this
+ // reason, initialize() cannot be called from a ctor.
+ wp<ConsumerListener> listener = wp<ConsumerListener>::fromExisting(this);
+ sp<IConsumerListener> proxy = sp<BufferQueue::ProxyConsumerListener>::make(listener);
- status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
+ status_t err = mConsumer->consumerConnect(proxy, mIsControlledByApp);
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
@@ -385,6 +389,26 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t ConsumerBase::addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence) {
+ CB_LOGV("addReleaseFence");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ CB_LOGE("addReleaseFence: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
+
+ return addReleaseFenceLocked(slotIndex, buffer, fence);
+}
+
status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mMutex);
if (mAbandoned) {
@@ -460,7 +484,6 @@
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
}
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -469,7 +492,6 @@
}
return mConsumer->setConsumerIsProtected(isProtected);
}
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<NativeHandle> ConsumerBase::getSidebandStream() const {
Mutex::Autolock _l(mMutex);
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 23b432e..ecbceb7 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -20,7 +20,11 @@
#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
#include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
#include <utils/Log.h>
#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,6 +35,28 @@
namespace android {
+std::tuple<sp<CpuConsumer>, sp<Surface>> CpuConsumer::create(size_t maxLockedBuffers,
+ bool controlledByApp,
+ bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<CpuConsumer> consumer =
+ sp<CpuConsumer>::make(maxLockedBuffers, controlledByApp, isConsumerSurfaceFlinger);
+ return {consumer, consumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+
+ return {sp<CpuConsumer>::make(igbc, maxLockedBuffers, controlledByApp),
+ sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<CpuConsumer> CpuConsumer::create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp) {
+ return sp<CpuConsumer>::make(bq, maxLockedBuffers, controlledByApp);
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
bool isConsumerSurfaceFlinger)
@@ -230,7 +256,7 @@
return err;
}
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence);
releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer);
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 68f10f4..6f23885 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -167,7 +167,7 @@
for (ssize_t i = 0; i < n; i++) {
const DisplayEventReceiver::Event& ev = buf[i];
switch (ev.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ case DisplayEventType::DISPLAY_EVENT_VSYNC:
// Later vsync events will just overwrite the info from earlier
// ones. That's fine, we only care about the most recent.
gotVsync = true;
@@ -183,7 +183,7 @@
ATRACE_INT("RenderRate", fps);
}
break;
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
if (ev.hotplug.connectionError == 0) {
dispatchHotplug(ev.header.timestamp, ev.header.displayId,
ev.hotplug.connected);
@@ -192,31 +192,28 @@
ev.hotplug.connectionError);
}
break;
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE:
dispatchModeChanged(ev.header.timestamp, ev.header.displayId,
ev.modeChange.modeId, ev.modeChange.vsyncPeriod);
break;
- case DisplayEventReceiver::DISPLAY_EVENT_NULL:
+ case DisplayEventType::DISPLAY_EVENT_NULL:
dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
break;
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
mFrameRateOverrides.emplace_back(ev.frameRateOverride);
break;
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
std::move(mFrameRateOverrides));
break;
- case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
dispatchHdcpLevelsChanged(ev.header.displayId,
ev.hdcpLevelsChange.connectedLevel,
ev.hdcpLevelsChange.maxLevel);
break;
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId);
break;
- default:
- ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
- break;
}
}
}
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 052b8ed..2c5770d 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -37,6 +37,7 @@
#include <gui/DebugEGLImageTracker.h>
#include <gui/GLConsumer.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
@@ -101,6 +102,50 @@
return hasIt;
}
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync,
+ bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<GLConsumer> consumer =
+ sp<GLConsumer>::make(tex, textureTarget, useFenceSync, isControlledByApp);
+ return {consumer, consumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc);
+
+ return {sp<GLConsumer>::make(igbc, tex, textureTarget, useFenceSync, isControlledByApp),
+ sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t textureTarget,
+ bool useFenceSync,
+ bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<GLConsumer> consumer = sp<GLConsumer>::make(textureTarget, useFenceSync, isControlledByApp);
+ return {consumer, consumer->getSurface()};
+#else
+ sp<IGraphicBufferProducer> igbp;
+ sp<IGraphicBufferConsumer> igbc;
+ BufferQueue::createBufferQueue(&igbp, &igbc);
+
+ return {sp<GLConsumer>::make(igbc, textureTarget, useFenceSync, isControlledByApp),
+ sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+ uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp) {
+ return sp<GLConsumer>::make(bq, tex, textureTarget, useFenceSync, isControlledByApp);
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp) {
+ return sp<GLConsumer>::make(bq, textureTarget, useFenceSync, isControlledByApp);
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
: ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
@@ -333,7 +378,7 @@
}
if (mReleasedTexImage == nullptr) {
- mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer());
}
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
@@ -365,10 +410,10 @@
if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
// The first time, create the debug texture in case the application
// continues to use it.
- sp<GraphicBuffer> buffer = new GraphicBuffer(
- kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
- DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
- "[GLConsumer debug texture]");
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+ DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ "[GLConsumer debug texture]");
uint32_t* bits;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
uint32_t stride = buffer->getStride();
@@ -400,7 +445,7 @@
// replaces any old EglImage with a new one (using the new buffer).
if (item->mGraphicBuffer != nullptr) {
int slot = item->mSlot;
- mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
+ mEglSlots[slot].mEglImage = sp<EglImage>::make(item->mGraphicBuffer);
}
return NO_ERROR;
@@ -737,7 +782,7 @@
"fd: %#x", eglGetError());
return UNKNOWN_ERROR;
}
- sp<Fence> fence(new Fence(fenceFd));
+ sp<Fence> fence = sp<Fence>::make(fenceFd);
status_t err = addReleaseFenceLocked(mCurrentTexture,
mCurrentTextureImage->graphicBuffer(), fence);
if (err != OK) {
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 9f71eb1..1d1910e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -104,7 +104,7 @@
}
bool nonNull = reply.readInt32();
if (nonNull) {
- *buf = new GraphicBuffer();
+ *buf = sp<GraphicBuffer>::make();
result = reply.read(**buf);
if(result != NO_ERROR) {
(*buf).clear();
@@ -197,7 +197,7 @@
}
*buf = reply.readInt32();
- *fence = new Fence();
+ *fence = sp<Fence>::make();
result = reply.read(**fence);
if (result != NO_ERROR) {
fence->clear();
@@ -293,7 +293,7 @@
if (result == NO_ERROR) {
bool nonNull = reply.readInt32();
if (nonNull) {
- *outBuffer = new GraphicBuffer;
+ *outBuffer = sp<GraphicBuffer>::make();
result = reply.read(**outBuffer);
if (result != NO_ERROR) {
outBuffer->clear();
@@ -302,7 +302,7 @@
}
nonNull = reply.readInt32();
if (nonNull) {
- *outFence = new Fence;
+ *outFence = sp<Fence>::make();
result = reply.read(**outFence);
if (result != NO_ERROR) {
outBuffer->clear();
@@ -640,7 +640,7 @@
bool hasBuffer = reply.readBool();
sp<GraphicBuffer> buffer;
if (hasBuffer) {
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
result = reply.read(*buffer);
if (result == NO_ERROR) {
result = reply.read(outTransformMatrix, sizeof(float) * 16);
@@ -650,7 +650,7 @@
ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
return result;
}
- sp<Fence> fence(new Fence);
+ sp<Fence> fence = sp<Fence>::make();
result = reply.read(*fence);
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -687,7 +687,7 @@
}
sp<GraphicBuffer> buffer;
if (hasBuffer) {
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
result = reply.read(*buffer);
if (result == NO_ERROR) {
result = reply.read(*outRect);
@@ -700,7 +700,7 @@
ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
return result;
}
- sp<Fence> fence(new Fence);
+ sp<Fence> fence = sp<Fence>::make();
result = reply.read(*fence);
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -1232,7 +1232,7 @@
}
case ATTACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- sp<GraphicBuffer> buffer = new GraphicBuffer();
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
status_t result = data.read(*buffer.get());
int slot = 0;
if (result == NO_ERROR) {
@@ -1250,7 +1250,7 @@
return result;
}
for (sp<GraphicBuffer>& buffer : buffers) {
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
result = data.read(*buffer.get());
if (result != NO_ERROR) {
return result;
@@ -1306,7 +1306,7 @@
case CANCEL_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
- sp<Fence> fence = new Fence();
+ sp<Fence> fence = sp<Fence>::make();
status_t result = data.read(*fence.get());
if (result == NO_ERROR) {
result = cancelBuffer(buf, fence);
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index 8b2e2dd..393e1c3 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -105,7 +105,7 @@
: std::nullopt;
#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
- fence = new Fence();
+ fence = sp<Fence>::make();
status_t result = fence->unflatten(buffer, size, fds, count);
if (result != NO_ERROR) {
return result;
@@ -228,7 +228,7 @@
FlattenableUtils::read(fBuffer, size, result);
int32_t isBufferNull = 0;
FlattenableUtils::read(fBuffer, size, isBufferNull);
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
if (!isBufferNull) {
status_t status = buffer->unflatten(fBuffer, size, fds, count);
if (status != NO_ERROR) {
@@ -323,7 +323,7 @@
FlattenableUtils::read(buffer, size, slot);
FlattenableUtils::read(buffer, size, bufferAge);
- fence = new Fence();
+ fence = sp<Fence>::make();
status_t status = fence->unflatten(buffer, size, fds, count);
if (status != NO_ERROR) {
return status;
@@ -395,7 +395,7 @@
FlattenableUtils::read(buffer, size, slot);
- fence = new Fence();
+ fence = sp<Fence>::make();
return fence->unflatten(buffer, size, fds, count);
}
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 83fc827..ed28e79 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -92,7 +92,7 @@
if (err != NO_ERROR) return err;
if (hasFence) {
- gpuCompositionDoneFence = new Fence();
+ gpuCompositionDoneFence = sp<Fence>::make();
err = input->read(*gpuCompositionDoneFence);
if (err != NO_ERROR) return err;
}
@@ -157,7 +157,7 @@
SAFE_PARCEL(input->readBool, &hasFence);
if (hasFence) {
- previousReleaseFence = new Fence();
+ previousReleaseFence = sp<Fence>::make();
SAFE_PARCEL(input->read, *previousReleaseFence);
}
bool hasTransformHint = false;
@@ -216,7 +216,7 @@
return err;
}
if (hasFence) {
- presentFence = new Fence();
+ presentFence = sp<Fence>::make();
err = input->read(*presentFence);
if (err != NO_ERROR) {
return err;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index ebfc62f..ad95d1a 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,6 +55,28 @@
using gui::FocusRequest;
using gui::WindowInfoHandle;
+namespace {
+bool isSameWindowHandle(const sp<WindowInfoHandle>& lhs, const sp<WindowInfoHandle>& rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+
+ if (!lhs || !rhs) {
+ return false;
+ }
+
+ return *lhs->getInfo() == *rhs->getInfo();
+};
+
+bool isSameSurfaceControl(const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+
+ return SurfaceControl::isSameSurface(lhs, rhs);
+};
+} // namespace
+
layer_state_t::layer_state_t()
: surface(nullptr),
layerId(-1),
@@ -73,7 +95,6 @@
transformToDisplayInverse(false),
crop({0, 0, -1, -1}),
dataspace(ui::Dataspace::UNKNOWN),
- surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
bgColor(0),
@@ -117,19 +138,21 @@
SAFE_PARCEL(output.writeFloat, crop.left);
SAFE_PARCEL(output.writeFloat, crop.bottom);
SAFE_PARCEL(output.writeFloat, crop.right);
- SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
- SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+ mNotDefCmpState.relativeLayerSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+ mNotDefCmpState.parentSurfaceControlForChild);
SAFE_PARCEL(output.writeFloat, color.r);
SAFE_PARCEL(output.writeFloat, color.g);
SAFE_PARCEL(output.writeFloat, color.b);
SAFE_PARCEL(output.writeFloat, color.a);
- SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
- SAFE_PARCEL(output.write, transparentRegion);
+ SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->writeToParcel, &output);
+ SAFE_PARCEL(output.write, mNotDefCmpState.transparentRegion);
SAFE_PARCEL(output.writeUint32, bufferTransform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
- SAFE_PARCEL(output.write, surfaceDamageRegion);
+ SAFE_PARCEL(output.write, mNotDefCmpState.surfaceDamageRegion);
SAFE_PARCEL(output.writeInt32, api);
if (sidebandStream) {
@@ -241,8 +264,10 @@
SAFE_PARCEL(input.readFloat, &crop.bottom);
SAFE_PARCEL(input.readFloat, &crop.right);
- SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
- SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+ &mNotDefCmpState.relativeLayerSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+ &mNotDefCmpState.parentSurfaceControlForChild);
float tmpFloat = 0;
SAFE_PARCEL(input.readFloat, &tmpFloat);
@@ -254,9 +279,9 @@
SAFE_PARCEL(input.readFloat, &tmpFloat);
color.a = tmpFloat;
- SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
+ SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->readFromParcel, &input);
- SAFE_PARCEL(input.read, transparentRegion);
+ SAFE_PARCEL(input.read, mNotDefCmpState.transparentRegion);
SAFE_PARCEL(input.readUint32, &bufferTransform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
@@ -265,7 +290,7 @@
dataspace = static_cast<ui::Dataspace>(tmpUint32);
SAFE_PARCEL(input.read, hdrMetadata);
- SAFE_PARCEL(input.read, surfaceDamageRegion);
+ SAFE_PARCEL(input.read, mNotDefCmpState.surfaceDamageRegion);
SAFE_PARCEL(input.readInt32, &api);
bool tmpBool = false;
@@ -583,7 +608,7 @@
}
if (other.what & eTransparentRegionChanged) {
what |= eTransparentRegionChanged;
- transparentRegion = other.transparentRegion;
+ mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion;
}
if (other.what & eFlagsChanged) {
what |= eFlagsChanged;
@@ -615,11 +640,13 @@
what |= eRelativeLayerChanged;
what &= ~eLayerChanged;
z = other.z;
- relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
+ mNotDefCmpState.relativeLayerSurfaceControl =
+ other.mNotDefCmpState.relativeLayerSurfaceControl;
}
if (other.what & eReparent) {
what |= eReparent;
- parentSurfaceControlForChild = other.parentSurfaceControlForChild;
+ mNotDefCmpState.parentSurfaceControlForChild =
+ other.mNotDefCmpState.parentSurfaceControlForChild;
}
if (other.what & eBufferTransformChanged) {
what |= eBufferTransformChanged;
@@ -665,7 +692,7 @@
}
if (other.what & eSurfaceDamageRegionChanged) {
what |= eSurfaceDamageRegionChanged;
- surfaceDamageRegion = other.surfaceDamageRegion;
+ mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion;
}
if (other.what & eApiChanged) {
what |= eApiChanged;
@@ -684,7 +711,8 @@
}
if (other.what & eInputInfoChanged) {
what |= eInputInfoChanged;
- windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
+ mNotDefCmpState.windowInfoHandle =
+ sp<WindowInfoHandle>::make(*other.mNotDefCmpState.windowInfoHandle);
}
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
@@ -807,7 +835,8 @@
CHECK_DIFF(diff, eAlphaChanged, other, color.a);
CHECK_DIFF(diff, eMatrixChanged, other, matrix);
if (other.what & eTransparentRegionChanged &&
- (!transparentRegion.hasSameRects(other.transparentRegion))) {
+ (!mNotDefCmpState.transparentRegion.hasSameRects(
+ other.mNotDefCmpState.transparentRegion))) {
diff |= eTransparentRegionChanged;
}
if (other.what & eFlagsChanged) {
@@ -824,8 +853,8 @@
diff &= ~eLayerChanged;
}
if (other.what & eReparent &&
- !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
- other.parentSurfaceControlForChild)) {
+ !SurfaceControl::isSameSurface(mNotDefCmpState.parentSurfaceControlForChild,
+ other.mNotDefCmpState.parentSurfaceControlForChild)) {
diff |= eReparent;
}
CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
@@ -839,7 +868,8 @@
CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint);
CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
if (other.what & eSurfaceDamageRegionChanged &&
- (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+ (!mNotDefCmpState.surfaceDamageRegion.hasSameRects(
+ other.mNotDefCmpState.surfaceDamageRegion))) {
diff |= eSurfaceDamageRegionChanged;
}
CHECK_DIFF(diff, eApiChanged, other, api);
@@ -901,6 +931,38 @@
SAFE_PARCEL(input.readFloat, &dsdy);
return NO_ERROR;
}
+void layer_state_t::updateTransparentRegion(const Region& transparentRegion) {
+ what |= eTransparentRegionChanged;
+ mNotDefCmpState.transparentRegion = transparentRegion;
+}
+void layer_state_t::updateSurfaceDamageRegion(const Region& surfaceDamageRegion) {
+ what |= eSurfaceDamageRegionChanged;
+ mNotDefCmpState.surfaceDamageRegion = surfaceDamageRegion;
+}
+void layer_state_t::updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z) {
+ what |= layer_state_t::eRelativeLayerChanged;
+ what &= ~layer_state_t::eLayerChanged;
+ mNotDefCmpState.relativeLayerSurfaceControl = relativeTo;
+ this->z = z;
+}
+void layer_state_t::updateParentLayer(const sp<SurfaceControl>& newParent) {
+ what |= layer_state_t::eReparent;
+ mNotDefCmpState.parentSurfaceControlForChild =
+ newParent ? newParent->getParentingLayer() : nullptr;
+}
+void layer_state_t::updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info) {
+ what |= eInputInfoChanged;
+ mNotDefCmpState.windowInfoHandle = std::move(info);
+}
+
+bool layer_state_t::NotDefaultComparableState::operator==(
+ const NotDefaultComparableState& rhs) const {
+ return transparentRegion.hasSameRects(rhs.transparentRegion) &&
+ surfaceDamageRegion.hasSameRects(rhs.surfaceDamageRegion) &&
+ isSameWindowHandle(windowInfoHandle, rhs.windowInfoHandle) &&
+ isSameSurfaceControl(relativeLayerSurfaceControl, rhs.relativeLayerSurfaceControl) &&
+ isSameSurfaceControl(parentSurfaceControlForChild, rhs.parentSurfaceControlForChild);
+}
// ------------------------------- InputWindowCommands ----------------------------------------
@@ -1001,13 +1063,13 @@
bool tmpBool = false;
SAFE_PARCEL(input->readBool, &tmpBool);
if (tmpBool) {
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
SAFE_PARCEL(input->read, *buffer);
}
SAFE_PARCEL(input->readBool, &tmpBool);
if (tmpBool) {
- acquireFence = new Fence();
+ acquireFence = sp<Fence>::make();
SAFE_PARCEL(input->read, *acquireFence);
}
@@ -1034,8 +1096,8 @@
}
status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const {
- SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface);
- SAFE_PARCEL(parcel->writeInt32, callbackId);
+ SAFE_PARCEL(parcel->writeStrongBinder, mState.callbackInterface);
+ SAFE_PARCEL(parcel->writeInt32, mState.callbackId);
return NO_ERROR;
}
@@ -1043,9 +1105,9 @@
sp<IBinder> tmpBinder = nullptr;
SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder);
if (tmpBinder) {
- callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+ mState.callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
}
- SAFE_PARCEL(parcel->readInt32, &callbackId);
+ SAFE_PARCEL(parcel->readInt32, &mState.callbackId);
return NO_ERROR;
}
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index 2de023e..30b5785 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -18,6 +18,7 @@
#include <private/gui/ParcelUtils.h>
#include <ui/FenceResult.h>
+#include <ui/GraphicBuffer.h>
namespace android::gui {
@@ -54,7 +55,7 @@
bool hasGraphicBuffer;
SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
if (hasGraphicBuffer) {
- buffer = new GraphicBuffer();
+ buffer = sp<GraphicBuffer>::make();
SAFE_PARCEL(parcel->read, *buffer);
}
@@ -79,7 +80,7 @@
bool hasGainmap;
SAFE_PARCEL(parcel->readBool, &hasGainmap);
if (hasGainmap) {
- optionalGainMap = new GraphicBuffer();
+ optionalGainMap = sp<GraphicBuffer>::make();
SAFE_PARCEL(parcel->read, *optionalGainMap);
}
SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio);
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 653b91b..848ae7a 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -47,7 +47,7 @@
return BAD_VALUE;
}
- sp<StreamSplitter> splitter(new StreamSplitter(inputQueue));
+ sp<StreamSplitter> splitter = sp<StreamSplitter>::make(inputQueue);
status_t status = splitter->mInput->consumerConnect(splitter, false);
if (status == NO_ERROR) {
splitter->mInput->setConsumerName(String8("StreamSplitter"));
@@ -82,7 +82,7 @@
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
- sp<OutputListener> listener(new OutputListener(this, outputQueue));
+ sp<OutputListener> listener = sp<OutputListener>::make(this, outputQueue);
IInterface::asBinder(outputQueue)->linkToDeath(listener);
status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU,
/* producerControlledByApp */ false, &queueBufferOutput);
@@ -140,7 +140,7 @@
// Initialize our reference count for this buffer
mBuffers.add(bufferItem.mGraphicBuffer->getId(),
- new BufferTracker(bufferItem.mGraphicBuffer));
+ sp<BufferTracker>::make(bufferItem.mGraphicBuffer));
IGraphicBufferProducer::QueueBufferInput queueInput(
bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ec23365..63dcfbc 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -509,7 +509,7 @@
if (result != OK) {
return result;
}
- sp<Fence> fence(new Fence(fenceFd));
+ sp<Fence> fence = sp<Fence>::make(fenceFd);
int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
if (waitResult != OK) {
ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
@@ -979,7 +979,7 @@
}
return OK;
}
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
mGraphicBufferProducer->cancelBuffer(i, fence);
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
@@ -1017,7 +1017,7 @@
ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
badSlotResult = slot;
} else {
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
cancelBufferInputs[numBuffersCancelled].slot = slot;
cancelBufferInputs[numBuffersCancelled++].fence = fence;
}
@@ -1078,7 +1078,7 @@
Rect crop(Rect::EMPTY_RECT);
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -2092,7 +2092,7 @@
}
int Surface::connect(int api) {
- static sp<SurfaceListener> listener = new StubSurfaceListener();
+ static sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
return connect(api, listener);
}
@@ -2104,7 +2104,7 @@
mReportRemovedBuffers = reportBufferRemoval;
if (listener != nullptr) {
- mListenerProxy = new ProducerListenerProxy(this, listener);
+ mListenerProxy = sp<ProducerListenerProxy>::make(this, listener);
}
int err =
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 37ed23b..e407a63 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -123,7 +123,7 @@
explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
- mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
+ mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerService*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
return true;
}
@@ -170,7 +170,7 @@
explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {}
};
- mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this));
+ mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerServiceAIDL*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
return true;
}
@@ -202,7 +202,7 @@
DefaultComposerClient& dc = DefaultComposerClient::getInstance();
Mutex::Autolock _l(dc.mLock);
if (dc.mClient == nullptr) {
- dc.mClient = new SurfaceComposerClient;
+ dc.mClient = sp<SurfaceComposerClient>::make();
}
return dc.mClient;
}
@@ -399,7 +399,7 @@
sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
std::lock_guard<std::mutex> lock(sListenerInstanceMutex);
if (sInstance == nullptr) {
- sInstance = new TransactionCompletedListener;
+ sInstance = sp<TransactionCompletedListener>::make();
}
return sInstance;
}
@@ -691,7 +691,7 @@
std::scoped_lock<std::mutex> lock(mMutex);
mTrustedPresentationCallbacks[id] =
std::tuple<TrustedPresentationCallback, void*>(tpc, context);
- return new SurfaceComposerClient::PresentationCallbackRAII(this, id);
+ return sp<SurfaceComposerClient::PresentationCallbackRAII>::make(this, id);
}
void TransactionCompletedListener::clearTrustedPresentationCallback(int id) {
@@ -743,7 +743,7 @@
*/
class BufferCache : public Singleton<BufferCache> {
public:
- BufferCache() : token(new BBinder()) {}
+ BufferCache() : token(sp<BBinder>::make()) {}
sp<IBinder> getToken() {
return IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -1357,7 +1357,7 @@
return binderStatus;
}
-sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make();
std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex;
@@ -1391,11 +1391,16 @@
// ---------------------------------------------------------------------------
sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
- bool isSecure, const std::string& uniqueId,
+ bool isSecure, bool optimizeForPower,
+ const std::string& uniqueId,
float requestedRefreshRate) {
+ const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower
+ ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower
+ : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
sp<IBinder> display = nullptr;
binder::Status status =
ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure,
+ optimizationPolicy,
uniqueId,
requestedRefreshRate,
&display);
@@ -1523,11 +1528,7 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eRelativeLayerChanged;
- s->what &= ~layer_state_t::eLayerChanged;
- s->relativeLayerSurfaceControl = relativeTo;
- s->z = z;
-
+ s->updateRelativeLayer(relativeTo, z);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1557,9 +1558,7 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eTransparentRegionChanged;
- s->transparentRegion = transparentRegion;
-
+ s->updateTransparentRegion(transparentRegion);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1721,9 +1720,7 @@
if (SurfaceControl::isSameSurface(sc, newParent)) {
return *this;
}
- s->what |= layer_state_t::eReparent;
- s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
-
+ s->updateParentLayer(newParent);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -1953,9 +1950,9 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
- const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
- const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions,
- const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) {
+ const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets,
+ const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes,
+ const std::vector<int32_t>& samplingKeys) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1964,8 +1961,8 @@
s->what |= layer_state_t::eLutsChanged;
if (lutFd.ok()) {
- s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
- dimensions, sizes, samplingKeys);
+ s->luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), offsets, dimensions, sizes,
+ samplingKeys);
} else {
s->luts = nullptr;
}
@@ -2009,9 +2006,7 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eSurfaceDamageRegionChanged;
- s->surfaceDamageRegion = surfaceDamageRegion;
-
+ s->updateSurfaceDamageRegion(surfaceDamageRegion);
registerSurfaceControlForCallback(sc);
return *this;
}
@@ -2130,21 +2125,20 @@
mStatus = BAD_INDEX;
return *this;
}
- s->windowInfoHandle = std::move(info);
- s->what |= layer_state_t::eInputInfoChanged;
+ s->updateInputWindowInfo(std::move(info));
return *this;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
- mInputWindowCommands.focusRequests.push_back(request);
+ mInputWindowCommands.addFocusRequest(request);
return *this;
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
- mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener);
+ mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
return *this;
}
@@ -2364,10 +2358,6 @@
return *this;
}
-bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() {
- return com::android::graphics::libgui::flags::edge_extension_shader();
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect(
const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) {
layer_state_t* s = getLayerState(sc);
@@ -2572,8 +2562,9 @@
}
s->what |= layer_state_t::eTrustedPresentationInfoChanged;
s->trustedPresentationThresholds = thresholds;
- s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance();
- s->trustedPresentationListener.callbackId = sc->getLayerId();
+ s->trustedPresentationListener.configure(
+ {.callbackInterface = TransactionCompletedListener::getIInstance(),
+ .callbackId = sc->getLayerId()});
return *this;
}
@@ -2589,8 +2580,7 @@
}
s->what |= layer_state_t::eTrustedPresentationInfoChanged;
s->trustedPresentationThresholds = TrustedPresentationThresholds();
- s->trustedPresentationListener.callbackInterface = nullptr;
- s->trustedPresentationListener.callbackId = -1;
+ s->trustedPresentationListener.clear();
return *this;
}
@@ -2683,9 +2673,9 @@
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, result.handle, result.layerId,
- toString(result.layerName), w, h, format,
- result.transformHint, flags);
+ *outSurface = sp<SurfaceControl>::make(this, result.handle, result.layerId,
+ toString(result.layerName), w, h, format,
+ result.transformHint, flags);
}
}
return err;
@@ -2701,7 +2691,8 @@
const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result);
const status_t err = statusTFromBinderStatus(status);
if (err == NO_ERROR) {
- return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+ return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+ toString(result.layerName));
}
return nullptr;
}
@@ -2711,7 +2702,8 @@
const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
const status_t err = statusTFromBinderStatus(status);
if (err == NO_ERROR) {
- return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+ return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+ toString(result.layerName));
}
return nullptr;
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index b735418..1eb9b87 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -269,10 +269,11 @@
SAFE_PARCEL(parcel.readUint32, &format);
// We aren't the original owner of the surface.
- *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), layerId, layerName, width, height, format,
- transformHint);
+ *outSurfaceControl =
+ sp<SurfaceControl>::make(sp<SurfaceComposerClient>::make(
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle, layerId, layerName, width, height, format,
+ transformHint);
return NO_ERROR;
}
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index a590c86..14d6df6 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -60,5 +60,34 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "libgui_test",
+ "keywords": [ "primary-device" ],
+ "options": [
+ // TODO(b/397776630): Failing on real devices.
+ {
+ "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow"
+ },
+ // TODO(b/233363648): Failing on real devices.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+ },
+ // TODO(b/233363648): Flaky on real devices.
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers"
+ }
+ ]
+ }
]
}
diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp
new file mode 100644
index 0000000..9e09bc2
--- /dev/null
+++ b/libs/gui/TransactionState.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2025 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 "TransactionState"
+#include <gui/LayerState.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/TransactionState.h>
+#include <private/gui/ParcelUtils.h>
+#include <algorithm>
+
+namespace android {
+
+status_t TransactionState::writeToParcel(Parcel* parcel) const {
+ SAFE_PARCEL(parcel->writeUint64, mId);
+ SAFE_PARCEL(parcel->writeUint32, mFlags);
+ SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
+ SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
+ SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
+ SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
+ SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
+ SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
+ for (auto const& displayState : mDisplayStates) {
+ displayState.write(*parcel);
+ }
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
+ for (auto const& composerState : mComposerStates) {
+ composerState.write(*parcel);
+ }
+
+ mInputWindowCommands.write(*parcel);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+ SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+ for (auto mergedTransactionId : mMergedTransactionIds) {
+ SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+ }
+
+ SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
+ for (const auto& [listener, callbackIds] : mListenerCallbacks) {
+ SAFE_PARCEL(parcel->writeStrongBinder, listener);
+ SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionState::readFromParcel(const Parcel* parcel) {
+ SAFE_PARCEL(parcel->readUint64, &mId);
+ SAFE_PARCEL(parcel->readUint32, &mFlags);
+ SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
+ SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
+ SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
+ SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
+ SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
+ SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
+
+ uint32_t count;
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mDisplayStates.clear();
+ mDisplayStates.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ DisplayState displayState;
+ if (displayState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ mDisplayStates.emplace_back(std::move(displayState));
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mComposerStates.clear();
+ mComposerStates.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ ComposerState composerState;
+ if (composerState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ mComposerStates.emplace_back(std::move(composerState));
+ }
+
+ if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
+ return status;
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mUncacheBuffers.clear();
+ mUncacheBuffers.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ client_cache_t client_cache;
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ client_cache.token = tmpBinder;
+ SAFE_PARCEL(parcel->readUint64, &client_cache.id);
+ mUncacheBuffers.emplace_back(std::move(client_cache));
+ }
+
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+ mMergedTransactionIds.clear();
+ mMergedTransactionIds.resize(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
+ }
+
+ SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
+ SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
+ mListenerCallbacks.clear();
+ mListenerCallbacks.reserve(count);
+ for (uint32_t i = 0; i < count; i++) {
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ std::vector<CallbackId> callbackIds;
+ SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
+ mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
+ }
+
+ return NO_ERROR;
+}
+
+void TransactionState::merge(TransactionState&& other,
+ const std::function<void(layer_state_t&)>& onBufferOverwrite) {
+ while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+ MAX_MERGE_HISTORY_LENGTH - 1 &&
+ mMergedTransactionIds.size() > 0) {
+ mMergedTransactionIds.pop_back();
+ }
+ if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end() - 1);
+ } else if (other.mMergedTransactionIds.size() > 0u) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end());
+ }
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+ for (auto const& otherState : other.mComposerStates) {
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&otherState](const auto& composerState) {
+ return composerState.state.surface ==
+ otherState.state.surface;
+ });
+ it != mComposerStates.end()) {
+ if (otherState.state.what & layer_state_t::eBufferChanged) {
+ onBufferOverwrite(it->state);
+ }
+ it->state.merge(otherState.state);
+ } else {
+ mComposerStates.push_back(otherState);
+ }
+ }
+
+ for (auto const& state : other.mDisplayStates) {
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [&state](const auto& displayState) {
+ return displayState.token == state.token;
+ });
+ it != mDisplayStates.end()) {
+ it->merge(state);
+ } else {
+ mDisplayStates.push_back(state);
+ }
+ }
+
+ for (const auto& cacheId : other.mUncacheBuffers) {
+ mUncacheBuffers.push_back(cacheId);
+ }
+
+ mInputWindowCommands.merge(other.mInputWindowCommands);
+ // TODO(b/385156191) Consider merging desired present time.
+ mFlags |= other.mFlags;
+ mMayContainBuffer |= other.mMayContainBuffer;
+ mLogCallPoints |= other.mLogCallPoints;
+
+ // mApplyToken is explicitly not merged. Token should be set before applying the transactions to
+ // make synchronization decisions a bit simpler.
+ mergeFrameTimelineInfo(other.mFrameTimelineInfo);
+ other.clear();
+}
+
+// copied from FrameTimelineInfo::merge()
+void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
+ // When merging vsync Ids we take the oldest valid one
+ if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+ other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
+ mFrameTimelineInfo = other;
+ }
+ } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ mFrameTimelineInfo = other;
+ }
+}
+
+void TransactionState::clear() {
+ mComposerStates.clear();
+ mDisplayStates.clear();
+ mListenerCallbacks.clear();
+ mHasListenerCallbacks = false;
+ mInputWindowCommands.clear();
+ mUncacheBuffers.clear();
+ mDesiredPresentTime = 0;
+ mIsAutoTimestamp = true;
+ mApplyToken = nullptr;
+ mFrameTimelineInfo = {};
+ mMergedTransactionIds.clear();
+ mFlags = 0;
+ mMayContainBuffer = false;
+ mLogCallPoints = false;
+}
+
+layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
+ auto handle = sc->getLayerStateHandle();
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&handle](const auto& composerState) {
+ return composerState.state.surface == handle;
+ });
+ it != mComposerStates.end()) {
+ return &it->state;
+ }
+
+ // we don't have it, add an initialized layer_state to our list
+ ComposerState s;
+ s.state.surface = handle;
+ s.state.layerId = sc->getLayerId();
+ mComposerStates.push_back(s);
+
+ return &mComposerStates.back().state;
+}
+
+DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [token](const auto& display) { return display.token == token; });
+ it != mDisplayStates.end()) {
+ return *it;
+ }
+
+ // If display state doesn't exist, add a new one.
+ DisplayState s;
+ s.token = token;
+ mDisplayStates.push_back(s);
+ return mDisplayStates.back();
+}
+
+}; // namespace android
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 91c9a85..d633f9f 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -15,6 +15,7 @@
*/
#include <android/gui/ISurfaceComposer.h>
+#include <android/gui/IWindowInfosListener.h>
#include <gui/AidlUtil.h>
#include <gui/WindowInfosListenerReporter.h>
#include "gui/WindowInfosUpdate.h"
@@ -27,7 +28,7 @@
using gui::aidl_utils::statusTFromBinderStatus;
sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() {
- static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter;
+ static sp<WindowInfosListenerReporter> sInstance = sp<WindowInfosListenerReporter>::make();
return sInstance;
}
@@ -116,7 +117,8 @@
std::scoped_lock lock(mListenersMutex);
if (!mWindowInfosListeners.empty()) {
gui::WindowInfosListenerInfo listenerInfo;
- composerService->addWindowInfosListener(this, &listenerInfo);
+ composerService->addWindowInfosListener(sp<gui::IWindowInfosListener>::fromExisting(this),
+ &listenerInfo);
mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
mListenerId = listenerInfo.listenerId;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index da47ee2..9b2f089 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -67,6 +67,11 @@
frameRateOverride = 1 << 1,
}
+ enum OptimizationPolicy {
+ optimizeForPower = 0,
+ optimizeForPerformance = 1,
+ }
+
/**
* Signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -97,6 +102,10 @@
* The name of the virtual display.
* isSecure
* Whether this virtual display is secure.
+ * optimizationPolicy
+ * Whether to optimize for power or performance. Displays that are optimizing for power may
+ * be dependent on a different display that optimizes for performance when they are on,
+ * which will guarantee performance for all of the other displays.
* uniqueId
* The unique ID for the display.
* requestedRefreshRate
@@ -108,7 +117,7 @@
* requires ACCESS_SURFACE_FLINGER permission.
*/
@nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure,
- @utf8InCpp String uniqueId, float requestedRefreshRate);
+ OptimizationPolicy optimizationPolicy, @utf8InCpp String uniqueId, float requestedRefreshRate);
/**
* Destroy a virtual display.
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index fd8ffe1..b1a23b3 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -971,7 +971,7 @@
// H2BGraphicBufferProducer
status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- *buf = new GraphicBuffer();
+ *buf = sp<GraphicBuffer>::make();
status_t fnStatus;
status_t transStatus = toStatusT(mBase->requestBuffer(
static_cast<int32_t>(slot),
@@ -999,7 +999,7 @@
uint32_t h, ::android::PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
- *fence = new Fence();
+ *fence = sp<Fence>::make();
status_t fnStatus;
status_t transStatus = toStatusT(mBase->dequeueBuffer(
w, h, static_cast<PixelFormat>(format), uint32_t(usage),
@@ -1035,8 +1035,8 @@
status_t H2BGraphicBufferProducer::detachNextBuffer(
sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
- *outBuffer = new GraphicBuffer();
- *outFence = new Fence();
+ *outBuffer = sp<GraphicBuffer>::make();
+ *outFence = sp<Fence>::make();
status_t fnStatus;
status_t transStatus = toStatusT(mBase->detachNextBuffer(
[&fnStatus, outBuffer, outFence] (
@@ -1127,8 +1127,8 @@
status_t H2BGraphicBufferProducer::connect(
const sp<IProducerListener>& listener, int api,
bool producerControlledByApp, QueueBufferOutput* output) {
- sp<HProducerListener> tListener = listener == nullptr ?
- nullptr : new B2HProducerListener(listener);
+ sp<HProducerListener> tListener =
+ listener == nullptr ? nullptr : sp<B2HProducerListener>::make(listener);
status_t fnStatus;
status_t transStatus = toStatusT(mBase->connect(
tListener, static_cast<int32_t>(api), producerControlledByApp,
@@ -1205,13 +1205,13 @@
hidl_handle const& fence,
hidl_array<float, 16> const& transformMatrix) {
fnStatus = toStatusT(status);
- *outBuffer = new GraphicBuffer();
+ *outBuffer = sp<GraphicBuffer>::make();
if (!convertTo(outBuffer->get(), buffer)) {
ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
"Invalid output buffer");
fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
}
- *outFence = new Fence();
+ *outFence = sp<Fence>::make();
if (!convertTo(outFence->get(), fence)) {
ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
"Invalid output fence");
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index c76d771..4384bd5 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -272,7 +272,7 @@
HConnectionType hConnectionType,
bool producerControlledByApp,
connect_cb _hidl_cb) {
- sp<BProducerListener> bListener = new H2BProducerListener(hListener);
+ sp<BProducerListener> bListener = sp<H2BProducerListener>::make(hListener);
int bConnectionType{};
if (!bListener || !h2b(hConnectionType, &bConnectionType)) {
_hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
index ae00a26..7121bb7 100644
--- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -325,7 +325,7 @@
}
sp<HProducerListener> hListener = nullptr;
if (listener && listener->needsReleaseNotify()) {
- hListener = new B2HProducerListener(listener);
+ hListener = sp<B2HProducerListener>::make(listener);
if (!hListener) {
LOG(ERROR) << "connect: failed to wrap listener.";
return UNKNOWN_ERROR;
diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp
index cbd6cad..c245766 100644
--- a/libs/gui/bufferqueue/2.0/types.cpp
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -147,13 +147,13 @@
bool h2b(native_handle_t const* from, sp<BFence>* to) {
if (!from || from->numFds == 0) {
- *to = new ::android::Fence();
+ *to = sp<::android::Fence>::make();
return true;
}
if (from->numFds != 1 || from->numInts != 0) {
return false;
}
- *to = new BFence(dup(from->data[0]));
+ *to = sp<BFence>::make(dup(from->data[0]));
return true;
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index cdc2150..db1b9fb 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -46,21 +46,6 @@
class BLASTBufferItemConsumer : public BufferItemConsumer {
public:
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
- const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
- int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
- : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
-#else
- BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
- int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
- : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- mBLASTBufferQueue(std::move(bbq)),
- mCurrentlyConnected(false),
- mPreviouslyConnected(false) {
- }
-
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex);
@@ -81,6 +66,23 @@
#endif
private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+ : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
+ BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+ : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBLASTBufferQueue(std::move(bbq)),
+ mCurrentlyConnected(false),
+ mPreviouslyConnected(false) {
+ }
+
+ friend class sp<BLASTBufferItemConsumer>;
+
const wp<BLASTBufferQueue> mBLASTBufferQueue;
uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0;
@@ -94,8 +96,6 @@
class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener {
public:
- BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
-
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
}
@@ -158,8 +158,13 @@
void onFirstRef() override;
private:
+ // Not public to ensure construction via sp<>::make().
+ BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
+
+ friend class sp<BLASTBufferQueue>;
friend class BLASTBufferQueueHelper;
friend class BBQBufferQueueProducer;
+ friend class TestBLASTBufferQueue;
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
friend class BBQBufferQueueCore;
#endif
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index 6810eda..0bfa7b2 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -47,6 +47,16 @@
enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
+ static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create(
+ uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+
+ static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
// Create a new buffer item consumer. The consumerUsage parameter determines
// the consumer usage flags passed to the graphics allocator. The
// bufferCount parameter specifies how many buffers can be locked for user
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index a93ba14..5862967 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -103,7 +103,7 @@
virtual void handleMessage(const Message& message) override;
static void initJVM(JNIEnv* env);
- static Choreographer* getForThread();
+ static sp<Choreographer> getForThread();
static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index acb0006..fd67f09 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -98,6 +98,8 @@
status_t detachBuffer(const sp<GraphicBuffer>& buffer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ status_t addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence);
+
// See IGraphicBufferConsumer::setDefaultBufferSize
status_t setDefaultBufferSize(uint32_t width, uint32_t height);
@@ -121,9 +123,7 @@
// See IGraphicBufferConsumer::setMaxAcquiredBufferCount
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
status_t setConsumerIsProtected(bool isProtected);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// See IGraphicBufferConsumer::getSidebandStream
sp<NativeHandle> getSidebandStream() const;
@@ -139,7 +139,8 @@
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
- void initialize(bool controlledByApp);
+ // Requires `this` to be sp/wp so must not be called from ctor.
+ void initialize();
protected:
// ConsumerBase constructs a new ConsumerBase object to consume image
@@ -252,6 +253,10 @@
const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR);
#endif
+ // Required to complete initialization, so `final` lest overrides forget to
+ // delegate.
+ void onFirstRef() override final;
+
// returns true iff the slot still has the graphicBuffer in it.
bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
@@ -327,6 +332,8 @@
// releaseBufferLocked.
sp<Fence> mPrevFinalReleaseFence;
+ const bool mIsControlledByApp;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed or when any of the *Locked methods are
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 2bba61b..995cdfb 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -31,6 +31,7 @@
class BufferQueue;
class GraphicBuffer;
class String8;
+class Surface;
/**
* CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
@@ -92,6 +93,13 @@
// Create a new CPU consumer. The maxLockedBuffers parameter specifies
// how many buffers can be locked for user access at the same time.
+ static std::tuple<sp<CpuConsumer>, sp<Surface>> create(size_t maxLockedBuffers,
+ bool controlledByApp = false,
+ bool isConsumerSurfaceFlinger = false);
+ static sp<CpuConsumer> create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
bool isConsumerSurfaceFlinger = false);
@@ -100,8 +108,8 @@
bool controlledByApp = false)
__attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
#else
- CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
- size_t maxLockedBuffers, bool controlledByApp = false);
+ CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp = false);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Gets the next graphics buffer from the producer and locks it for CPU use,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index ab6a6b7..f51390a 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -55,20 +55,20 @@
static_cast<uint32_t>(c4);
}
+enum class DisplayEventType : uint32_t {
+ DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
+ DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
+ DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
+ DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
+ DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
+ DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
+ DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
+};
+
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
- enum {
- DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
- DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
- DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
- DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
- DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
- DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
- DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
- DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
- };
-
struct Event {
// We add __attribute__((aligned(8))) for nsecs_t fields because
// we need to make sure all fields are aligned the same with x86
@@ -77,7 +77,7 @@
// https://en.wikipedia.org/wiki/Data_structure_alignment
struct Header {
- uint32_t type;
+ DisplayEventType type;
PhysicalDisplayId displayId __attribute__((aligned(8)));
nsecs_t timestamp __attribute__((aligned(8)));
};
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index ab86ac4..187381c 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -18,6 +18,10 @@
#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+#include <algorithm>
+#include <ostream>
#include <vector>
namespace android::gui {
@@ -62,4 +66,99 @@
base::unique_fd fd;
}; // struct DisplayLuts
+static inline void PrintTo(const std::vector<int32_t>& offsets, ::std::ostream* os) {
+ *os << "\n .offsets = {";
+ for (size_t i = 0; i < offsets.size(); i++) {
+ *os << offsets[i];
+ if (i != offsets.size() - 1) {
+ *os << ", ";
+ }
+ }
+ *os << "}";
+}
+
+static inline void PrintTo(const std::vector<DisplayLuts::Entry>& entries, ::std::ostream* os) {
+ *os << "\n .lutProperties = {\n";
+ for (auto& [dimension, size, samplingKey] : entries) {
+ *os << " Entry{"
+ << "dimension: " << dimension << ", size: " << size << ", samplingKey: " << samplingKey
+ << "}\n";
+ }
+ *os << " }";
+}
+
+static constexpr size_t kMaxPrintCount = 100;
+
+static inline void PrintTo(const std::vector<float>& buffer, size_t offset, int32_t dimension,
+ size_t size, ::std::ostream* os) {
+ size_t range = std::min(size, kMaxPrintCount);
+ *os << "{";
+ if (dimension == 1) {
+ for (size_t i = 0; i < range; i++) {
+ *os << buffer[offset + i];
+ if (i != range - 1) {
+ *os << ", ";
+ }
+ }
+ } else {
+ *os << "\n {R channel:";
+ for (size_t i = 0; i < range; i++) {
+ *os << buffer[offset + i];
+ if (i != range - 1) {
+ *os << ", ";
+ }
+ }
+ *os << "}\n {G channel:";
+ for (size_t i = 0; i < range; i++) {
+ *os << buffer[offset + size + i];
+ if (i != range - 1) {
+ *os << ", ";
+ }
+ }
+ *os << "}\n {B channel:";
+ for (size_t i = 0; i < range; i++) {
+ *os << buffer[offset + 2 * size + i];
+ if (i != range - 1) {
+ *os << ", ";
+ }
+ }
+ }
+ *os << "}";
+}
+
+static inline void PrintTo(const std::shared_ptr<DisplayLuts> luts, ::std::ostream* os) {
+ *os << "gui::DisplayLuts {";
+ auto& fd = luts->getLutFileDescriptor();
+ *os << "\n .pfd = " << fd.get();
+ if (fd.ok()) {
+ PrintTo(luts->offsets, os);
+ PrintTo(luts->lutProperties, os);
+ // decode luts
+ int32_t fullLength = luts->offsets[luts->offsets.size() - 1];
+ if (luts->lutProperties[luts->offsets.size() - 1].dimension == 1) {
+ fullLength += luts->lutProperties[luts->offsets.size() - 1].size;
+ } else {
+ fullLength += (luts->lutProperties[luts->offsets.size() - 1].size *
+ luts->lutProperties[luts->offsets.size() - 1].size *
+ luts->lutProperties[luts->offsets.size() - 1].size * 3);
+ }
+ size_t bufferSize = static_cast<size_t>(fullLength) * sizeof(float);
+ float* ptr = (float*)mmap(NULL, bufferSize, PROT_READ, MAP_SHARED, fd.get(), 0);
+ if (ptr == MAP_FAILED) {
+ *os << "\n .bufferdata cannot mmap!";
+ return;
+ }
+ std::vector<float> buffers(ptr, ptr + fullLength);
+ munmap(ptr, bufferSize);
+
+ *os << "\n .bufferdata = ";
+ for (size_t i = 0; i < luts->offsets.size(); i++) {
+ PrintTo(buffers, static_cast<size_t>(luts->offsets[i]),
+ luts->lutProperties[i].dimension,
+ static_cast<size_t>(luts->lutProperties[i].size), os);
+ }
+ }
+ *os << "\n }";
+}
+
} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index dbf707f..254d8ac 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -83,6 +83,20 @@
// If the constructor without the tex parameter is used, the GLConsumer is
// created in a detached state, and attachToContext must be called before
// calls to updateTexImage.
+ static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync,
+ bool isControlledByApp);
+ static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+ static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+ uint32_t textureTarget, bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated(
+ "Prefer create functions that create their own surface and consumer.")));
+ static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated(
+ "Prefer create functions that create their own surface and consumer.")));
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 7ee291d..6381db2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -44,6 +44,10 @@
LayerMetadata& operator=(const LayerMetadata& other);
LayerMetadata& operator=(LayerMetadata&& other);
+ // Note: `default` is not feasible because Parcelable does not provide ==.
+ bool operator==(const LayerMetadata& rhs) const { return mMap == rhs.mMap; }
+ bool operator!=(const LayerMetadata&) const = default;
+
// Merges other into this LayerMetadata. If eraseEmpty is true, any entries in
// in this whose keys are paired with empty values in other will be erased.
bool merge(const LayerMetadata& other, bool eraseEmpty = false);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index d04b861..369d3d1 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -17,9 +17,9 @@
#ifndef ANDROID_SF_LAYER_STATE_H
#define ANDROID_SF_LAYER_STATE_H
-
#include <stdint.h>
#include <sys/types.h>
+#include <span>
#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/IWindowInfosReportedListener.h>
@@ -69,21 +69,39 @@
uint64_t id;
bool operator==(const client_cache_t& other) const { return id == other.id; }
+ bool operator!=(const client_cache_t&) const = default;
bool isValid() const { return token != nullptr; }
};
class TrustedPresentationListener : public Parcelable {
public:
- sp<ITransactionCompletedListener> callbackInterface;
- int callbackId = -1;
+ struct State {
+ sp<ITransactionCompletedListener> callbackInterface;
+ int callbackId = -1;
+ bool operator==(const State&) const = default;
+ bool operator!=(const State&) const = default;
+ };
void invoke(bool presentedWithinThresholds) {
- callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds);
+ mState.callbackInterface->onTrustedPresentationChanged(mState.callbackId,
+ presentedWithinThresholds);
+ }
+ void configure(State&& state) { mState = std::move(state); }
+ const sp<ITransactionCompletedListener>& getCallback() { return mState.callbackInterface; }
+ void clear() {
+ mState.callbackInterface = nullptr;
+ mState.callbackId = -1;
}
status_t writeToParcel(Parcel* parcel) const;
status_t readFromParcel(const Parcel* parcel);
+
+ bool operator==(const TrustedPresentationListener& rhs) const { return mState == rhs.mState; }
+ bool operator!=(const TrustedPresentationListener&) const = default;
+
+private:
+ State mState;
};
class BufferData : public Parcelable {
@@ -309,6 +327,32 @@
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
+ void updateTransparentRegion(const Region& transparentRegion);
+ const Region& getTransparentRegion() const { return mNotDefCmpState.transparentRegion; }
+ void updateSurfaceDamageRegion(const Region& surfaceDamageRegion);
+ const Region& getSurfaceDamageRegion() const { return mNotDefCmpState.surfaceDamageRegion; }
+ // Do not update state flags. Used to set up test state.
+ void setSurfaceDamageRegion(Region&& surfaceDamageRegion) {
+ mNotDefCmpState.surfaceDamageRegion = std::move(surfaceDamageRegion);
+ }
+ void updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z);
+ void updateParentLayer(const sp<SurfaceControl>& newParent);
+ void updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info);
+ const gui::WindowInfo& getWindowInfo() const {
+ return *mNotDefCmpState.windowInfoHandle->getInfo();
+ }
+ gui::WindowInfo* editWindowInfo() { return mNotDefCmpState.windowInfoHandle->editInfo(); }
+
+ const sp<SurfaceControl>& getParentSurfaceControlForChild() const {
+ return mNotDefCmpState.parentSurfaceControlForChild;
+ }
+ const sp<SurfaceControl>& getRelativeLayerSurfaceControl() const {
+ return mNotDefCmpState.relativeLayerSurfaceControl;
+ }
+
+ bool operator==(const layer_state_t&) const = default;
+ bool operator!=(const layer_state_t&) const = default;
+
struct matrix22_t {
float dsdx{0};
float dtdx{0};
@@ -337,28 +381,20 @@
float clientDrawnCornerRadius;
uint32_t backgroundBlurRadius;
- sp<SurfaceControl> relativeLayerSurfaceControl;
-
- sp<SurfaceControl> parentSurfaceControlForChild;
-
half4 color;
// non POD must be last. see write/read
- Region transparentRegion;
uint32_t bufferTransform;
bool transformToDisplayInverse;
FloatRect crop;
std::shared_ptr<BufferData> bufferData = nullptr;
ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
- Region surfaceDamageRegion;
int32_t api;
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
std::vector<BlurRegion> blurRegions;
- sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
-
LayerMetadata metadata;
// The following refer to the alpha, and dataspace, respectively of
@@ -444,6 +480,18 @@
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
std::shared_ptr<gui::DisplayLuts> luts;
+
+protected:
+ struct NotDefaultComparableState {
+ Region transparentRegion;
+ Region surfaceDamageRegion;
+ sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+ sp<SurfaceControl> relativeLayerSurfaceControl;
+ sp<SurfaceControl> parentSurfaceControlForChild;
+
+ bool operator==(const NotDefaultComparableState& rhs) const;
+ bool operator!=(const NotDefaultComparableState& rhs) const = default;
+ } mNotDefCmpState;
};
class ComposerState {
@@ -451,6 +499,9 @@
layer_state_t state;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+
+ bool operator==(const ComposerState&) const = default;
+ bool operator!=(const ComposerState&) const = default;
};
struct DisplayState {
@@ -502,28 +553,49 @@
Rect layerStackSpaceRect = Rect::EMPTY_RECT;
Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
- // Exclusive to virtual displays: The sink surface into which the virtual display is rendered,
- // and an optional resolution that overrides its default dimensions.
- sp<IGraphicBufferProducer> surface;
+ // For physical displays, this is the resolution, which must match the active display mode. To
+ // change the resolution, the client must first call SurfaceControl.setDesiredDisplayModeSpecs
+ // with the new DesiredDisplayModeSpecs#defaultMode, then commit the matching width and height.
+ //
+ // For virtual displays, this is an optional resolution that overrides its default dimensions.
+ //
uint32_t width = 0;
uint32_t height = 0;
+ // For virtual displays, this is the sink surface into which the virtual display is rendered.
+ sp<IGraphicBufferProducer> surface;
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+
+ bool operator==(const DisplayState&) const = default;
+ bool operator!=(const DisplayState&) const = default;
};
struct InputWindowCommands {
- std::vector<gui::FocusRequest> focusRequests;
- std::unordered_set<sp<gui::IWindowInfosReportedListener>,
- SpHash<gui::IWindowInfosReportedListener>>
- windowInfosReportedListeners;
-
+ using Listener = gui::IWindowInfosReportedListener;
+ using ListenerSet = std::unordered_set<sp<Listener>, SpHash<Listener>>;
// Merges the passed in commands and returns true if there were any changes.
bool merge(const InputWindowCommands& other);
bool empty() const;
void clear();
+ void addFocusRequest(const gui::FocusRequest& request) { focusRequests.push_back(request); }
+ void addWindowInfosReportedListener(const sp<Listener>& listener) {
+ windowInfosReportedListeners.insert(listener);
+ }
+ ListenerSet&& releaseListeners() { return std::move(windowInfosReportedListeners); }
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+
+ std::span<const gui::FocusRequest> getFocusRequests() const { return focusRequests; }
+ const ListenerSet& getListeners() const { return windowInfosReportedListeners; }
+ bool operator==(const InputWindowCommands&) const = default;
+ bool operator!=(const InputWindowCommands&) const = default;
+
+private:
+ std::vector<gui::FocusRequest> focusRequests;
+ ListenerSet windowInfosReportedListeners;
};
static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h
index 28237b6..8176f75 100644
--- a/libs/gui/include/gui/StreamSplitter.h
+++ b/libs/gui/include/gui/StreamSplitter.h
@@ -153,6 +153,8 @@
size_t mReleaseCount;
};
+ friend class sp<StreamSplitter>;
+
// Only called from createSplitter
explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 10225cc..4fda8de 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -345,8 +345,6 @@
static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>
getDisplayDecorationSupport(const sp<IBinder>& displayToken);
- static bool flagEdgeExtensionEffectUseShader();
-
/**
* Returns how many picture profiles are supported by the display.
*
@@ -396,6 +394,7 @@
static const std::string kEmpty;
static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+ bool optimizeForPower = true,
const std::string& uniqueId = kEmpty,
float requestedRefreshRate = 0);
@@ -620,7 +619,7 @@
Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
float currentBufferRatio, float desiredRatio);
Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio);
- Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+ Transaction& setLuts(const sp<SurfaceControl>& sc, base::unique_fd&& lutFd,
const std::vector<int32_t>& offsets,
const std::vector<int32_t>& dimensions,
const std::vector<int32_t>& sizes,
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
new file mode 100644
index 0000000..4358227
--- /dev/null
+++ b/libs/gui/include/gui/TransactionState.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/FrameTimelineInfo.h>
+#include <binder/Parcelable.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+// Class to store all the transaction data and the parcelling logic
+class TransactionState {
+public:
+ explicit TransactionState() = default;
+ TransactionState(TransactionState const& other) = default;
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel* parcel);
+ layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+ DisplayState& getDisplayState(const sp<IBinder>& token);
+
+ // Returns the current id of the transaction.
+ // The id is updated every time the transaction is applied.
+ uint64_t getId() const { return mId; }
+ std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
+ void enableDebugLogCallPoints() { mLogCallPoints = true; }
+ void merge(TransactionState&& other,
+ const std::function<void(layer_state_t&)>& onBufferOverwrite);
+
+ // copied from FrameTimelineInfo::merge()
+ void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
+ void clear();
+ bool operator==(const TransactionState& rhs) const = default;
+ bool operator!=(const TransactionState& rhs) const = default;
+
+ uint64_t mId = 0;
+ std::vector<uint64_t> mMergedTransactionIds;
+ uint32_t mFlags = 0;
+ // The vsync id provided by Choreographer.getVsyncId and the input event id
+ gui::FrameTimelineInfo mFrameTimelineInfo;
+ // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+ // to be presented. When it is not possible to present at exactly that time, it will be
+ // presented after the time has passed.
+ //
+ // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+ // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+ //
+ // Desired present times that are more than 1 second in the future may be ignored.
+ // When a desired present time has already passed, the transaction will be presented as soon
+ // as possible.
+ //
+ // Transactions from the same process are presented in the same order that they are applied.
+ // The desired present time does not affect this ordering.
+ int64_t mDesiredPresentTime = 0;
+ bool mIsAutoTimestamp = true;
+ // If not null, transactions will be queued up using this token otherwise a common token
+ // per process will be used.
+ sp<IBinder> mApplyToken;
+ // Indicates that the Transaction may contain buffers that should be cached. The reason this
+ // is only a guess is that buffers can be removed before cache is called. This is only a
+ // hint that at some point a buffer was added to this transaction before apply was called.
+ bool mMayContainBuffer = false;
+ // Prints debug logs when enabled.
+ bool mLogCallPoints = false;
+
+ std::vector<DisplayState> mDisplayStates;
+ std::vector<ComposerState> mComposerStates;
+ InputWindowCommands mInputWindowCommands;
+ std::vector<client_cache_t> mUncacheBuffers;
+ // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
+ // empty.
+ bool mHasListenerCallbacks = false;
+ std::vector<ListenerCallbacks> mListenerCallbacks;
+
+private:
+ // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+ // Ordered most recently merged to least recently merged.
+ static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+};
+
+}; // namespace android
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 420dc21..9ac49c0 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -268,6 +268,7 @@
bool overlaps(const WindowInfo* other) const;
bool operator==(const WindowInfo& inputChannel) const;
+ bool operator!=(const WindowInfo&) const = default;
status_t writeToParcel(android::Parcel* parcel) const override;
@@ -319,6 +320,9 @@
status_t readFromParcel(const android::Parcel* parcel);
status_t writeToParcel(android::Parcel* parcel) const;
+ bool operator==(const WindowInfoHandle& rhs) const { return mInfo == rhs.mInfo; }
+ bool operator!=(const WindowInfoHandle&) const = default;
+
protected:
virtual ~WindowInfoHandle();
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 90d91ac..534f05e 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -77,14 +77,6 @@
} # wb_stream_splitter
flag {
- name: "edge_extension_shader"
- namespace: "windowing_frontend"
- description: "Enable edge extension via shader"
- bug: "322036393"
- is_fixed_read_only: true
-} # edge_extension_shader
-
-flag {
name: "buffer_release_channel"
namespace: "window_surfaces"
description: "Enable BufferReleaseChannel to optimize buffer releases"
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 87051a7..e20345d 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -90,6 +90,7 @@
"testserver/TestServerClient.cpp",
"testserver/TestServerHost.cpp",
"TextureRenderer.cpp",
+ "TransactionState_test.cpp",
"VsyncEventData_test.cpp",
"WindowInfo_test.cpp",
],
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index e6ee89f..b861c6d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -114,8 +114,8 @@
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
- height, PIXEL_FORMAT_RGBA_8888);
+ mBlastBufferQueueAdapter = sp<TestBLASTBufferQueue>::make("TestBLASTBufferQueue", sc, width,
+ height, PIXEL_FORMAT_RGBA_8888);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 6453885..b980f88 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -62,14 +62,15 @@
void SetUp() override {
mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS);
- mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
+ sp<Surface> surface;
+ std::tie(mBIC, surface) = BufferItemConsumer::create(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
sp<IProducerListener> producerListener = new TrackingProducerListener(this);
- mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
+ mProducer = surface->getIGraphicBufferProducer();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 8db48d2..314dea6 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -50,7 +50,7 @@
TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
sp<Looper> looper = Looper::prepare(0);
- Choreographer* choreographer = Choreographer::getForThread();
+ sp<Choreographer> choreographer = Choreographer::getForThread();
VsyncCallback animationCb;
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
CALLBACK_ANIMATION);
@@ -83,4 +83,4 @@
animationCb.frameTime.count());
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 9476930..482cfde 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,10 +66,9 @@
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
- mCC = new CpuConsumer(params.maxLockedBuffers);
+ std::tie(mCC, mSTC) = CpuConsumer::create(params.maxLockedBuffers);
String8 name("CpuConsumer_Under_Test");
mCC->setName(name);
- mSTC = mCC->getSurface();
mANW = mSTC;
}
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 2428bb3..7ae6f40 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,8 +34,8 @@
virtual void SetUp() {
GLTest::SetUp();
- mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSurface = mGlConsumer->getSurface();
+ std::tie(mGlConsumer, mSurface) =
+ GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
mANW = mSurface.get();
}
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index a0d8c53..c35efe2 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -40,7 +40,7 @@
std::unique_lock<decltype(mutex_)> lk(mutex_);
auto check_event = [](auto const& ev) -> bool {
- return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ return ev.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC;
};
DisplayEventReceiver::Event ev_;
int evs = receiver_.getEvents(&ev_, 1);
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 59d05b6..d3434ea 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,8 +40,7 @@
}
virtual void SetUp() {
- mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSTC = mST->getSurface();
+ std::tie(mST, mSTC) = GLConsumer::create(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
@@ -727,8 +726,7 @@
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
- sp<Surface> stc = st->getSurface();
+ auto [st, stc] = GLConsumer::create(i, GLConsumer::TEXTURE_EXTERNAL, true, false);
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 1309635..eb31cd1 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,8 +38,7 @@
void SetUp() {
GLTest::SetUp();
- mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSTC = mST->getSurface();
+ std::tie(mST, mSTC) = GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
mANW = mSTC;
ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 98d1329..e7690e2 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -294,9 +294,7 @@
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
-
- sp<Surface> s = c->getSurface();
+ auto [c, s] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
sp<ANativeWindow> anw(s);
int flags = -1;
@@ -309,10 +307,8 @@
TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ auto [cpuConsumer, s] = CpuConsumer::create(1);
cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
-
- sp<Surface> s = cpuConsumer->getSurface();
sp<ANativeWindow> anw(s);
android_dataspace dataSpace;
@@ -325,8 +321,7 @@
}
TEST_F(SurfaceTest, SettingGenerationNumber) {
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
- sp<Surface> surface = cpuConsumer->getSurface();
+ auto [cpuConsumer, surface] = CpuConsumer::create(1);
sp<ANativeWindow> window(surface);
// Allocate a buffer with a generation number of 0
@@ -692,10 +687,11 @@
return binder::Status::ok();
}
- binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/,
- const std::string& /*uniqueId*/,
- float /*requestedRefreshRate*/,
- sp<IBinder>* /*outDisplay*/) override {
+ binder::Status createVirtualDisplay(
+ const std::string& /*displayName*/, bool /*isSecure*/,
+ gui::ISurfaceComposer::OptimizationPolicy /*optimizationPolicy*/,
+ const std::string& /*uniqueId*/, float /*requestedRefreshRate*/,
+ sp<IBinder>* /*outDisplay*/) override {
return binder::Status::ok();
}
@@ -2180,8 +2176,7 @@
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
- sp<Surface> surface = cpuConsumer->getSurface();
+ auto [cpuConsumer, surface] = CpuConsumer::create(1);
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2229,8 +2224,7 @@
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
- sp<Surface> surface = cpuConsumer->getSurface();
+ auto [cpuConsumer, surface] = CpuConsumer::create(1);
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2377,8 +2371,7 @@
sp<IGraphicBufferConsumer> bqConsumer;
BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
- sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3);
- sp<Surface> surface = sp<Surface>::make(bqProducer);
+ auto [consumer, surface] = BufferItemConsumer::create(3);
sp<ImmediateReleaseConsumerListener> consumerListener =
sp<ImmediateReleaseConsumerListener>::make(consumer);
consumer->setFrameAvailableListener(consumerListener);
diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp
new file mode 100644
index 0000000..179b264
--- /dev/null
+++ b/libs/gui/tests/TransactionState_test.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 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 <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+#include "android/gui/FocusRequest.h"
+#include "binder/Binder.h"
+#include "binder/Parcel.h"
+#include "gtest/gtest.h"
+#include "gui/LayerState.h"
+#include "gui/WindowInfo.h"
+
+#include "gui/TransactionState.h"
+
+namespace android {
+
+void sprintf(std::string& out, const char* format, ...) {
+ va_list arg_list;
+ va_start(arg_list, format);
+
+ int len = vsnprintf(nullptr, 0, format, arg_list);
+ if (len < 0) {
+ va_end(arg_list);
+ }
+ std::string line(len, '\0');
+ int written = vsnprintf(line.data(), len + 1, format, arg_list);
+ if (written != len) {
+ va_end(arg_list);
+ }
+ line.pop_back();
+ out += line;
+ va_end(arg_list);
+}
+
+constexpr std::string dump_struct(auto& x) {
+ std::string s;
+#if __has_builtin(__builtin_dump_struct)
+ __builtin_dump_struct(&x, sprintf, s);
+#else
+ (void)x;
+#endif
+ return s;
+}
+
+void PrintTo(const TransactionState& state, ::std::ostream* os) {
+ *os << dump_struct(state);
+ *os << state.mFrameTimelineInfo.toString();
+ for (auto mergedId : state.mMergedTransactionIds) {
+ *os << mergedId << ",";
+ }
+}
+
+void PrintTo(const ComposerState& state, ::std::ostream* os) {
+ *os << dump_struct(state.state);
+ *os << state.state.getWindowInfo();
+}
+
+// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
+// field did not compare ==.
+void Compare(const TransactionState& s1, const TransactionState& s2) {
+ EXPECT_EQ(s1.mId, s2.mId);
+ EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
+ EXPECT_EQ(s1.mFlags, s2.mFlags);
+ EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
+ EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
+ EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
+ EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
+ EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
+ EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
+ EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
+ EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
+ EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
+ EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
+ EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
+ EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
+ EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
+ EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
+ EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
+}
+
+std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
+ auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
+ for (size_t i = 0; i < maxSize; ++i) {
+ result->emplace(i, sp<BBinder>::make());
+ }
+ return result;
+}
+
+constexpr size_t kMaxComposerStates = 2;
+ComposerState createComposerStateForTest(size_t i) {
+ static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
+
+ ComposerState state;
+ state.state.what = layer_state_t::eFlagsChanged;
+ state.state.surface = sLayerHandle->at(i);
+ state.state.layerId = i;
+ state.state.flags = 20 * i;
+ return state;
+}
+
+constexpr size_t kMaxDisplayStates = 5;
+DisplayState createDisplayStateForTest(size_t i) {
+ static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
+
+ DisplayState displayState;
+ displayState.what = DisplayState::eFlagsChanged;
+ displayState.token = sDisplayTokens->at(i);
+ displayState.flags = 20 * i;
+ return displayState;
+}
+
+TransactionState createTransactionStateForTest() {
+ static sp<BBinder> sApplyToken = sp<BBinder>::make();
+
+ TransactionState state;
+ state.mId = 123;
+ state.mMergedTransactionIds.push_back(15);
+ state.mMergedTransactionIds.push_back(0);
+ state.mFrameTimelineInfo.vsyncId = 14;
+ state.mDesiredPresentTime = 11;
+ state.mIsAutoTimestamp = true;
+ state.mApplyToken = sApplyToken;
+ for (size_t i = 0; i < kMaxDisplayStates; i++) {
+ state.mDisplayStates.push_back(createDisplayStateForTest(i));
+ }
+ for (size_t i = 0; i < kMaxComposerStates; i++) {
+ state.mComposerStates.push_back(createComposerStateForTest(i));
+ }
+ static const auto* const sFocusRequestTokens = createTokenMap(5).release();
+ for (int i = 0; i < 5; i++) {
+ gui::FocusRequest request;
+ request.token = sFocusRequestTokens->at(i);
+ request.timestamp = i;
+ state.mInputWindowCommands.addFocusRequest(request);
+ }
+ static const auto* const sCacheToken = createTokenMap(5).release();
+ for (int i = 0; i < 5; i++) {
+ client_cache_t cache;
+ cache.token = sCacheToken->at(i);
+ cache.id = i;
+ state.mUncacheBuffers.emplace_back(std::move(cache));
+ }
+ static const auto* const sListenerCallbacks = []() {
+ auto* callbacks = new std::vector<ListenerCallbacks>();
+ for (int i = 0; i < 5; i++) {
+ callbacks->emplace_back(sp<BBinder>::make(),
+ std::unordered_set<CallbackId, CallbackIdHash>{});
+ }
+ return callbacks;
+ }();
+ state.mHasListenerCallbacks = true;
+ state.mListenerCallbacks = *sListenerCallbacks;
+ return state;
+}
+
+TransactionState createEmptyTransaction(uint64_t id) {
+ TransactionState state;
+ state.mId = id;
+ return state;
+}
+
+TEST(TransactionStateTest, parcel) {
+ TransactionState state = createTransactionStateForTest();
+ Parcel p;
+ state.writeToParcel(&p);
+ p.setDataPosition(0);
+ TransactionState parcelledState;
+ parcelledState.readFromParcel(&p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelDisplayState) {
+ DisplayState state = createDisplayStateForTest(0);
+ Parcel p;
+ state.write(p);
+ p.setDataPosition(0);
+ DisplayState parcelledState;
+ parcelledState.read(p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelLayerState) {
+ ComposerState state = createComposerStateForTest(0);
+ Parcel p;
+ state.write(p);
+ p.setDataPosition(0);
+ ComposerState parcelledState;
+ parcelledState.read(p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelEmptyState) {
+ TransactionState state;
+ Parcel p;
+ state.writeToParcel(&p);
+ p.setDataPosition(0);
+ TransactionState parcelledState;
+ state.readFromParcel(&p);
+ EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, mergeLayerState) {
+ ComposerState composerState = createComposerStateForTest(0);
+ ComposerState update;
+ update.state.surface = composerState.state.surface;
+ update.state.layerId = 0;
+ update.state.what = layer_state_t::eAlphaChanged;
+ update.state.color.a = .42;
+ composerState.state.merge(update.state);
+
+ ComposerState expectedMergedState = createComposerStateForTest(0);
+ expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
+ expectedMergedState.state.color.a = .42;
+ EXPECT_EQ(composerState, expectedMergedState);
+};
+
+TEST(TransactionStateTest, merge) {
+ // Setup.
+ static constexpr uint64_t kUpdateTransactionId = 200;
+
+ TransactionState state = createTransactionStateForTest();
+
+ TransactionState update;
+ update.mId = kUpdateTransactionId;
+ {
+ ComposerState composerState;
+ composerState.state.surface = state.mComposerStates[0].state.surface;
+ composerState.state.what = layer_state_t::eAlphaChanged;
+ composerState.state.color.a = .42;
+ update.mComposerStates.push_back(composerState);
+ }
+ {
+ ComposerState composerState;
+ composerState.state.surface = state.mComposerStates[1].state.surface;
+ composerState.state.what = layer_state_t::eBufferChanged;
+ update.mComposerStates.push_back(composerState);
+ }
+ int32_t overrwiteLayerId = -1;
+ // Mutation.
+ state.merge(std::move(update),
+ [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
+ // Assertions.
+ EXPECT_EQ(1, overrwiteLayerId);
+ EXPECT_EQ(update, createEmptyTransaction(update.getId()));
+
+ TransactionState expectedMergedState = createTransactionStateForTest();
+ expectedMergedState.mMergedTransactionIds
+ .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
+ expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
+ expectedMergedState.mComposerStates.at(0).state.color.a = .42;
+ expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
+ auto inputCommands = expectedMergedState.mInputWindowCommands;
+
+ // desired present time is not merged.
+ expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
+
+ EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
+ EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
+ EXPECT_EQ(state, expectedMergedState);
+};
+
+TEST(TransactionStateTest, clear) {
+ TransactionState state = createTransactionStateForTest();
+ state.clear();
+ TransactionState emptyState = createEmptyTransaction(state.getId());
+ EXPECT_EQ(state, emptyState);
+};
+
+} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index d2e4320..ff26184 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -230,6 +230,7 @@
"InputConsumerNoResampling.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputFlags.cpp",
"InputTransport.cpp",
"InputVerifier.cpp",
"KeyCharacterMap.cpp",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 65a088e..155ea00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -284,6 +284,36 @@
return false;
}
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+ int32_t action) {
+ return isStylusEvent(source, properties) && isHoverAction(action);
+}
+
+bool isFromMouse(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::MOUSE;
+}
+
+bool isFromTouchpad(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::FINGER;
+}
+
+bool isFromDrawingTablet(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
+ isStylusToolType(toolType);
+}
+
+bool isHoverAction(int32_t action) {
+ return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
+}
+
+bool isMouseOrTouchpad(uint32_t sources) {
+ // Check if this is a mouse or touchpad, but not a drawing tablet.
+ return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
+ (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
+ !isFromSource(sources, AINPUT_SOURCE_STYLUS));
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp
new file mode 100644
index 0000000..f866f9b
--- /dev/null
+++ b/libs/input/InputFlags.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2025 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/InputFlags.h>
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <cutils/properties.h>
+
+#include <string>
+
+namespace android {
+
+bool InputFlags::connectedDisplaysCursorEnabled() {
+ static std::optional<bool> cachedDevOption;
+ if (!cachedDevOption.has_value()) {
+ char value[PROPERTY_VALUE_MAX];
+ constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts";
+ const int devOptionEnabled =
+ property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0;
+ cachedDevOption = devOptionEnabled == 1;
+ }
+ if (cachedDevOption.value_or(false)) {
+ return true;
+ }
+ return com::android::input::flags::connected_displays_cursor();
+}
+
+bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() {
+ return connectedDisplaysCursorEnabled() &&
+ com::android::input::flags::connected_displays_associated_display_cursor_bugfix();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 4e187ed..de5e428 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -61,20 +61,6 @@
}
flag {
- name: "report_palms_to_gestures_library"
- namespace: "input"
- description: "Report touches marked as palm by firmware to gestures library"
- bug: "302505955"
-}
-
-flag {
- name: "enable_touchpad_typing_palm_rejection"
- namespace: "input"
- description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
- bug: "301055381"
-}
-
-flag {
name: "enable_v2_touchpad_typing_palm_rejection"
namespace: "input"
description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click."
@@ -134,13 +120,6 @@
}
flag {
- name: "hide_pointer_indicators_for_secure_windows"
- namespace: "input"
- description: "Hide touch and pointer indicators if a secure window is present on display"
- bug: "325252005"
-}
-
-flag {
name: "enable_keyboard_classifier"
namespace: "input"
description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
@@ -155,13 +134,6 @@
}
flag {
- name: "include_relative_axis_values_for_captured_touchpads"
- namespace: "input"
- description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads."
- bug: "330522990"
-}
-
-flag {
name: "enable_per_device_input_latency_metrics"
namespace: "input"
description: "Capture input latency metrics on a per device granular level using histograms."
@@ -239,6 +211,16 @@
}
flag {
+ name: "connected_displays_associated_display_cursor_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Apply some rules to define associated display cursor behavior in connected displays"
+ bug: "396568321"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "use_cloned_screen_coordinates_as_raw"
namespace: "input"
description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
@@ -255,3 +237,12 @@
bug: "277261245"
}
+flag {
+ name: "prevent_merging_input_pointer_devices"
+ namespace: "desktop_input"
+ description: "Prevent merging input sub-devices that provide pointer input streams"
+ bug: "389689566"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 6eb2d73..35ba04f 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -50,7 +50,7 @@
bitflags! {
/// Source of the input device or input events.
- #[derive(Debug, PartialEq)]
+ #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Source: u32 {
// Constants from SourceClass, added here for compatibility reasons
/// SourceClass::Button
@@ -101,7 +101,7 @@
/// A rust enum representation of a MotionEvent action.
#[repr(u32)]
-#[derive(Eq, PartialEq)]
+#[derive(Clone, Copy, Eq, PartialEq)]
pub enum MotionAction {
/// ACTION_DOWN
Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
@@ -132,9 +132,15 @@
/// ACTION_SCROLL
Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
/// ACTION_BUTTON_PRESS
- ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ButtonPress {
+ /// The button being pressed.
+ action_button: MotionButton,
+ } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
/// ACTION_BUTTON_RELEASE
- ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ButtonRelease {
+ /// The button being released.
+ action_button: MotionButton,
+ } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
}
impl fmt::Display for MotionAction {
@@ -153,14 +159,20 @@
MotionAction::Scroll => write!(f, "SCROLL"),
MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
- MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
- MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+ MotionAction::ButtonPress { action_button } => {
+ write!(f, "BUTTON_PRESS({action_button:?})")
+ }
+ MotionAction::ButtonRelease { action_button } => {
+ write!(f, "BUTTON_RELEASE({action_button:?})")
+ }
}
}
}
-impl From<u32> for MotionAction {
- fn from(action: u32) -> Self {
+impl MotionAction {
+ /// Creates a [`MotionAction`] from an `AMOTION_EVENT_ACTION_…` constant and an action button
+ /// (which should be empty for all actions except `BUTTON_PRESS` and `…_RELEASE`).
+ pub fn from_code(action: u32, action_button: MotionButton) -> Self {
let (action_masked, action_index) = MotionAction::breakdown_action(action);
match action_masked {
input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
@@ -178,14 +190,16 @@
input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => {
+ MotionAction::ButtonPress { action_button }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => {
+ MotionAction::ButtonRelease { action_button }
+ }
_ => panic!("Unknown action: {}", action),
}
}
-}
-impl MotionAction {
fn breakdown_action(action: u32) -> (u32, usize) {
let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -219,7 +233,7 @@
/// MotionEvent flags.
/// The source of truth for the flag definitions are the MotionEventFlag AIDL enum.
/// The flag values are redefined here as a bitflags API.
- #[derive(Debug)]
+ #[derive(Clone, Copy, Debug)]
pub struct MotionFlags: u32 {
/// FLAG_WINDOW_IS_OBSCURED
const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32;
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 6d94316..f87dd41 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -22,26 +22,50 @@
use std::collections::HashMap;
use std::collections::HashSet;
-fn verify_event(
- action: MotionAction,
- action_button: MotionButton,
- pointer_properties: &[RustPointerProperties],
- flags: &MotionFlags,
- verify_buttons: bool,
-) -> Result<(), String> {
- let pointer_count = pointer_properties.len();
+/// Represents a movement or state change event from a pointer device. The Rust equivalent of the
+/// C++ NotifyMotionArgs struct.
+#[derive(Clone, Copy)]
+pub struct NotifyMotionArgs<'a> {
+ /// The ID of the device that emitted the event.
+ pub device_id: DeviceId,
+
+ /// The type of device that emitted the event.
+ pub source: Source,
+
+ /// The type of event that took place.
+ pub action: MotionAction,
+
+ /// The properties of each of the pointers involved in the event.
+ pub pointer_properties: &'a [RustPointerProperties],
+
+ /// Flags applied to the event.
+ pub flags: MotionFlags,
+
+ /// The set of buttons that were pressed at the time of the event.
+ ///
+ /// We allow DOWN events to include buttons in their state for which BUTTON_PRESS events haven't
+ /// been sent yet. In that case, the DOWN should be immediately followed by BUTTON_PRESS events
+ /// for those buttons, building up to a button state matching that of the DOWN. For example, if
+ /// the user presses the primary and secondary buttons at exactly the same time, we'd expect
+ /// this sequence:
+ ///
+ /// | Action | Action button | Button state |
+ /// |----------------|---------------|------------------------|
+ /// | `HOVER_EXIT` | - | - |
+ /// | `DOWN` | - | `PRIMARY`, `SECONDARY` |
+ /// | `BUTTON_PRESS` | `PRIMARY` | `PRIMARY` |
+ /// | `BUTTON_PRESS` | `SECONDARY` | `PRIMARY`, `SECONDARY` |
+ /// | `MOVE` | - | `PRIMARY`, `SECONDARY` |
+ pub button_state: MotionButton,
+}
+
+/// Verifies the properties of an event that should always be true, regardless of the current state.
+fn verify_event(event: NotifyMotionArgs<'_>, verify_buttons: bool) -> Result<(), String> {
+ let pointer_count = event.pointer_properties.len();
if pointer_count < 1 {
- return Err(format!("Invalid {} event: no pointers", action));
+ return Err(format!("Invalid {} event: no pointers", event.action));
}
- if action_button != MotionButton::empty()
- && action != MotionAction::ButtonPress
- && action != MotionAction::ButtonRelease
- {
- return Err(format!(
- "Invalid {action} event: has action button {action_button:?} but is not a button action"
- ));
- }
- match action {
+ match event.action {
MotionAction::Down
| MotionAction::HoverEnter
| MotionAction::HoverExit
@@ -49,32 +73,39 @@
| MotionAction::Up => {
if pointer_count != 1 {
return Err(format!(
- "Invalid {action} event: there are {pointer_count} pointers in the event",
+ "Invalid {} event: there are {} pointers in the event",
+ event.action, pointer_count
));
}
}
MotionAction::Cancel => {
- if !flags.contains(MotionFlags::CANCELED) {
+ if !event.flags.contains(MotionFlags::CANCELED) {
return Err(format!(
- "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {flags:#?}",
+ "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}",
+ event.flags
));
}
}
MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
if action_index >= pointer_count {
- return Err(format!("Got {action}, but event has {pointer_count} pointer(s)"));
+ return Err(format!(
+ "Got {}, but event has {} pointer(s)",
+ event.action, pointer_count
+ ));
}
}
- MotionAction::ButtonPress | MotionAction::ButtonRelease => {
+ MotionAction::ButtonPress { action_button }
+ | MotionAction::ButtonRelease { action_button } => {
if verify_buttons {
let button_count = action_button.iter().count();
if button_count != 1 {
return Err(format!(
- "Invalid {action} event: must specify a single action button, not \
- {button_count} action buttons"
+ "Invalid {} event: must specify a single action button, not {} action \
+ buttons",
+ event.action, button_count
));
}
}
@@ -92,59 +123,43 @@
button_state: MotionButton,
/// The set of "pending buttons", which were seen in the last DOWN event's button state but
- /// for which we haven't seen BUTTON_PRESS events yet.
- ///
- /// We allow DOWN events to include buttons in their state for which BUTTON_PRESS events haven't
- /// been sent yet. In that case, the DOWN should be immediately followed by BUTTON_PRESS events
- /// for those buttons, building up to a button state matching that of the DOWN. For example, if
- /// the user presses the primary and secondary buttons at exactly the same time, we'd expect
- /// this sequence:
- ///
- /// | Action | Action button | Button state |
- /// |----------------|---------------|------------------------|
- /// | `HOVER_EXIT` | - | - |
- /// | `DOWN` | - | `PRIMARY`, `SECONDARY` |
- /// | `BUTTON_PRESS` | `PRIMARY` | `PRIMARY` |
- /// | `BUTTON_PRESS` | `SECONDARY` | `PRIMARY`, `SECONDARY` |
- /// | `MOVE` | - | `PRIMARY`, `SECONDARY` |
+ /// for which we haven't seen BUTTON_PRESS events yet (see [`NotifyMotionArgs::button_state`]).
pending_buttons: MotionButton,
}
impl ButtonVerifier {
- pub fn process_action(
- &mut self,
- action: MotionAction,
- action_button: MotionButton,
- button_state: MotionButton,
- ) -> Result<(), String> {
+ pub fn process_event(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> {
if !self.pending_buttons.is_empty() {
// We just saw a DOWN with some additional buttons in its state, so it should be
// immediately followed by ButtonPress events for those buttons.
- if action != MotionAction::ButtonPress || !self.pending_buttons.contains(action_button)
- {
- return Err(format!(
- "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {} with \
- action button {:?}",
- self.pending_buttons, action, action_button
- ));
- } else {
- self.pending_buttons -= action_button;
- }
- }
- let expected_state = match action {
- MotionAction::Down => {
- if self.button_state - button_state != MotionButton::empty() {
+ match event.action {
+ MotionAction::ButtonPress { action_button }
+ if self.pending_buttons.contains(action_button) =>
+ {
+ self.pending_buttons -= action_button;
+ }
+ _ => {
return Err(format!(
- "DOWN event button state is missing {:?}",
- self.button_state - button_state
+ "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {}",
+ self.pending_buttons, event.action
));
}
- self.pending_buttons = button_state - self.button_state;
+ }
+ }
+ let expected_state = match event.action {
+ MotionAction::Down => {
+ if self.button_state - event.button_state != MotionButton::empty() {
+ return Err(format!(
+ "DOWN event button state is missing {:?}",
+ self.button_state - event.button_state
+ ));
+ }
+ self.pending_buttons = event.button_state - self.button_state;
// We've already checked that the state isn't missing any already-down buttons, and
// extra buttons are valid on DOWN actions, so bypass the expected state check.
- button_state
+ event.button_state
}
- MotionAction::ButtonPress => {
+ MotionAction::ButtonPress { action_button } => {
if self.button_state.contains(action_button) {
return Err(format!(
"Duplicate BUTTON_PRESS; button state already contains {action_button:?}"
@@ -152,24 +167,25 @@
}
self.button_state | action_button
}
- MotionAction::ButtonRelease => {
+ MotionAction::ButtonRelease { action_button } => {
if !self.button_state.contains(action_button) {
return Err(format!(
- "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}"
+ "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}",
));
}
self.button_state - action_button
}
_ => self.button_state,
};
- if button_state != expected_state {
+ if event.button_state != expected_state {
return Err(format!(
- "Expected {action} button state to be {expected_state:?}, but was {button_state:?}"
+ "Expected {} button state to be {:?}, but was {:?}",
+ event.action, expected_state, event.button_state
));
}
// DOWN events can have pending buttons, so don't update the state for them.
- if action != MotionAction::Down {
- self.button_state = button_state;
+ if event.action != MotionAction::Down {
+ self.button_state = event.button_state;
}
Ok(())
}
@@ -205,78 +221,61 @@
/// Process a pointer movement event from an InputDevice.
/// If the event is not valid, we return an error string that describes the issue.
- #[allow(clippy::too_many_arguments)]
- pub fn process_movement(
- &mut self,
- device_id: DeviceId,
- source: Source,
- action: u32,
- action_button: MotionButton,
- pointer_properties: &[RustPointerProperties],
- flags: MotionFlags,
- button_state: MotionButton,
- ) -> Result<(), String> {
- if !source.is_from_class(SourceClass::Pointer) {
+ pub fn process_movement(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> {
+ if !event.source.is_from_class(SourceClass::Pointer) {
// Skip non-pointer sources like MOUSE_RELATIVE for now
return Ok(());
}
if self.should_log {
info!(
"Processing {} for device {:?} ({} pointer{}) on {}",
- MotionAction::from(action).to_string(),
- device_id,
- pointer_properties.len(),
- if pointer_properties.len() == 1 { "" } else { "s" },
+ event.action,
+ event.device_id,
+ event.pointer_properties.len(),
+ if event.pointer_properties.len() == 1 { "" } else { "s" },
self.name
);
}
- verify_event(
- action.into(),
- action_button,
- pointer_properties,
- &flags,
- self.verify_buttons,
- )?;
+ verify_event(event, self.verify_buttons)?;
if self.verify_buttons {
- self.button_verifier_by_device.entry(device_id).or_default().process_action(
- action.into(),
- action_button,
- button_state,
- )?;
+ self.button_verifier_by_device
+ .entry(event.device_id)
+ .or_default()
+ .process_event(event)?;
}
- match action.into() {
+ match event.action {
MotionAction::Down => {
- if self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ if self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
return Err(format!(
"{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
- self.name, device_id, self.touching_pointer_ids_by_device
+ self.name, event.device_id, self.touching_pointer_ids_by_device
));
}
- let it = self.touching_pointer_ids_by_device.entry(device_id).or_default();
- it.insert(pointer_properties[0].id);
+ let it = self.touching_pointer_ids_by_device.entry(event.device_id).or_default();
+ it.insert(event.pointer_properties[0].id);
}
MotionAction::PointerDown { action_index } => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
return Err(format!(
"{}: Received POINTER_DOWN but no pointers are currently down \
for device {:?}",
- self.name, device_id
+ self.name, event.device_id
));
}
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
- if it.len() != pointer_properties.len() - 1 {
+ let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
+ if it.len() != event.pointer_properties.len() - 1 {
return Err(format!(
"{}: There are currently {} touching pointers, but the incoming \
POINTER_DOWN event has {}",
self.name,
it.len(),
- pointer_properties.len()
+ event.pointer_properties.len()
));
}
- let pointer_id = pointer_properties[action_index].id;
+ let pointer_id = event.pointer_properties[action_index].id;
if it.contains(&pointer_id) {
return Err(format!(
"{}: Pointer with id={} already present found in the properties",
@@ -286,7 +285,7 @@
it.insert(pointer_id);
}
MotionAction::Move => {
- if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
return Err(format!(
"{}: ACTION_MOVE touching pointers don't match",
self.name
@@ -294,49 +293,49 @@
}
}
MotionAction::PointerUp { action_index } => {
- if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
return Err(format!(
"{}: ACTION_POINTER_UP touching pointers don't match",
self.name
));
}
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
- let pointer_id = pointer_properties[action_index].id;
+ let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
+ let pointer_id = event.pointer_properties[action_index].id;
it.remove(&pointer_id);
}
MotionAction::Up => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
return Err(format!(
"{} Received ACTION_UP but no pointers are currently down for device {:?}",
- self.name, device_id
+ self.name, event.device_id
));
}
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
if it.len() != 1 {
return Err(format!(
"{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
- self.name, it, device_id
+ self.name, it, event.device_id
));
}
- let pointer_id = pointer_properties[0].id;
+ let pointer_id = event.pointer_properties[0].id;
if !it.contains(&pointer_id) {
return Err(format!(
"{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
{:?} for device {:?}",
- self.name, pointer_id, it, device_id
+ self.name, pointer_id, it, event.device_id
));
}
- self.touching_pointer_ids_by_device.remove(&device_id);
+ self.touching_pointer_ids_by_device.remove(&event.device_id);
}
MotionAction::Cancel => {
- if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
return Err(format!(
"{}: Got ACTION_CANCEL, but the pointers don't match. \
Existing pointers: {:?}",
self.name, self.touching_pointer_ids_by_device
));
}
- self.touching_pointer_ids_by_device.remove(&device_id);
+ self.touching_pointer_ids_by_device.remove(&event.device_id);
}
/*
* The hovering protocol currently supports a single pointer only, because we do not
@@ -345,41 +344,41 @@
* eventually supported.
*/
MotionAction::HoverEnter => {
- if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ if self.hovering_pointer_ids_by_device.contains_key(&event.device_id) {
return Err(format!(
"{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
{:?}",
- self.name, device_id, self.hovering_pointer_ids_by_device
+ self.name, event.device_id, self.hovering_pointer_ids_by_device
));
}
- let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
- it.insert(pointer_properties[0].id);
+ let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default();
+ it.insert(event.pointer_properties[0].id);
}
MotionAction::HoverMove => {
// For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
// If there was no prior HOVER_ENTER, just start a new hovering pointer.
- let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
- it.insert(pointer_properties[0].id);
+ let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default();
+ it.insert(event.pointer_properties[0].id);
}
MotionAction::HoverExit => {
- if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ if !self.hovering_pointer_ids_by_device.contains_key(&event.device_id) {
return Err(format!(
"{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
- self.name, device_id
+ self.name, event.device_id
));
}
- let pointer_id = pointer_properties[0].id;
- let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = event.pointer_properties[0].id;
+ let it = self.hovering_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
it.remove(&pointer_id);
if !it.is_empty() {
return Err(format!(
"{}: Removed hovering pointer {}, but pointers are still\
hovering for device {:?}: {:?}",
- self.name, pointer_id, device_id, it
+ self.name, pointer_id, event.device_id, it
));
}
- self.hovering_pointer_ids_by_device.remove(&device_id);
+ self.hovering_pointer_ids_by_device.remove(&event.device_id);
}
_ => return Ok(()),
}
@@ -421,11 +420,25 @@
mod tests {
use crate::input::MotionButton;
use crate::input_verifier::InputVerifier;
+ use crate::input_verifier::NotifyMotionArgs;
use crate::DeviceId;
+ use crate::MotionAction;
use crate::MotionFlags;
use crate::RustPointerProperties;
use crate::Source;
+ const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
+ const BASE_EVENT: NotifyMotionArgs = NotifyMotionArgs {
+ device_id: DeviceId(1),
+ source: Source::Touchscreen,
+ action: MotionAction::Down,
+ pointer_properties: &BASE_POINTER_PROPERTIES,
+ flags: MotionFlags::empty(),
+ button_state: MotionButton::empty(),
+ };
+ const BASE_MOUSE_EVENT: NotifyMotionArgs =
+ NotifyMotionArgs { source: Source::Mouse, ..BASE_EVENT };
+
#[test]
/**
* Send a DOWN event with 2 pointers and ensure that it's marked as invalid.
@@ -436,15 +449,11 @@
let pointer_properties =
Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_err());
}
@@ -454,37 +463,25 @@
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Move,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Up,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
}
@@ -494,56 +491,38 @@
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
// POINTER 1 DOWN
let two_pointer_properties =
Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
- | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- MotionButton::empty(),
- &two_pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::PointerDown { action_index: 1 },
+ pointer_properties: &two_pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
// POINTER 0 UP
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP
- | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- MotionButton::empty(),
- &two_pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::PointerUp { action_index: 0 },
+ pointer_properties: &two_pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
// ACTION_UP for pointer id=1
let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- MotionButton::empty(),
- &pointer_1_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Up,
+ pointer_properties: &pointer_1_properties,
+ ..BASE_EVENT
+ })
.is_ok());
}
@@ -551,61 +530,40 @@
fn multi_device_stream() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(1),
+ action: MotionAction::Down,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(1),
+ action: MotionAction::Move,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(2),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(2),
+ action: MotionAction::Down,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(2),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(2),
+ action: MotionAction::Move,
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(1),
+ action: MotionAction::Up,
+ ..BASE_EVENT
+ })
.is_ok());
}
@@ -613,28 +571,19 @@
fn action_cancel() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ flags: MotionFlags::empty(),
+ ..BASE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::CANCELED,
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Cancel,
+ flags: MotionFlags::CANCELED,
+ ..BASE_EVENT
+ })
.is_ok());
}
@@ -642,28 +591,11 @@
fn invalid_action_cancel() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::Down, ..BASE_EVENT })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(), // forgot to set FLAG_CANCELED
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::Cancel, ..BASE_EVENT })
.is_err());
}
@@ -671,17 +603,8 @@
fn invalid_up() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::Up, ..BASE_EVENT })
.is_err());
}
@@ -689,53 +612,20 @@
fn correct_hover_sequence() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverMove, ..BASE_EVENT })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverExit, ..BASE_EVENT })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
.is_ok());
}
@@ -743,29 +633,12 @@
fn double_hover_enter() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
.is_err());
}
@@ -775,17 +648,13 @@
fn relative_mouse_move() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(2),
- Source::MouseRelative,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ device_id: DeviceId(2),
+ source: Source::MouseRelative,
+ action: MotionAction::Move,
+ ..BASE_EVENT
+ })
.is_ok());
}
@@ -796,42 +665,29 @@
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
// POINTER 1 DOWN
let two_pointer_properties =
Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
- | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- MotionButton::empty(),
- &two_pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::PointerDown { action_index: 1 },
+ pointer_properties: &two_pointer_properties,
+ ..BASE_EVENT
+ })
.is_ok());
// MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Touchscreen,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Move,
+ pointer_properties: &pointer_properties,
+ ..BASE_EVENT
+ })
.is_err());
}
@@ -839,17 +695,12 @@
fn correct_button_press() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
}
@@ -857,17 +708,12 @@
fn button_press_without_action_button() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::empty() },
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -875,17 +721,14 @@
fn button_press_with_multiple_action_buttons() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back | MotionButton::Forward,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back | MotionButton::Forward,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress {
+ action_button: MotionButton::Back | MotionButton::Forward
+ },
+ button_state: MotionButton::Back | MotionButton::Forward,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -893,17 +736,12 @@
fn button_press_without_action_button_in_state() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -911,64 +749,19 @@
fn button_release_with_action_button_in_state() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
- .is_err());
- }
-
- #[test]
- fn nonbutton_action_with_action_button() {
- let mut verifier =
- InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
- assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
- .is_err());
- }
-
- #[test]
- fn nonbutton_action_with_action_button_and_state() {
- let mut verifier =
- InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
- assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonRelease { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -976,28 +769,19 @@
fn nonbutton_action_with_button_state_change() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::HoverEnter,
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::HoverMove,
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1005,39 +789,26 @@
fn nonbutton_action_missing_button_state() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::HoverEnter,
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::HoverMove,
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1045,40 +816,27 @@
fn up_without_button_release() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
// This UP event shouldn't change the button state; a BUTTON_RELEASE before it should.
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Up,
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1086,28 +844,19 @@
fn button_press_for_already_pressed_button() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1115,17 +864,12 @@
fn button_release_for_unpressed_button() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonRelease { action_button: MotionButton::Back },
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1133,28 +877,19 @@
fn correct_multiple_button_presses_without_down() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Forward,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back | MotionButton::Forward,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Forward },
+ button_state: MotionButton::Back | MotionButton::Forward,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
}
@@ -1162,52 +897,35 @@
fn correct_down_with_button_press() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary | MotionButton::Secondary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ button_state: MotionButton::Primary | MotionButton::Secondary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Secondary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary | MotionButton::Secondary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Secondary },
+ button_state: MotionButton::Primary | MotionButton::Secondary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
// Also check that the MOVE afterwards is OK, as that's where errors would be raised if not
// enough BUTTON_PRESSes were sent.
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary | MotionButton::Secondary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Move,
+ button_state: MotionButton::Primary | MotionButton::Secondary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
}
@@ -1215,29 +933,20 @@
fn down_with_button_state_change_not_followed_by_button_press() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
// The DOWN event itself is OK, but it needs to be immediately followed by a BUTTON_PRESS.
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Move,
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1245,41 +954,28 @@
fn down_with_button_state_change_not_followed_by_enough_button_presses() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary | MotionButton::Secondary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ button_state: MotionButton::Primary | MotionButton::Secondary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
// The DOWN event itself is OK, but it needs to be immediately followed by two
// BUTTON_PRESSes, one for each button.
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Primary,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Primary,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Move,
+ button_state: MotionButton::Primary,
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
@@ -1287,28 +983,19 @@
fn down_missing_already_pressed_button() {
let mut verifier =
InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- MotionButton::Back,
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::Back,
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+ button_state: MotionButton::Back,
+ ..BASE_MOUSE_EVENT
+ })
.is_ok());
assert!(verifier
- .process_movement(
- DeviceId(1),
- Source::Mouse,
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- MotionButton::empty(),
- &pointer_properties,
- MotionFlags::empty(),
- MotionButton::empty(),
- )
+ .process_movement(NotifyMotionArgs {
+ action: MotionAction::Down,
+ button_state: MotionButton::empty(),
+ ..BASE_MOUSE_EVENT
+ })
.is_err());
}
}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 6db4356..ee999f7 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -27,7 +27,7 @@
DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton,
MotionFlags, Source,
};
-pub use input_verifier::InputVerifier;
+pub use input_verifier::{InputVerifier, NotifyMotionArgs};
pub use keyboard_classifier::KeyboardClassifier;
#[cxx::bridge(namespace = "android::input")]
@@ -133,37 +133,52 @@
flags: u32,
button_state: u32,
) -> String {
- let motion_flags = MotionFlags::from_bits(flags);
- if motion_flags.is_none() {
+ let Some(converted_source) = Source::from_bits(source) else {
+ panic!(
+ "The conversion of source 0x{source:08x} failed, please check if some sources have not \
+ been added to Source."
+ );
+ };
+ let Some(motion_flags) = MotionFlags::from_bits(flags) else {
panic!(
"The conversion of flags 0x{:08x} failed, please check if some flags have not been \
added to MotionFlags.",
flags
);
- }
- let motion_action_button = MotionButton::from_bits(action_button);
- if motion_action_button.is_none() {
+ };
+ let Some(motion_action_button) = MotionButton::from_bits(action_button) else {
panic!(
"The conversion of action button 0x{action_button:08x} failed, please check if some \
buttons need to be added to MotionButton."
);
- }
- let motion_button_state = MotionButton::from_bits(button_state);
- if motion_button_state.is_none() {
+ };
+ let Some(motion_button_state) = MotionButton::from_bits(button_state) else {
panic!(
"The conversion of button state 0x{button_state:08x} failed, please check if some \
buttons need to be added to MotionButton."
);
+ };
+ let motion_action = MotionAction::from_code(action, motion_action_button);
+ if motion_action_button != MotionButton::empty() {
+ match motion_action {
+ MotionAction::ButtonPress { action_button: _ }
+ | MotionAction::ButtonRelease { action_button: _ } => {}
+ _ => {
+ return format!(
+ "Invalid {motion_action} event: has action button {motion_action_button:?} but \
+ is not a button action"
+ );
+ }
+ }
}
- let result = verifier.process_movement(
- DeviceId(device_id),
- Source::from_bits(source).unwrap(),
- action,
- motion_action_button.unwrap(),
+ let result = verifier.process_movement(NotifyMotionArgs {
+ device_id: DeviceId(device_id),
+ source: converted_source,
+ action: motion_action,
pointer_properties,
- motion_flags.unwrap(),
- motion_button_state.unwrap(),
- );
+ flags: motion_flags,
+ button_state: motion_button_state,
+ });
match result {
Ok(()) => "".to_string(),
Err(e) => e,
@@ -230,3 +245,44 @@
}
classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
}
+
+#[cfg(test)]
+mod tests {
+ use crate::create_input_verifier;
+ use crate::process_movement;
+ use crate::RustPointerProperties;
+
+ const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
+
+ #[test]
+ fn verify_nonbutton_action_with_action_button() {
+ let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+ assert!(process_movement(
+ &mut verifier,
+ 1,
+ input_bindgen::AINPUT_SOURCE_MOUSE,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+ &BASE_POINTER_PROPERTIES,
+ 0,
+ 0,
+ )
+ .contains("button action"));
+ }
+
+ #[test]
+ fn verify_nonbutton_action_with_action_button_and_button_state() {
+ let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+ assert!(process_movement(
+ &mut verifier,
+ 1,
+ input_bindgen::AINPUT_SOURCE_MOUSE,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+ &BASE_POINTER_PROPERTIES,
+ 0,
+ input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+ )
+ .contains("button action"));
+ }
+}
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 82ae422..08f0c5f 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,5 +1,4 @@
mathias@google.com
-randolphs@google.com
romainguy@google.com
sumir@google.com
jreck@google.com
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index bed31e2..89a97de 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -142,7 +142,7 @@
}
AChoreographer* AChoreographer_getInstance() {
- return Choreographer_to_AChoreographer(Choreographer::getForThread());
+ return Choreographer_to_AChoreographer(Choreographer::getForThread().get());
}
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
@@ -238,13 +238,17 @@
}
AChoreographer* AChoreographer_create() {
- Choreographer* choreographer = new Choreographer(nullptr);
+ // Increments default strongRef count on construction, will be decremented on
+ // function exit.
+ auto choreographer = sp<Choreographer>::make(nullptr);
status_t result = choreographer->initialize();
if (result != OK) {
ALOGW("Failed to initialize");
return nullptr;
}
- return Choreographer_to_AChoreographer(choreographer);
+ // Will be decremented and destroyed by AChoreographer_destroy
+ choreographer->incStrong((void*)AChoreographer_create);
+ return Choreographer_to_AChoreographer(choreographer.get());
}
void AChoreographer_destroy(AChoreographer* choreographer) {
@@ -252,7 +256,7 @@
return;
}
- delete AChoreographer_to_Choreographer(choreographer);
+ AChoreographer_to_Choreographer(choreographer)->decStrong((void*)AChoreographer_create);
}
int AChoreographer_getFd(const AChoreographer* choreographer) {
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 937ff02..51d0c81 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -50,14 +50,9 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN);
- mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer());
-#else
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN);
- mWindow = new TestableSurface(mProducer);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<Surface> surface;
+ std::tie(mItemConsumer, surface) = BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN);
+ mWindow = new TestableSurface(surface->getIGraphicBufferProducer());
const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU);
EXPECT_EQ(0, success);
}
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index 0eeca54..929f067 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -16,6 +16,7 @@
double_loadable: true,
srcs: [
"aidl/android/content/AttributionSourceState.aidl",
+ "aidl/com/android/internal/app/IAppOpsCallback.aidl",
"aidl/android/permission/IPermissionChecker.aidl",
],
}
@@ -36,7 +37,6 @@
],
srcs: [
"AppOpsManager.cpp",
- "IAppOpsCallback.cpp",
"IAppOpsService.cpp",
"android/permission/PermissionChecker.cpp",
],
diff --git a/libs/permission/IAppOpsCallback.cpp b/libs/permission/IAppOpsCallback.cpp
deleted file mode 100644
index 2b3f462..0000000
--- a/libs/permission/IAppOpsCallback.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AppOpsCallback"
-
-#include <binder/IAppOpsCallback.h>
-
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
-{
-public:
- explicit BpAppOpsCallback(const sp<IBinder>& impl)
- : BpInterface<IAppOpsCallback>(impl)
- {
- }
-
- virtual void opChanged(int32_t op, const String16& packageName) {
- Parcel data, reply;
- data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
- data.writeInt32(op);
- data.writeString16(packageName);
- remote()->transact(OP_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback")
-
-// ----------------------------------------------------------------------
-
-// NOLINTNEXTLINE(google-default-arguments)
-status_t BnAppOpsCallback::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case OP_CHANGED_TRANSACTION: {
- CHECK_INTERFACE(IAppOpsCallback, data, reply);
- int32_t op = data.readInt32();
- String16 packageName;
- (void)data.readString16(&packageName);
- opChanged(op, packageName);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl
new file mode 100644
index 0000000..36b19df
--- /dev/null
+++ b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+oneway interface IAppOpsCallback {
+ void opChanged(int op, int uid, String packageName, String persistentDeviceId);
+}
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index 7e179d6..a22c975 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -180,10 +180,10 @@
void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
const std::optional<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
- const sp<IAppOpsCallback>& callback);
+ const sp<com::android::internal::app::IAppOpsCallback>& callback);
void startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
- const sp<IAppOpsCallback>& callback);
- void stopWatchingMode(const sp<IAppOpsCallback>& callback);
+ const sp<com::android::internal::app::IAppOpsCallback>& callback);
+ void stopWatchingMode(const sp<com::android::internal::app::IAppOpsCallback>& callback);
int32_t permissionToOpCode(const String16& permission);
void setCameraAudioRestriction(int32_t mode);
diff --git a/libs/permission/include/binder/IAppOpsCallback.h b/libs/permission/include/binder/IAppOpsCallback.h
deleted file mode 100644
index eb76f57..0000000
--- a/libs/permission/include/binder/IAppOpsCallback.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#ifndef __ANDROID_VNDK__
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsCallback : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AppOpsCallback)
-
- virtual void opChanged(int32_t op, const String16& packageName) = 0;
-
- enum {
- OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
-{
-public:
- // NOLINTNEXTLINE(google-default-arguments)
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index 918fcdb..1468fd9 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -16,7 +16,8 @@
#pragma once
-#include <binder/IAppOpsCallback.h>
+#include <com/android/internal/app/IAppOpsCallback.h>
+#include <com/android/internal/app/BnAppOpsCallback.h>
#include <binder/IInterface.h>
#include <optional>
@@ -27,6 +28,8 @@
namespace android {
+using IAppOpsCallback = ::com::android::internal::app::IAppOpsCallback;
+
// ----------------------------------------------------------------------
class IAppOpsService : public IInterface
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index ac43da8..ecb16b2 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -301,6 +301,10 @@
*os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect;
}
*os << "\n .whitePointNits = " << settings.whitePointNits;
+ if (settings.luts) {
+ *os << "\n .luts = ";
+ PrintTo(settings.luts, os);
+ }
*os << "\n}";
}
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 57041ee..f43694e 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
#include "Cache.h"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "AutoBackendTexture.h"
#include "SkiaRenderEngine.h"
#include "android-base/unique_fd.h"
@@ -28,6 +31,7 @@
#include "utils/Timers.h"
#include <com_android_graphics_libgui_flags.h>
+#include <common/trace.h>
namespace android::renderengine::skia {
@@ -337,17 +341,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
};
@@ -370,16 +374,16 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = rect,
+ .positionTransform = mat4(),
.roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.sourceDataspace = kDestDataSpace,
};
@@ -421,17 +425,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = mat4(),
.roundedCornersRadius = {27.f, 27.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
@@ -489,17 +493,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kBT2020DataSpace,
};
@@ -527,17 +531,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = kScaleAsymmetric,
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = kScaleAsymmetric,
.roundedCornersRadius = {64.1f, 64.1f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = true,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 0.5f,
.sourceDataspace = kBT2020DataSpace,
@@ -556,17 +560,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kExtendedHdrDataSpce,
};
@@ -594,17 +598,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = false}},
+ .isOpaque = false,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kOtherDataSpace,
};
@@ -659,6 +663,7 @@
// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
// gPrintSKSL = true
void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) {
+ SFTRACE_CALL();
const int previousCount = renderengine->reportShadersCompiled();
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
@@ -724,24 +729,29 @@
impl::ExternalTexture::Usage::WRITEABLE);
if (config.cacheHolePunchLayer) {
+ SFTRACE_NAME("cacheHolePunchLayer");
drawHolePunchLayer(renderengine, display, dstTexture);
}
if (config.cacheSolidLayers) {
+ SFTRACE_NAME("cacheSolidLayers");
drawSolidLayers(renderengine, display, dstTexture);
drawSolidLayers(renderengine, p3Display, dstTexture);
}
if (config.cacheSolidDimmedLayers) {
+ SFTRACE_NAME("cacheSolidDimmedLayers");
drawSolidDimmedLayers(renderengine, display, dstTexture);
}
if (config.cacheShadowLayers) {
+ SFTRACE_NAME("cacheShadowLayers");
drawShadowLayers(renderengine, display, srcTexture);
drawShadowLayers(renderengine, p3Display, srcTexture);
}
if (renderengine->supportsBackgroundBlur()) {
+ SFTRACE_NAME("supportsBackgroundBlur");
drawBlurLayers(renderengine, display, dstTexture);
}
@@ -776,32 +786,37 @@
for (auto texture : textures) {
if (config.cacheImageLayers) {
+ SFTRACE_NAME("cacheImageLayers");
drawImageLayers(renderengine, display, dstTexture, texture);
}
if (config.cacheImageDimmedLayers) {
+ SFTRACE_NAME("cacheImageDimmedLayers");
drawImageDimmedLayers(renderengine, display, dstTexture, texture);
drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture);
drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture);
}
if (config.cacheClippedLayers) {
+ SFTRACE_NAME("cacheClippedLayers");
// Draw layers for b/185569240.
drawClippedLayers(renderengine, display, dstTexture, texture);
}
- if (com::android::graphics::libgui::flags::edge_extension_shader() &&
- config.cacheEdgeExtension) {
+ if (config.cacheEdgeExtension) {
+ SFTRACE_NAME("cacheEdgeExtension");
drawEdgeExtensionLayers(renderengine, display, dstTexture, texture);
drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture);
}
}
if (config.cachePIPImageLayers) {
+ SFTRACE_NAME("cachePIPImageLayers");
drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
}
if (config.cacheTransparentImageDimmedLayers) {
+ SFTRACE_NAME("cacheTransparentImageDimmedLayers");
drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture,
externalTexture);
drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture);
@@ -811,10 +826,12 @@
}
if (config.cacheClippedDimmedImageLayers) {
+ SFTRACE_NAME("cacheClippedDimmedImageLayers");
drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
}
if (config.cacheUltraHDR) {
+ SFTRACE_NAME("cacheUltraHDR");
drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
@@ -833,7 +850,10 @@
};
auto layers = std::vector<LayerSettings>{layer};
// call get() to make it synchronous
- renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get();
+ {
+ SFTRACE_NAME("finalLayer");
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get();
+ }
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp
index 11bcdb8..1c1ee0a 100644
--- a/libs/renderengine/skia/debug/CaptureTimer.cpp
+++ b/libs/renderengine/skia/debug/CaptureTimer.cpp
@@ -30,7 +30,7 @@
void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) {
this->clear = false;
- CommonPool::post([=]() {
+ CommonPool::post([=,this]() {
if (this->clear) return;
std::this_thread::sleep_for(delay);
if (this->clear) return;
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
index 4164c4b..f007427 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -58,9 +58,6 @@
)");
EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() {
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- return;
- }
mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader));
LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(),
"EdgeExtensionShaderFactory compilation "
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
index 6d91da3..6977a49 100644
--- a/libs/tonemap/OWNERS
+++ b/libs/tonemap/OWNERS
@@ -1,4 +1,3 @@
alecmouri@google.com
jreck@google.com
sallyqi@google.com
-scroggo@google.com
\ No newline at end of file
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
index 800bf3c..271d7c8 100644
--- a/libs/tracing_perfetto/include/tracing_sdk.h
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -292,13 +292,8 @@
arg_ = std::move(arg);
}
- ~DebugArg() {
- free_string_value();
- }
-
void set_value(T value) {
if constexpr (std::is_same_v<T, const char*>) {
- free_string_value();
arg_.value = value;
} else if constexpr (std::is_same_v<T, int64_t>) {
arg_.value = value;
@@ -321,16 +316,6 @@
DISALLOW_COPY_AND_ASSIGN(DebugArg);
TypeMap::type arg_;
const std::string name_;
-
- constexpr void free_string_value() {
- if constexpr (std::is_same_v<typename TypeMap::type,
- PerfettoTeHlExtraDebugArgString>) {
- if (arg_.value) {
- free((void*)arg_.value);
- arg_.value = nullptr;
- }
- }
- }
};
template <typename T>
@@ -375,10 +360,6 @@
arg_ = std::move(arg);
}
- ~ProtoField() {
- free_string_value();
- }
-
void set_value(uint32_t id, T value) {
if constexpr (std::is_same_v<T, int64_t>) {
arg_.header.id = id;
@@ -387,7 +368,6 @@
arg_.header.id = id;
arg_.value = value;
} else if constexpr (std::is_same_v<T, const char*>) {
- free_string_value();
arg_.header.id = id;
arg_.str = value;
}
@@ -404,16 +384,6 @@
private:
DISALLOW_COPY_AND_ASSIGN(ProtoField);
TypeMap::type arg_;
-
- constexpr void free_string_value() {
- if constexpr (std::is_same_v<typename TypeMap::type,
- PerfettoTeHlProtoFieldCstr>) {
- if (arg_.str) {
- free((void*)arg_.str);
- arg_.str = nullptr;
- }
- }
- }
};
class ProtoFieldNested {
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
index 3e1ac2a..0e3fb07 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -25,8 +25,6 @@
namespace internal {
-bool isPerfettoRegistered();
-
struct PerfettoTeCategory* toPerfettoCategory(uint64_t category);
void registerWithPerfetto(bool test = false);
diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp
index c97e900..70b8be9 100644
--- a/libs/tracing_perfetto/tracing_sdk.cpp
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -38,7 +38,7 @@
PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type,
type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name,
extra->get());
- extra->pop_extra();
+ extra->clear_extras();
}
}
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index a0b5fe7..2a85a4b 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -2,6 +2,5 @@
alecmouri@google.com
chrisforbes@google.com
jreck@google.com
-lpy@google.com
mathias@google.com
romainguy@google.com
diff --git a/libs/ui/include/ui/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h
index 65d2b8f..834a304 100644
--- a/libs/ui/include/ui/DisplayMap.h
+++ b/libs/ui/include/ui/DisplayMap.h
@@ -18,6 +18,7 @@
#include <ftl/small_map.h>
#include <ftl/small_vector.h>
+#include <ftl/unit.h>
namespace android::ui {
@@ -30,6 +31,8 @@
constexpr size_t kPhysicalDisplayCapacity = 3;
template <typename Key, typename Value>
using PhysicalDisplayMap = ftl::SmallMap<Key, Value, kPhysicalDisplayCapacity>;
+template <typename Key>
+using PhysicalDisplaySet = ftl::SmallMap<Key, ftl::Unit, kPhysicalDisplayCapacity>;
template <typename T>
using DisplayVector = ftl::SmallVector<T, kDisplayCapacity>;
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/libs/ui/include/ui/RingBuffer.h
similarity index 93%
rename from services/surfaceflinger/Utils/RingBuffer.h
rename to libs/ui/include/ui/RingBuffer.h
index 215472b..31d5a95 100644
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ b/libs/ui/include/ui/RingBuffer.h
@@ -19,7 +19,7 @@
#include <stddef.h>
#include <array>
-namespace android::utils {
+namespace android::ui {
template <class T, size_t SIZE>
class RingBuffer {
@@ -31,8 +31,8 @@
~RingBuffer() = default;
constexpr size_t capacity() const { return SIZE; }
-
size_t size() const { return mCount; }
+ bool isFull() const { return size() == capacity(); }
T& next() {
mHead = static_cast<size_t>(mHead + 1) % SIZE;
@@ -67,4 +67,4 @@
size_t mCount = 0;
};
-} // namespace android::utils
+} // namespace android::ui
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
index 70c50f0..98018d9 100644
--- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -36,7 +36,7 @@
*/
inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace,
std::optional<ui::PixelFormat> pixelFormat,
- float hdrSdrRatio = 1.f) {
+ float hdrSdrRatio = 1.f, bool hasHdrMetadata = false) {
const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
const auto range = dataspace & HAL_DATASPACE_RANGE_MASK;
@@ -49,7 +49,8 @@
HAL_DATASPACE_RANGE_EXTENDED);
if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) &&
- pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) {
+ pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16 &&
+ hasHdrMetadata) {
return HdrRenderType::GENERIC_HDR;
}
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 2d8a1e3..2b11786 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -144,6 +144,17 @@
}
cc_test {
+ name: "RingBuffer_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["RingBuffer_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/RingBuffer_test.cpp b/libs/ui/tests/RingBuffer_test.cpp
new file mode 100644
index 0000000..9839492
--- /dev/null
+++ b/libs/ui/tests/RingBuffer_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 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 <ui/RingBuffer.h>
+
+namespace android::ui {
+
+TEST(RingBuffer, basic) {
+ RingBuffer<int, 5> rb;
+
+ rb.next() = 1;
+ ASSERT_EQ(1, rb.size());
+ ASSERT_EQ(1, rb.back());
+ ASSERT_EQ(1, rb.front());
+
+ rb.next() = 2;
+ ASSERT_EQ(2, rb.size());
+ ASSERT_EQ(2, rb.back());
+ ASSERT_EQ(1, rb.front());
+ ASSERT_EQ(1, rb[-1]);
+
+ rb.next() = 3;
+ ASSERT_EQ(3, rb.size());
+ ASSERT_EQ(3, rb.back());
+ ASSERT_EQ(1, rb.front());
+ ASSERT_EQ(2, rb[-1]);
+ ASSERT_EQ(1, rb[-2]);
+
+ rb.next() = 4;
+ ASSERT_EQ(4, rb.size());
+ ASSERT_EQ(4, rb.back());
+ ASSERT_EQ(1, rb.front());
+ ASSERT_EQ(3, rb[-1]);
+ ASSERT_EQ(2, rb[-2]);
+ ASSERT_EQ(1, rb[-3]);
+
+ rb.next() = 5;
+ ASSERT_EQ(5, rb.size());
+ ASSERT_EQ(5, rb.back());
+ ASSERT_EQ(1, rb.front());
+ ASSERT_EQ(4, rb[-1]);
+ ASSERT_EQ(3, rb[-2]);
+ ASSERT_EQ(2, rb[-3]);
+ ASSERT_EQ(1, rb[-4]);
+
+ rb.next() = 6;
+ ASSERT_EQ(5, rb.size());
+ ASSERT_EQ(6, rb.back());
+ ASSERT_EQ(2, rb.front());
+ ASSERT_EQ(5, rb[-1]);
+ ASSERT_EQ(4, rb[-2]);
+ ASSERT_EQ(3, rb[-3]);
+ ASSERT_EQ(2, rb[-4]);
+}
+
+} // namespace android::ui
\ No newline at end of file
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 04c525e..7faf361 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -356,7 +356,7 @@
// If we're going to be over the cache limit, kick off a trim to clear space
if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
- ALOGV("SET: Cache is full, calling trimCache to clear space");
+ ALOGW("SET: Cache is full, calling trimCache to clear space");
trimCache();
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 5fe9484..0dd9f19 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -134,6 +134,11 @@
if (cnx->egl.eglGetPlatformDisplay) {
std::vector<EGLAttrib> attrs;
+ // These must have the same lifetime as |attrs|, because |attrs| contains pointers to these
+ // variables.
+ std::vector<const char*> enabled; // ANGLE features to enable
+ std::vector<const char*> disabled; // ANGLE features to disable
+
if (attrib_list) {
for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
attrs.push_back(attr[0]);
@@ -142,9 +147,6 @@
}
if (graphicsenv_flags::feature_overrides()) {
- std::vector<const char*> enabled; // ANGLE features to enable
- std::vector<const char*> disabled; // ANGLE features to disable
-
// Get the list of ANGLE features to enable from Global.Settings.
const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
for (const std::string& eglFeature : eglFeatures) {
@@ -154,25 +156,24 @@
// Get the list of ANGLE features to enable/disable from gpuservice.
GraphicsEnv::getInstance().getAngleFeatureOverrides(enabled, disabled);
if (!enabled.empty()) {
- enabled.push_back(0);
+ enabled.push_back(nullptr);
attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
attrs.push_back(reinterpret_cast<EGLAttrib>(enabled.data()));
}
if (!disabled.empty()) {
- disabled.push_back(0);
+ disabled.push_back(nullptr);
attrs.push_back(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE);
attrs.push_back(reinterpret_cast<EGLAttrib>(disabled.data()));
}
} else {
const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
- std::vector<const char*> features;
- if (eglFeatures.size() > 0) {
+ if (!eglFeatures.empty()) {
for (const std::string& eglFeature : eglFeatures) {
- features.push_back(eglFeature.c_str());
+ enabled.push_back(eglFeature.c_str());
}
- features.push_back(0);
+ enabled.push_back(nullptr);
attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
- attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+ attrs.push_back(reinterpret_cast<EGLAttrib>(enabled.data()));
}
}
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index 5c7f344..e96b17a 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -2,3 +2,4 @@
class hal
user graphics
group automotive_evs
+ disabled
diff --git a/services/displayservice/DisplayEventReceiver.cpp b/services/displayservice/DisplayEventReceiver.cpp
index 2bb74c2..9927fb6 100644
--- a/services/displayservice/DisplayEventReceiver.cpp
+++ b/services/displayservice/DisplayEventReceiver.cpp
@@ -22,6 +22,7 @@
#include <android/frameworks/displayservice/1.0/BpHwEventCallback.h>
#include <thread>
+#include <ftl/enum.h>
namespace android {
namespace frameworks {
@@ -97,11 +98,11 @@
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
const FwkReceiver::Event &event = buf[i];
- uint32_t type = event.header.type;
+ android::DisplayEventType type = event.header.type;
uint64_t timestamp = event.header.timestamp;
switch(buf[i].header.type) {
- case FwkReceiver::DISPLAY_EVENT_VSYNC: {
+ case DisplayEventType::DISPLAY_EVENT_VSYNC: {
auto ret = mCallback->onVsync(timestamp, event.vsync.count);
if (!ret.isOk()) {
LOG(ERROR) << "AttachedEvent handleEvent fails on onVsync callback"
@@ -109,7 +110,7 @@
return 0; // remove the callback
}
} break;
- case FwkReceiver::DISPLAY_EVENT_HOTPLUG: {
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG: {
auto ret = mCallback->onHotplug(timestamp, event.hotplug.connected);
if (!ret.isOk()) {
LOG(ERROR) << "AttachedEvent handleEvent fails on onHotplug callback"
@@ -118,7 +119,8 @@
}
} break;
default: {
- LOG(ERROR) << "AttachedEvent handleEvent unknown type: " << type;
+ LOG(ERROR) << "AttachedEvent handleEvent unknown type: "
+ << ftl::to_underlying(type);
}
}
}
diff --git a/services/displayservice/OWNERS b/services/displayservice/OWNERS
index 7a3e4c2..40164aa 100644
--- a/services/displayservice/OWNERS
+++ b/services/displayservice/OWNERS
@@ -1,2 +1 @@
smoreland@google.com
-lpy@google.com
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 689221f..74e354f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -7,6 +7,13 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "gpuservice_flags",
+ package: "com.android.frameworks.gpuservice.flags",
+ container: "system",
+ srcs: ["gpuservice_flags.aconfig"],
+}
+
cc_defaults {
name: "gpuservice_defaults",
cflags: [
@@ -20,6 +27,11 @@
}
cc_aconfig_library {
+ name: "gpuservice_multiuser_flags_c_lib",
+ aconfig_declarations: "gpuservice_flags",
+}
+
+cc_aconfig_library {
name: "gpuservice_flags_c_lib",
aconfig_declarations: "graphicsenv_flags",
}
@@ -27,6 +39,7 @@
cc_defaults {
name: "libgpuservice_defaults",
defaults: [
+ "aconfig_lib_cc_static_link.defaults",
"gpuservice_defaults",
"libfeatureoverride_deps",
"libgfxstats_deps",
@@ -92,6 +105,9 @@
srcs: [
":libgpuservice_sources",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
}
cc_defaults {
@@ -126,4 +142,7 @@
static_libs: [
"libgpuservice",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index fadb1fd..25ee21f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,7 +24,11 @@
#include <binder/IResultReceiver.h>
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
+#include <com_android_frameworks_gpuservice_flags.h>
+#include <com_android_graphics_graphicsenv_flags.h>
#include <cutils/properties.h>
+#include <cutils/multiuser.h>
+#include <feature_override/FeatureOverrideParser.h>
#include <gpumem/GpuMem.h>
#include <gpuwork/GpuWork.h>
#include <gpustats/GpuStats.h>
@@ -38,6 +42,9 @@
#include <thread>
#include <memory>
+namespace gpuservice_flags = com::android::frameworks::gpuservice::flags;
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
+
namespace android {
using base::StringAppendF;
@@ -113,11 +120,22 @@
// only system_server with the ACCESS_GPU_SERVICE permission is allowed to set
// persist.graphics.egl
- if (uid != AID_SYSTEM ||
- !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
- ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
+ if (gpuservice_flags::multiuser_permission_check()) {
+ // retrieve the appid of Settings app on multiuser builds
+ const int multiuserappid = multiuser_get_app_id(uid);
+ if (multiuserappid != AID_SYSTEM ||
+ !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
+ ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
+ "pid=%d, uid=%d\n, multiuserappid=%d", pid, uid, multiuserappid);
+ return;
+ }
+ } else {
+ if (uid != AID_SYSTEM ||
+ !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
+ ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
"pid=%d, uid=%d\n", pid, uid);
- return;
+ return;
+ }
}
std::lock_guard<std::mutex> lock(mLock);
@@ -128,6 +146,14 @@
}
}
+FeatureOverrides GpuService::getFeatureOverrides() {
+ if (!graphicsenv_flags::feature_overrides()) {
+ FeatureOverrides featureOverrides;
+ return featureOverrides;
+ }
+
+ return mFeatureOverrideParser.getFeatureOverrides();
+}
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -156,7 +182,11 @@
for (size_t i = 0, n = args.size(); i < n; i++)
ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str());
- if (args.size() >= 1) {
+ if (!args.empty()) {
+ if (graphicsenv_flags::feature_overrides()) {
+ if (args[0] == String16("featureOverrides"))
+ return cmdFeatureOverrides(out, err);
+ }
if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
if (args[0] == String16("vkprofiles")) return cmdVkprofiles(out, err);
if (args[0] == String16("help")) return cmdHelp(out);
@@ -220,6 +250,11 @@
return NO_ERROR;
}
+status_t GpuService::cmdFeatureOverrides(int out, int /*err*/) {
+ dprintf(out, "%s\n", mFeatureOverrideParser.getFeatureOverrides().toString().c_str());
+ return NO_ERROR;
+}
+
namespace {
status_t cmdHelp(int out) {
@@ -232,6 +267,10 @@
"GPU Service commands:\n"
" vkjson dump Vulkan properties as JSON\n"
" vkprofiles print support for select Vulkan profiles\n");
+ if (graphicsenv_flags::feature_overrides()) {
+ fprintf(outs,
+ " featureOverrides update and output gpuservice's feature overrides\n");
+ }
fclose(outs);
return NO_ERROR;
}
diff --git a/services/gpuservice/feature_override/Android.bp b/services/gpuservice/feature_override/Android.bp
index cda67f0..842a0c4 100644
--- a/services/gpuservice/feature_override/Android.bp
+++ b/services/gpuservice/feature_override/Android.bp
@@ -34,9 +34,7 @@
"libbase",
"libgraphicsenv",
"liblog",
- ],
- static_libs: [
- "libprotobuf-cpp-lite-ndk",
+ "libprotobuf-cpp-lite",
],
}
@@ -71,6 +69,7 @@
name: "libfeatureoverride",
defaults: [
"libfeatureoverride_deps",
+ "libvkjson_deps",
],
srcs: [
":feature_config_proto_definitions",
@@ -87,6 +86,9 @@
cppflags: [
"-Wno-sign-compare",
],
+ static_libs: [
+ "libvkjson",
+ ],
export_include_dirs: ["include"],
proto: {
type: "lite",
diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
index 1ad637c..26ff84a 100644
--- a/services/gpuservice/feature_override/FeatureOverrideParser.cpp
+++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
@@ -23,8 +23,10 @@
#include <sys/stat.h>
#include <vector>
+#include <android-base/macros.h>
#include <graphicsenv/FeatureOverrides.h>
#include <log/log.h>
+#include <vkjson.h>
#include "feature_config.pb.h"
@@ -35,13 +37,53 @@
featureOverrides.mPackageFeatures.clear();
}
+bool
+gpuVendorIdMatches(const VkJsonInstance &vkJsonInstance,
+ const uint32_t &configVendorId) {
+ if (vkJsonInstance.devices.empty()) {
+ return false;
+ }
+
+ // Always match the TEST Vendor ID
+ if (configVendorId == feature_override::GpuVendorID::VENDOR_ID_TEST) {
+ return true;
+ }
+
+ // Always assume one GPU device.
+ uint32_t vendorID = vkJsonInstance.devices.front().properties.vendorID;
+
+ return vendorID == configVendorId;
+}
+
+bool
+conditionsMet(const VkJsonInstance &vkJsonInstance,
+ const android::FeatureConfig &featureConfig) {
+ bool gpuVendorIdMatch = false;
+
+ if (featureConfig.mGpuVendorIDs.empty()) {
+ gpuVendorIdMatch = true;
+ } else {
+ for (const auto &gpuVendorID: featureConfig.mGpuVendorIDs) {
+ if (gpuVendorIdMatches(vkJsonInstance, gpuVendorID)) {
+ gpuVendorIdMatch = true;
+ break;
+ }
+ }
+ }
+
+ return gpuVendorIdMatch;
+}
+
void initFeatureConfig(android::FeatureConfig &featureConfig,
const feature_override::FeatureConfig &featureConfigProto) {
featureConfig.mFeatureName = featureConfigProto.feature_name();
featureConfig.mEnabled = featureConfigProto.enabled();
+ for (const auto &gpuVendorIdProto: featureConfigProto.gpu_vendor_ids()) {
+ featureConfig.mGpuVendorIDs.emplace_back(static_cast<uint32_t>(gpuVendorIdProto));
+ }
}
-feature_override::FeatureOverrideProtos readFeatureConfigProtos(std::string configFilePath) {
+feature_override::FeatureOverrideProtos readFeatureConfigProtos(const std::string &configFilePath) {
feature_override::FeatureOverrideProtos overridesProtos;
std::ifstream protobufBinaryFile(configFilePath.c_str());
@@ -78,9 +120,15 @@
bool FeatureOverrideParser::shouldReloadFeatureOverrides() const {
std::string configFilePath = getFeatureOverrideFilePath();
+
+ std::ifstream configFile(configFilePath);
+ if (!configFile.good()) {
+ return false;
+ }
+
struct stat fileStat{};
- if (stat(getFeatureOverrideFilePath().c_str(), &fileStat) != 0) {
- ALOGE("Error getting file information for '%s': %s", getFeatureOverrideFilePath().c_str(),
+ if (stat(configFilePath.c_str(), &fileStat) != 0) {
+ ALOGE("Error getting file information for '%s': %s", configFilePath.c_str(),
strerror(errno));
// stat'ing the file failed, so return false since reading it will also likely fail.
return false;
@@ -90,7 +138,6 @@
}
void FeatureOverrideParser::forceFileRead() {
- resetFeatureOverrides(mFeatureOverrides);
mLastProtobufReadTime = 0;
}
@@ -98,12 +145,25 @@
const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos(
getFeatureOverrideFilePath());
+ // Clear out the stale values before adding the newly parsed data.
+ resetFeatureOverrides(mFeatureOverrides);
+
+ if (overridesProtos.global_features().empty() &&
+ overridesProtos.package_features().empty()) {
+ // No overrides to parse.
+ return;
+ }
+
+ const VkJsonInstance vkJsonInstance = VkJsonGetInstance();
+
// Global feature overrides.
for (const auto &featureConfigProto: overridesProtos.global_features()) {
FeatureConfig featureConfig;
initFeatureConfig(featureConfig, featureConfigProto);
- mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
+ if (conditionsMet(vkJsonInstance, featureConfig)) {
+ mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
+ }
}
// App-specific feature overrides.
@@ -120,7 +180,9 @@
FeatureConfig featureConfig;
initFeatureConfig(featureConfig, featureConfigProto);
- featureConfigs.emplace_back(featureConfig);
+ if (conditionsMet(vkJsonInstance, featureConfig)) {
+ featureConfigs.emplace_back(featureConfig);
+ }
}
mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs;
diff --git a/services/gpuservice/feature_override/proto/feature_config.proto b/services/gpuservice/feature_override/proto/feature_config.proto
index 4d4bf28..f285187 100644
--- a/services/gpuservice/feature_override/proto/feature_config.proto
+++ b/services/gpuservice/feature_override/proto/feature_config.proto
@@ -21,14 +21,43 @@
option optimize_for = LITE_RUNTIME;
/**
+ * GPU Vendor IDs.
+ * Taken from: external/angle/src/libANGLE/renderer/driver_utils.h
+ */
+enum GpuVendorID
+{
+ // Test ID matches every GPU Vendor ID.
+ VENDOR_ID_TEST = 0;
+ VENDOR_ID_AMD = 0x1002;
+ VENDOR_ID_ARM = 0x13B5;
+ // Broadcom devices won't use PCI, but this is their Vulkan vendor id.
+ VENDOR_ID_BROADCOM = 0x14E4;
+ VENDOR_ID_GOOGLE = 0x1AE0;
+ VENDOR_ID_INTEL = 0x8086;
+ VENDOR_ID_MESA = 0x10005;
+ VENDOR_ID_MICROSOFT = 0x1414;
+ VENDOR_ID_NVIDIA = 0x10DE;
+ VENDOR_ID_POWERVR = 0x1010;
+ // This is Qualcomm PCI Vendor ID.
+ // Android doesn't have a PCI bus, but all we need is a unique id.
+ VENDOR_ID_QUALCOMM = 0x5143;
+ VENDOR_ID_SAMSUNG = 0x144D;
+ VENDOR_ID_VIVANTE = 0x9999;
+ VENDOR_ID_VMWARE = 0x15AD;
+ VENDOR_ID_VIRTIO = 0x1AF4;
+}
+
+/**
* Feature Configuration
* feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h).
* enabled: Either enable or disable the feature.
+ * gpu_vendor_ids: The GPU architectures this FeatureConfig applies to, if any.
*/
message FeatureConfig
{
string feature_name = 1;
bool enabled = 2;
+ repeated GpuVendorID gpu_vendor_ids = 3;
}
/**
diff --git a/services/gpuservice/gpuservice_flags.aconfig b/services/gpuservice/gpuservice_flags.aconfig
new file mode 100644
index 0000000..be6a7bb
--- /dev/null
+++ b/services/gpuservice/gpuservice_flags.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.frameworks.gpuservice.flags"
+container: "system"
+
+flag {
+ name: "multiuser_permission_check"
+ namespace: "gpu"
+ description: "Whether to consider headless system user mode/multiuser when checking toggleAngleAsSystemDriver permission."
+ bug: "389867658"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 057d127..22be9a7 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -20,6 +20,7 @@
#include <binder/IInterface.h>
#include <cutils/compiler.h>
#include <feature_override/FeatureOverrideParser.h>
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <graphicsenv/IGpuService.h>
#include <serviceutils/PriorityDumper.h>
@@ -64,6 +65,7 @@
const uint64_t* values, const uint32_t valueCount) override;
void setUpdatableDriverPath(const std::string& driverPath) override;
std::string getUpdatableDriverPath() override;
+ FeatureOverrides getFeatureOverrides() override;
void toggleAngleAsSystemDriver(bool enabled) override;
void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode,
const char *engineName) override;
@@ -86,6 +88,8 @@
status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+ status_t cmdFeatureOverrides(int out, int /*err*/);
+
/*
* Attributes
*/
diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
index d4d48c4..7be3253 100644
--- a/services/gpuservice/tests/fuzzers/Android.bp
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -13,6 +13,9 @@
"libgpuservice",
"liblog",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
fuzz_config: {
cc: [
"paulthomson@google.com",
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index d2184d8..0dac24d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -89,6 +89,7 @@
],
header_libs: ["bpf_headers"],
shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
"libbase",
"libbinder",
"libbpf_bcc",
diff --git a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
index 65a1b58..66556cd 100644
--- a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
+++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
@@ -70,14 +70,14 @@
};
testing::AssertionResult validateFeatureConfigTestTxtpbSizes(FeatureOverrides overrides) {
- size_t expectedGlobalFeaturesSize = 1;
+ size_t expectedGlobalFeaturesSize = 3;
if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) {
return testing::AssertionFailure()
<< "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size()
<< ", expected: " << expectedGlobalFeaturesSize;
}
- size_t expectedPackageFeaturesSize = 1;
+ size_t expectedPackageFeaturesSize = 3;
if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) {
return testing::AssertionFailure()
<< "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size()
@@ -133,6 +133,96 @@
EXPECT_TRUE(validateGlobalOverrides1(overrides));
}
+testing::AssertionResult validateGlobalOverrides2(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 1;
+ const std::string expectedFeatureName = "globalOverrides2";
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = true;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x13B5, // GpuVendorID::VENDOR_ID_ARM
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides2) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validateGlobalOverrides2(overrides));
+}
+
+testing::AssertionResult validateGlobalOverrides3(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 2;
+ const std::string expectedFeatureName = "globalOverrides3";
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = true;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validateGlobalOverrides3(overrides));
+}
+
testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) {
const std::string expectedTestPackageName = "com.gpuservice_unittest.packageOverrides1";
@@ -155,6 +245,12 @@
const std::string expectedFeatureName = "packageOverrides1";
const FeatureConfig &cfg = features[0];
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
bool expectedEnabled = true;
if (cfg.mEnabled != expectedEnabled) {
return testing::AssertionFailure()
@@ -193,6 +289,160 @@
return testing::AssertionSuccess();
}
+testing::AssertionResult validatePackageOverrides2(FeatureOverrides overrides) {
+ const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides2";
+
+ if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+ }
+
+ const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+ size_t expectedFeaturesSize = 1;
+ if (features.size() != expectedFeaturesSize) {
+ return testing::AssertionFailure()
+ << "features.size(): " << features.size()
+ << ", expectedFeaturesSize: " << expectedFeaturesSize;
+ }
+
+ const std::string expectedFeatureName = "packageOverrides2";
+ const FeatureConfig &cfg = features[0];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = false;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides2) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validatePackageOverrides2(overrides));
+}
+
+testing::AssertionResult validatePackageOverrides3(FeatureOverrides overrides) {
+ const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides3";
+
+ if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+ }
+
+ const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+ size_t expectedFeaturesSize = 2;
+ if (features.size() != expectedFeaturesSize) {
+ return testing::AssertionFailure()
+ << "features.size(): " << features.size()
+ << ", expectedFeaturesSize: " << expectedFeaturesSize;
+ }
+
+ std::string expectedFeatureName = "packageOverrides3_1";
+ const FeatureConfig &cfg_1 = features[0];
+
+ if (cfg_1.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg_1.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = false;
+ if (cfg_1.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg_1.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x13B5, // GpuVendorID::VENDOR_ID_ARM
+ };
+ if (cfg_1.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg_1.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg_1.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_1.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ expectedFeatureName = "packageOverrides3_2";
+ const FeatureConfig &cfg_2 = features[1];
+
+ if (cfg_2.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg_2.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ expectedEnabled = true;
+ if (cfg_2.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg_2.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg_2.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg_2.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg_2.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_2.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validatePackageOverrides3(overrides));
+}
+
TEST_F(FeatureOverrideParserTest, forceFileRead) {
FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
diff --git a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
index 726779e..44a6f78 100644
--- a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
+++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
@@ -22,6 +22,22 @@
{
feature_name: "globalOverrides1"
enabled: False
+ },
+ {
+ feature_name: "globalOverrides2"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_ARM
+ ]
+ },
+ {
+ feature_name: "globalOverrides3"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
}
]
@@ -36,5 +52,39 @@
enabled: True
}
]
+ },
+ {
+ package_name: "com.gpuservice_unittest.packageOverrides2"
+ feature_configs: [
+ {
+ feature_name: "packageOverrides2"
+ enabled: False
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
+ }
+ ]
+ },
+ {
+ package_name: "com.gpuservice_unittest.packageOverrides3"
+ feature_configs: [
+ {
+ feature_name: "packageOverrides3_1"
+ enabled: False
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_ARM
+ ]
+ },
+ {
+ feature_name: "packageOverrides3_2"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
+ }
+ ]
}
]
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index a63de1c..13a089f 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,8 +1,5 @@
# Bug component: 653544
kocdemir@google.com
paulthomson@google.com
-pbaiget@google.com
-lfy@google.com
chrisforbes@google.com
-lpy@google.com
alecmouri@google.com
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 3140dc8..f8ab830 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -19,9 +19,11 @@
#include <android-base/logging.h>
#include <android/configuration.h>
#include <com_android_input_flags.h>
+#include <algorithm>
#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
#endif
+#include <input/InputFlags.h>
#include <input/Keyboard.h>
#include <input/PrintTools.h>
#include <unordered_set>
@@ -34,37 +36,6 @@
namespace {
-bool isFromMouse(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
- args.pointerProperties[0].toolType == ToolType::MOUSE;
-}
-
-bool isFromTouchpad(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
- args.pointerProperties[0].toolType == ToolType::FINGER;
-}
-
-bool isFromDrawingTablet(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
- isStylusToolType(args.pointerProperties[0].toolType);
-}
-
-bool isHoverAction(int32_t action) {
- return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
-}
-
-bool isStylusHoverEvent(const NotifyMotionArgs& args) {
- return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
-}
-
-bool isMouseOrTouchpad(uint32_t sources) {
- // Check if this is a mouse or touchpad, but not a drawing tablet.
- return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
- (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
- !isFromSource(sources, AINPUT_SOURCE_STYLUS));
-}
-
inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
PointerChoreographerPolicyInterface& policy) {
if (!change) {
@@ -161,10 +132,11 @@
}),
mNextListener(listener),
mPolicy(policy),
- mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
+ mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false),
+ mPointerMotionFilterEnabled(false),
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
mIsWindowInfoListenerRegistered(false),
mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -236,15 +208,16 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
std::scoped_lock _l(getLock());
- if (isFromMouse(args)) {
+ if (isFromMouse(args.source, args.pointerProperties[0].toolType)) {
newArgs = processMouseEventLocked(args);
pointerDisplayChange = calculatePointerDisplayChangeToNotify();
- } else if (isFromTouchpad(args)) {
+ } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) {
newArgs = processTouchpadEventLocked(args);
pointerDisplayChange = calculatePointerDisplayChangeToNotify();
- } else if (isFromDrawingTablet(args)) {
+ } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) {
processDrawingTabletEventLocked(args);
- } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ } else if (mStylusPointerIconEnabled &&
+ isStylusHoverEvent(args.source, args.pointerProperties, args.action)) {
processStylusHoverEventLocked(args);
} else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
processTouchscreenAndStylusEventLocked(args);
@@ -322,9 +295,11 @@
PointerControllerInterface& pc) {
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-
- vec2 unconsumedDelta = pc.move(deltaX, deltaY);
- if (com::android::input::flags::connected_displays_cursor() &&
+ vec2 filteredDelta =
+ filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
+ newArgs.displayId);
+ vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
+ if (InputFlags::connectedDisplaysCursorEnabled() &&
(std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
handleUnconsumedDeltaLocked(pc, unconsumedDelta);
// pointer may have moved to a different viewport
@@ -386,7 +361,7 @@
LOG(FATAL) << "A cursor already exists on destination display"
<< destinationViewport.displayId;
}
- mDefaultMouseDisplayId = destinationViewport.displayId;
+ mCurrentMouseDisplayId = destinationViewport.displayId;
auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
pcNode.key() = destinationViewport.displayId;
mMousePointersByDisplay.insert(std::move(pcNode));
@@ -501,6 +476,14 @@
<< args.dump();
}
+ // Fade the mouse pointer on the display if there is one when the stylus starts hovering.
+ if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+ if (const auto it = mMousePointersByDisplay.find(args.displayId);
+ it != mMousePointersByDisplay.end()) {
+ it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+ }
+
// Get the stylus pointer controller for the device, or create one if it doesn't exist.
auto [it, controllerAdded] =
mStylusPointersByDevice.try_emplace(args.deviceId,
@@ -552,9 +535,6 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
- return;
- }
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
!mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
@@ -619,15 +599,21 @@
}
void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
- std::scoped_lock _l(getLock());
- mTopology = displayTopologyGraph;
+ PointerDisplayChange pointerDisplayChange;
+ { // acquire lock
+ std::scoped_lock _l(getLock());
+ mTopology = displayTopologyGraph;
- // make primary display default mouse display, if it was not set
- // or the existing display was removed
- if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID ||
- mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) {
- mDefaultMouseDisplayId = mTopology.primaryDisplayId;
- }
+ // make primary display default mouse display, if it was not set or
+ // the existing display was removed
+ if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+ mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
+ mCurrentMouseDisplayId = mTopology.primaryDisplayId;
+ pointerDisplayChange = updatePointerControllersLocked();
+ }
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::dump(std::string& dump) {
@@ -638,6 +624,8 @@
mShowTouchesEnabled ? "true" : "false");
dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
mStylusPointerIconEnabled ? "true" : "false");
+ dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
+ mPointerMotionFilterEnabled ? "true" : "false");
dump += INDENT "MousePointerControllers:\n";
for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
@@ -674,7 +662,25 @@
ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
ui::LogicalDisplayId associatedDisplayId) const {
- return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
+ if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
+ if (associatedDisplayId.isValid()) {
+ return associatedDisplayId;
+ }
+ return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
+ : ui::LogicalDisplayId::DEFAULT;
+ }
+ // Associated display is not included in the topology, return this associated display.
+ if (associatedDisplayId.isValid() &&
+ mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
+ return associatedDisplayId;
+ }
+ if (mCurrentMouseDisplayId.isValid()) {
+ return mCurrentMouseDisplayId;
+ }
+ if (mTopology.primaryDisplayId.isValid()) {
+ return mTopology.primaryDisplayId;
+ }
+ return ui::LogicalDisplayId::DEFAULT;
}
std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
@@ -783,7 +789,8 @@
PointerChoreographer::calculatePointerDisplayChangeToNotify() {
ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
vec2 cursorPosition = {0, 0};
- if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+ if (const auto it =
+ mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId));
it != mMousePointersByDisplay.end()) {
const auto& pointerController = it->second;
// Use the displayId from the pointerController, because it accurately reflects whether
@@ -800,12 +807,16 @@
}
void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+ if (InputFlags::connectedDisplaysCursorEnabled()) {
+ // In connected displays scenario, default mouse display will only be updated from topology.
+ return;
+ }
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
std::scoped_lock _l(getLock());
- mDefaultMouseDisplayId = displayId;
+ mCurrentMouseDisplayId = displayId;
pointerDisplayChange = updatePointerControllersLocked();
} // release lock
@@ -973,6 +984,11 @@
mCurrentFocusedDisplay = displayId;
}
+void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
+ std::scoped_lock _l(getLock());
+ mPointerMotionFilterEnabled = enabled;
+}
+
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -1046,6 +1062,21 @@
return std::nullopt;
}
+vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
+ if (!mPointerMotionFilterEnabled) {
+ return delta;
+ }
+ std::optional<vec2> filterResult =
+ mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
+ if (!filterResult.has_value()) {
+ // Disable filter when there's any error.
+ mPointerMotionFilterEnabled = false;
+ return delta;
+ }
+ return *filterResult;
+}
+
// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index a9d971a..67bdca1 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -90,6 +90,11 @@
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
+
+ /**
+ * Enables motion event filter before pointer coordinates are determined.
+ */
+ virtual void setAccessibilityPointerMotionFilterEnabled(bool enabled) = 0;
};
class PointerChoreographer : public PointerChoreographerInterface {
@@ -110,6 +115,7 @@
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+ void setAccessibilityPointerMotionFilterEnabled(bool enabled) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -168,6 +174,10 @@
const DisplayTopologyPosition sourceBoundary,
int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
+ vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta,
+ const ui::LogicalDisplayId& displayId)
+ REQUIRES(getLock());
+
/* Topology is initialized with default-constructed value, which is an empty topology. Till we
* receive setDisplayTopology call.
* Meanwhile Choreographer will treat every display as independent disconnected display.
@@ -221,13 +231,19 @@
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
GUARDED_BY(getLock());
- ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+ // In connected displays scenario, this tracks the latest display the cursor is at, within the
+ // DisplayTopology. By default, this will be set to topology primary display, and updated when
+ // mouse crossed to another display.
+ // In non-connected displays scenario, this will be treated as the default display cursor
+ // will be on, when mouse doesn't have associated display.
+ ui::LogicalDisplayId mCurrentMouseDisplayId GUARDED_BY(getLock());
ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
bool mShowTouchesEnabled GUARDED_BY(getLock());
bool mStylusPointerIconEnabled GUARDED_BY(getLock());
+ bool mPointerMotionFilterEnabled GUARDED_BY(getLock());
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a500567..ef50fc0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -32,6 +32,7 @@
#include <gui/SurfaceComposerClient.h>
#endif
#include <input/InputDevice.h>
+#include <input/InputFlags.h>
#include <input/PrintTools.h>
#include <input/TraceTools.h>
#include <openssl/mem.h>
@@ -935,6 +936,7 @@
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
mConnectionManager(mLooper),
+ mTouchStates(mWindowInfos, mConnectionManager),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
@@ -971,6 +973,9 @@
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener);
+#endif
mCommandQueue.clear();
}
@@ -1141,9 +1146,7 @@
// 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.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Dispatch frozen. Waiting some more.";
return;
}
@@ -1464,14 +1467,13 @@
std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets(
ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
- int32_t pointerId, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+ int32_t pointerId, std::function<void()> dump) {
if (touchedWindow == nullptr) {
return {};
}
// Traverse windows from front to back until we encounter the touched window.
std::vector<InputTarget> outsideTargets;
- const auto& windowHandles = windowInfos.getWindowHandlesForDisplay(displayId);
+ const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (windowHandle == touchedWindow) {
// Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
@@ -1483,13 +1485,10 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- DispatcherTouchState::addPointerWindowTarget(windowHandle,
- InputTarget::DispatchMode::OUTSIDE,
- ftl::Flags<InputTarget::Flags>(),
- pointerIds,
- /*firstDownTimeInTarget=*/std::nullopt,
- connections, windowInfos, dump,
- outsideTargets);
+ addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::OUTSIDE,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt,
+ /*pointerDisplayId=*/std::nullopt, dump, outsideTargets);
}
}
return outsideTargets;
@@ -1521,9 +1520,7 @@
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
- if (debugInboundEventDetails()) {
- ALOGD("Dropped event because policy consumed it.");
- }
+ LOG_IF(INFO, debugInboundEventDetails()) << "Dropped event because policy consumed it.";
reason = "inbound event was dropped because the policy consumed it";
break;
case DropReason::DISABLED:
@@ -1637,9 +1634,7 @@
void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) {
const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Injected inbound event was dropped.");
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "Injected inbound event was dropped.";
setInjectionResult(*entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
@@ -1679,10 +1674,9 @@
bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
const DeviceResetEntry& entry) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
- entry.deviceId);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "dispatchDeviceReset - eventTime=" << entry.eventTime
+ << ", deviceId=" << entry.deviceId;
// Reset key repeating in case a keyboard device was disabled or enabled.
if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1874,9 +1868,8 @@
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
// The key on device 'deviceId' is still down, do not stop key repeat
- if (debugInboundEventDetails()) {
- ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "deviceId=" << entry->deviceId << " got KEY_UP as stale";
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -1973,25 +1966,24 @@
}
void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%s, "
- "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.toString().c_str(), entry.policyFlags, entry.action, entry.flags,
- entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId
+ << ", source=0x" << std::hex << entry.source
+ << ", displayId=" << entry.displayId.toString() << ", policyFlags=0x"
+ << entry.policyFlags << ", action=0x" << entry.action << ", flags=0x" << entry.flags
+ << ", keyCode=0x" << entry.keyCode << ", scanCode=0x" << entry.scanCode
+ << ", metaState=0x" << entry.metaState << ", repeatCount=" << std::dec
+ << entry.repeatCount << ", downTime=" << entry.downTime;
}
void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
const std::shared_ptr<const SensorEntry>& entry,
DropReason* dropReason, nsecs_t& nextWakeupTime) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
- "source=0x%x, sensorType=%s",
- entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- ftl::enum_string(entry->sensorType).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "notifySensorEvent eventTime=" << entry->eventTime
+ << ", hwTimestamp=" << entry->hwTimestamp << ", deviceId=" << entry->deviceId
+ << ", source=0x" << std::hex << entry->source << std::dec
+ << ", sensorType=" << ftl::enum_string(entry->sensorType);
auto command = [this, entry]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -2005,10 +1997,9 @@
}
bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
- ftl::enum_string(sensorType).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "flushSensor deviceId=" << deviceId
+ << ", sensorType=" << ftl::enum_string(sensorType).c_str();
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -2059,8 +2050,7 @@
Result<std::vector<InputTarget>, InputEventInjectionResult> result =
mTouchStates
- .findTouchedWindowTargets(currentTime, *entry, mConnectionManager,
- mWindowInfos,
+ .findTouchedWindowTargets(currentTime, *entry,
mDragState ? mDragState->dragWindow : nullptr,
std::bind_front(&InputDispatcher::
addDragEventLocked,
@@ -2178,9 +2168,7 @@
std::shared_ptr<const EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("dispatchEventToCurrentInputTargets");
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "dispatchEventToCurrentInputTargets";
processInteractionsLocked(*eventEntry, inputTargets);
@@ -2223,9 +2211,7 @@
}
void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
- if (DEBUG_FOCUS) {
- ALOGD("Resetting ANR timeouts.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Resetting ANR timeouts.";
// Reset input target wait timeout.
mNoFocusedWindowTimeoutTime = std::nullopt;
@@ -2384,8 +2370,7 @@
base::Result<std::vector<InputTarget>, os::InputEventInjectionResult>
InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
- nsecs_t currentTime, const MotionEntry& entry, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
+ nsecs_t currentTime, const MotionEntry& entry,
const sp<android::gui::WindowInfoHandle> dragWindow,
std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) {
ATRACE_CALL();
@@ -2398,12 +2383,11 @@
const int32_t maskedAction = MotionEvent::getActionMasked(action);
// Copy current touch state into tempTouchState.
- // This state will be used to update mTouchStatesByDisplay at the end of this function.
+ // This state will be used to update saved touch state at the end of this function.
// If no state for the specified display exists, then our initial state will be empty.
- const TouchState* oldState = nullptr;
+ const TouchState* oldState = getTouchStateForMotionEntry(entry);
TouchState tempTouchState;
- if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- oldState = &(it->second);
+ if (oldState != nullptr) {
tempTouchState = *oldState;
}
@@ -2447,12 +2431,10 @@
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
sp<WindowInfoHandle> newTouchedWindowHandle =
- windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (isDown) {
- targets += DispatcherTouchState::findOutsideTargets(displayId, newTouchedWindowHandle,
- pointer.id, connections,
- windowInfos, dump);
+ targets += findOutsideTargets(displayId, newTouchedWindowHandle, pointer.id, dump);
}
LOG_IF(INFO, newTouchedWindowHandle == nullptr)
<< "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
@@ -2465,7 +2447,7 @@
}
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, windowInfos);
+ findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos);
if (newTouchedWindowHandle != nullptr) {
// Process the foreground window first so that it is the first to receive the event.
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2478,7 +2460,7 @@
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
- if (!canWindowReceiveMotion(windowHandle, entry, connections, windowInfos)) {
+ if (!canWindowReceiveMotion(windowHandle, entry)) {
continue;
}
@@ -2490,8 +2472,7 @@
// Set target flags.
ftl::Flags<InputTarget::Flags> targetFlags =
- DispatcherTouchState::getTargetFlags(windowHandle, {x, y}, isSplit,
- windowInfos);
+ getTargetFlags(windowHandle, {x, y}, isSplit);
// Update the temporary touch state.
@@ -2521,7 +2502,7 @@
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper =
- windowInfos.findWallpaperWindowBelow(windowHandle);
+ mWindowInfos.findWallpaperWindowBelow(windowHandle);
if (wallpaper != nullptr) {
ftl::Flags<InputTarget::Flags> wallpaperFlags =
InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2559,11 +2540,10 @@
// If the pointer is not currently down, then ignore the event.
if (!tempTouchState.isDown(entry.deviceId) &&
maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- if (DEBUG_DROPPED_EVENTS_VERBOSE) {
- LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
- << " is not down or we previously dropped the pointer down event in "
- << "display " << displayId << ": " << entry.getDescription();
- }
+ LOG_IF(INFO, DEBUG_DROPPED_EVENTS_VERBOSE)
+ << "Dropping event because the pointer for device " << entry.deviceId
+ << " is not down or we previously dropped the pointer down event in display "
+ << displayId << ": " << entry.getDescription();
return injectionError(InputEventInjectionResult::FAILED);
}
@@ -2591,7 +2571,7 @@
tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
sp<WindowInfoHandle> newTouchedWindowHandle =
- windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2601,7 +2581,7 @@
// Do not slide events to the window which can not receive motion event
if (newTouchedWindowHandle != nullptr &&
- !canWindowReceiveMotion(newTouchedWindowHandle, entry, connections, windowInfos)) {
+ !canWindowReceiveMotion(newTouchedWindowHandle, entry)) {
newTouchedWindowHandle = nullptr;
}
@@ -2618,18 +2598,16 @@
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
- DispatcherTouchState::
- addPointerWindowTarget(oldTouchedWindowHandle,
- InputTarget::DispatchMode::SLIPPERY_EXIT,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId),
- connections, windowInfos, dump, targets);
+ addPointerWindowTarget(oldTouchedWindowHandle,
+ InputTarget::DispatchMode::SLIPPERY_EXIT,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
// Make a slippery entrance into the new window.
ftl::Flags<InputTarget::Flags> targetFlags =
- DispatcherTouchState::getTargetFlags(newTouchedWindowHandle, {x, y},
- isSplit, windowInfos);
+ getTargetFlags(newTouchedWindowHandle, {x, y}, isSplit);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
InputTarget::DispatchMode::SLIPPERY_ENTER,
@@ -2637,10 +2615,8 @@
entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
- DispatcherTouchState::slipWallpaperTouch(targetFlags, oldTouchedWindowHandle,
- newTouchedWindowHandle, tempTouchState,
- entry, targets, connections, windowInfos,
- dump);
+ slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
+ tempTouchState, entry, targets, dump);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2672,12 +2648,10 @@
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
- touchedWindow.dispatchMode,
- touchedWindow.targetFlags, pointerIds,
- touchedWindow.getDownTimeInTarget(
- entry.deviceId),
- connections, windowInfos, dump, targets);
+ addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
}
}
@@ -2706,7 +2680,7 @@
for (InputTarget& target : targets) {
if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
sp<WindowInfoHandle> targetWindow =
- windowInfos.findWindowHandle(target.connection->getToken());
+ mWindowInfos.findWindowHandle(target.connection->getToken());
if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
target.flags |= InputTarget::Flags::ZERO_COORDS;
}
@@ -2730,13 +2704,10 @@
if (touchingPointers.empty()) {
continue;
}
- DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
- touchedWindow.dispatchMode,
- touchedWindow.targetFlags,
- getPointerIds(touchingPointers),
- touchedWindow.getDownTimeInTarget(
- entry.deviceId),
- connections, windowInfos, dump, targets);
+ addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, getPointerIds(touchingPointers),
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/displayId, dump, targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2791,16 +2762,12 @@
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
if (displayId >= ui::LogicalDisplayId::DEFAULT) {
tempTouchState.clearWindowsWithoutPointers();
- mTouchStatesByDisplay[displayId] = tempTouchState;
+ saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
} else {
- mTouchStatesByDisplay.erase(displayId);
+ eraseTouchStateForMotionEntry(entry);
}
}
- if (tempTouchState.windows.empty()) {
- mTouchStatesByDisplay.erase(displayId);
- }
-
return targets;
}
@@ -2823,8 +2790,9 @@
}
void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
- if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
- mDragState->deviceId != entry.deviceId) {
+ if (!mDragState || mDragState->deviceId != entry.deviceId ||
+ !mWindowInfos.areDisplaysConnected(mDragState->dragWindow->getInfo()->displayId,
+ entry.displayId)) {
return;
}
@@ -2943,8 +2911,8 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget,
- const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump, std::vector<InputTarget>& inputTargets) {
+ std::optional<ui::LogicalDisplayId> pointerDisplayId, std::function<void()> dump,
+ std::vector<InputTarget>& inputTargets) {
if (pointerIds.none()) {
for (const auto& target : inputTargets) {
LOG(INFO) << "Target: " << target;
@@ -2972,14 +2940,15 @@
const WindowInfo& windowInfo = *windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::shared_ptr<Connection> connection = connections.getConnection(windowInfo.token);
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(windowInfo.token);
if (connection == nullptr) {
ALOGW("Not creating InputTarget for %s, no input channel", windowInfo.name.c_str());
return;
}
inputTargets.push_back(
createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
- windowInfos.getRawTransform(*windowHandle->getInfo()),
+ mWindowInfos.getRawTransform(*windowHandle->getInfo(),
+ pointerDisplayId),
firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3243,10 +3212,9 @@
return;
}
if (windowDisablingUserActivityInfo != nullptr) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.",
- windowDisablingUserActivityInfo->name.c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "Not poking user activity: disabled by window '"
+ << windowDisablingUserActivityInfo->name << "'.";
return;
}
break;
@@ -3260,10 +3228,9 @@
// the apps, like system shortcuts
if (windowDisablingUserActivityInfo != nullptr &&
keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.",
- windowDisablingUserActivityInfo->name.c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "Not poking user activity: disabled by window '"
+ << windowDisablingUserActivityInfo->name << "'.";
return;
}
break;
@@ -3291,22 +3258,19 @@
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
connection->getInputChannelName().c_str(), eventEntry->id));
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
- "globalScaleFactor=%f, pointerIds=%s %s",
- connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
- inputTarget.globalScaleFactor, bitsetToString(inputTarget.getPointerIds()).c_str(),
- inputTarget.getPointerInfoString().c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ prepareDispatchCycle - flags=" << inputTarget.flags.string()
+ << ", globalScaleFactor=" << inputTarget.globalScaleFactor
+ << ", pointerIds=" << bitsetToString(inputTarget.getPointerIds()) << " "
+ << inputTarget.getPointerInfoString();
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
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(),
- ftl::enum_string(connection->status).c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "channel '" << connection->getInputChannelName()
+ << "' ~ Dropping event because the channel status is "
+ << ftl::enum_string(connection->status);
return;
}
@@ -3425,11 +3389,10 @@
if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
!connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
motionEntry.displayId)) {
- if (DEBUG_DISPATCH_CYCLE) {
- LOG(DEBUG) << "channel '" << connection->getInputChannelName().c_str()
- << "' ~ enqueueDispatchEntryLocked: filling in missing hover "
- "enter event";
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName().c_str()
+ << "' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
+ "event";
resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
@@ -3723,9 +3686,8 @@
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
connection->getInputChannelName().c_str()));
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName() << "' ~ startDispatchCycle";
while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
std::unique_ptr<DispatchEntry>& dispatchEntry = connection->outboundQueue.front();
@@ -3740,10 +3702,9 @@
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(INFO) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
// Publish the key event.
status = connection->inputPublisher
@@ -3762,10 +3723,9 @@
}
case EventEntry::Type::MOTION: {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(INFO) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
if (status == BAD_VALUE) {
@@ -3838,11 +3798,10 @@
} 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());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ Could not publish event because the pipe is full, waiting for "
+ "the application to catch up";
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -3908,10 +3867,9 @@
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
uint32_t seq, bool handled, nsecs_t consumeTime) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
- connection->getInputChannelName().c_str(), seq, toString(handled));
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ finishDispatchCycle - seq=" << seq << ", handled=" << toString(handled);
if (connection->status != Connection::Status::NORMAL) {
return;
@@ -3926,10 +3884,8 @@
void InputDispatcher::abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
- if (DEBUG_DISPATCH_CYCLE) {
- LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
- << " - notify=" << toString(notify);
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "channel '" << connection->getInputChannelName() << "'~ "
+ << __func__ << " - notify=" << toString(notify);
// Clear the dispatch queues.
drainDispatchQueue(connection->outboundQueue);
@@ -4135,12 +4091,11 @@
return;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
- "with reality: %s, mode=%s.",
- connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
- ftl::enum_string(options.mode).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
+ << cancelationEvents.size()
+ << " cancelation events to bring channel back in sync with reality: " << options.reason
+ << ", mode=" << ftl::enum_string(options.mode) << ".";
std::string reason = std::string("reason=").append(options.reason);
android_log_event_list(LOGTAG_INPUT_CANCEL)
@@ -4193,15 +4148,15 @@
sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
mDragState.reset();
}
- DispatcherTouchState::
- addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- motionEntry.downTime, mConnectionManager,
- mWindowInfos,
- std::bind_front(&InputDispatcher::
- logDispatchStateLocked,
- this),
- targets);
+ mTouchStates
+ .addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
+ std::bind_front(&InputDispatcher::
+ logDispatchStateLocked,
+ this),
+ targets);
} else {
targets.emplace_back(fallbackTarget);
// Since we don't have a window, use the display transform as the raw transform.
@@ -4257,18 +4212,12 @@
return;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
- connection->getInputChannelName().c_str(), downEvents.size());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
+ << downEvents.size() << " down events to ensure consistent event stream.";
- auto touchedWindowHandleAndDisplay =
- mTouchStates.findTouchedWindowHandleAndDisplay(connection->getToken());
- if (!touchedWindowHandleAndDisplay.has_value()) {
- LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
- }
-
- const auto [windowHandle, displayId] = touchedWindowHandleAndDisplay.value();
+ const auto [windowHandle, displayId] =
+ mTouchStates.findExistingTouchedWindowHandleAndDisplay(connection->getToken());
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
@@ -4286,14 +4235,14 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- DispatcherTouchState::
- addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
- targetFlags, pointerIds, motionEntry.downTime,
- mConnectionManager, mWindowInfos,
- std::bind_front(&InputDispatcher::
- logDispatchStateLocked,
- this),
- targets);
+ mTouchStates
+ .addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
+ targetFlags, pointerIds, motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
+ std::bind_front(&InputDispatcher::
+ logDispatchStateLocked,
+ this),
+ targets);
} else {
targets.emplace_back(connection, targetFlags);
// Since we don't have a window, use the display transform as the raw transform.
@@ -4400,15 +4349,15 @@
}
void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
- ALOGD_IF(debugInboundEventDetails(),
- "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
- ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, action=%s, flags=0x%x, "
- "keyCode=%s, scanCode=0x%x, metaState=0x%x, "
- "downTime=%" PRId64,
- args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
- args.displayId.toString().c_str(), args.policyFlags,
- KeyEvent::actionToString(args.action), args.flags, KeyEvent::getLabel(args.keyCode),
- args.scanCode, args.metaState, args.downTime);
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyKey - id=" << args.id << ", eventTime=" << args.eventTime
+ << ", deviceId=" << args.deviceId
+ << ", source=" << inputEventSourceToString(args.source)
+ << ", displayId=" << args.displayId.toString() << ", policyFlags=0x" << std::hex
+ << args.policyFlags << ", action=" << KeyEvent::actionToString(args.action)
+ << ", flags=0x" << args.flags << ", keyCode=" << KeyEvent::getLabel(args.keyCode)
+ << ", scanCode=0x" << args.scanCode << ", metaState=0x" << args.metaState
+ << ", downTime=" << std::dec << args.downTime;
Result<void> keyCheck = validateKeyEvent(args.action);
if (!keyCheck.ok()) {
LOG(ERROR) << "invalid key event: " << keyCheck.error();
@@ -4618,12 +4567,10 @@
}
void InputDispatcher::notifySensor(const NotifySensorArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
- " sensorType=%s",
- args.id, args.eventTime, args.deviceId, args.source,
- ftl::enum_string(args.sensorType).c_str());
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifySensor - id=" << args.id << " eventTime=" << args.eventTime
+ << ", deviceId=" << args.deviceId << ", source=0x" << std::hex << args.source
+ << std::dec << ", sensorType=" << ftl::enum_string(args.sensorType);
bool needWake = false;
{ // acquire lock
@@ -4645,10 +4592,9 @@
}
void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args.eventTime,
- args.deviceId, args.isOn);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyVibratorState - eventTime=" << args.eventTime << ", device=" << args.deviceId
+ << ", isOn=" << args.isOn;
mPolicy.notifyVibratorState(args.deviceId, args.isOn);
}
@@ -4657,11 +4603,10 @@
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
- "switchMask=0x%08x",
- args.eventTime, args.policyFlags, args.switchValues, args.switchMask);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifySwitch - eventTime=" << args.eventTime << ", policyFlags=0x" << std::hex
+ << args.policyFlags << ", switchValues=0x" << std::setfill('0') << std::setw(8)
+ << args.switchValues << ", switchMask=0x" << std::setw(8) << args.switchMask;
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4670,10 +4615,8 @@
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
// TODO(b/308677868) Remove device reset from the InputListener interface
- if (debugInboundEventDetails()) {
- ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
- args.deviceId);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyDeviceReset - eventTime=" << args.eventTime << ", deviceId=" << args.deviceId;
bool needWake = false;
{ // acquire lock
@@ -4694,10 +4637,9 @@
}
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime,
- args.request.isEnable() ? "true" : "false");
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyPointerCaptureChanged - eventTime=%" << args.eventTime
+ << ", enabled=" << toString(args.request.isEnable());
bool needWake = false;
{ // acquire lock
@@ -4757,12 +4699,10 @@
return InputEventInjectionResult::FAILED;
}
- if (debugInboundEventDetails()) {
- LOG(INFO) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
- << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
- << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
- << ", event=" << *event;
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << __func__ << ": targetUid=" << toString(targetUid, &uidString)
+ << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
+ << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec << ", event=" << *event;
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED;
@@ -4939,9 +4879,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Injecting " << injectedEntries.front()->getDescription();
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "Injecting " << injectedEntries.front()->getDescription();
needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
}
@@ -4967,10 +4905,8 @@
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Timed out waiting for injection result "
- "to become available.");
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Timed out waiting for "
+ "injection result to become available.";
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -4981,16 +4917,14 @@
if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectionState->pendingForegroundDispatches);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Waiting for "
+ << injectionState->pendingForegroundDispatches
+ << " pending foreground dispatches.";
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Timed out waiting for pending foreground "
- "dispatches to finish.");
- }
+ LOG_IF(INFO, DEBUG_INJECTION)
+ << "injectInputEvent - Timed out waiting for pending foreground "
+ "dispatches to finish.";
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -5001,10 +4935,8 @@
}
} // release lock
- if (DEBUG_INJECTION) {
- LOG(INFO) << "injectInputEvent - Finished with result "
- << ftl::enum_string(injectionResult);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Finished with result "
+ << ftl::enum_string(injectionResult);
return injectionResult;
}
@@ -5050,10 +4982,8 @@
}
InjectionState& injectionState = *entry.injectionState;
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
@@ -5137,8 +5067,19 @@
}
// Only look through the requested display.
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) {
- if (windowHandle->getToken() == windowHandleToken) {
+ return findWindowHandleOnDisplay(windowHandleToken, *displayId);
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
+ sp<WindowInfoHandle> windowHandle;
+ for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) {
+ windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId);
+ if (windowHandle != nullptr) {
return windowHandle;
}
}
@@ -5220,13 +5161,38 @@
}
ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
- const android::gui::WindowInfo& windowInfo) const {
+ const android::gui::WindowInfo& windowInfo,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId) const {
+ // TODO(b/383092013): Handle TOPOLOGY_AWARE window flag.
+ // For now, we assume all windows are topology-aware and can handle cross-display streams.
+ if (com::android::input::flags::connected_displays_cursor() && pointerDisplayId.has_value() &&
+ *pointerDisplayId != windowInfo.displayId) {
+ // Sending pointer to a different display than the window. This is a
+ // cross-display drag gesture, so always use the new display's transform.
+ return getDisplayTransform(*pointerDisplayId);
+ }
// If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
- // APIs. If not, fall back to using the DisplayInfo transform of the window's display.
- return (input_flags::use_cloned_screen_coordinates_as_raw() &&
- windowInfo.cloneLayerStackTransform)
- ? *windowInfo.cloneLayerStackTransform
- : getDisplayTransform(windowInfo.displayId);
+ // APIs. If not, fall back to using the DisplayInfo transform of the window's display
+ bool useClonedScreenCoordinates = (input_flags::use_cloned_screen_coordinates_as_raw() &&
+ windowInfo.cloneLayerStackTransform);
+ if (useClonedScreenCoordinates) {
+ return *windowInfo.cloneLayerStackTransform;
+ }
+ return getDisplayTransform(windowInfo.displayId);
+}
+
+ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId(
+ ui::LogicalDisplayId displayId) const {
+ if (mTopology.graph.contains(displayId)) {
+ return mTopology.primaryDisplayId;
+ }
+ return displayId;
+}
+
+bool InputDispatcher::DispatcherWindowInfo::areDisplaysConnected(
+ ui::LogicalDisplayId display1, ui::LogicalDisplayId display2) const {
+ return display1 == display2 ||
+ (mTopology.graph.contains(display1) && mTopology.graph.contains(display2));
}
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
@@ -5259,10 +5225,32 @@
return dump;
}
+std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays(
+ ui::LogicalDisplayId displayId) const {
+ if (!mTopology.graph.contains(displayId)) {
+ return {displayId};
+ }
+
+ std::vector<ui::LogicalDisplayId> connectedDisplays;
+ for (auto it : mTopology.graph) {
+ connectedDisplays.push_back(it.first);
+ }
+ return connectedDisplays;
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+ for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion(
const sp<android::gui::WindowInfoHandle>& window,
- const android::inputdispatcher::MotionEntry& motionEntry,
- const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos) const {
+ const android::inputdispatcher::MotionEntry& motionEntry) const {
const WindowInfo& info = *window->getInfo();
// Skip spy window targets that are not valid for targeted injection.
@@ -5281,7 +5269,7 @@
return false;
}
- std::shared_ptr<Connection> connection = connections.getConnection(window->getToken());
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken());
if (connection == nullptr) {
ALOGW("Not sending touch to %s because there's no corresponding connection",
window->getName().c_str());
@@ -5296,8 +5284,8 @@
// Drop events that can't be trusted due to occlusion
const auto [x, y] = resolveTouchedPosition(motionEntry);
DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
- windowInfos.computeTouchOcclusionInfo(window, x, y);
- if (!windowInfos.isTouchTrusted(occlusionInfo)) {
+ mWindowInfos.computeTouchOcclusionInfo(window, x, y);
+ if (!mWindowInfos.isTouchTrusted(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
for (const auto& log : occlusionInfo.debugInfo) {
@@ -5310,7 +5298,7 @@
}
// Drop touch events if requested by input feature
- if (shouldDropInput(motionEntry, window, windowInfos)) {
+ if (shouldDropInput(motionEntry, window, mWindowInfos)) {
return false;
}
@@ -5446,7 +5434,7 @@
CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
"WindowInfo changed", traceContext.getTracker());
const std::list<DispatcherTouchState::CancellationArgs> cancellations =
- mTouchStates.updateFromWindowInfo(displayId, mWindowInfos);
+ mTouchStates.updateFromWindowInfo(displayId);
for (const auto& cancellationArgs : cancellations) {
switch (cancellationArgs.mode) {
case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
@@ -5480,35 +5468,32 @@
// which might not happen until the next GC.
for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
if (!mWindowInfos.isWindowPresent(oldWindowHandle)) {
- if (DEBUG_FOCUS) {
- ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Window went away: " << oldWindowHandle->getName();
oldWindowHandle->releaseChannel();
}
}
}
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
-InputDispatcher::DispatcherTouchState::updateFromWindowInfo(
- ui::LogicalDisplayId displayId, const DispatcherWindowInfo& windowInfos) {
+InputDispatcher::DispatcherTouchState::updateFromWindowInfo(ui::LogicalDisplayId displayId) {
std::list<CancellationArgs> cancellations;
- if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- TouchState& state = it->second;
- cancellations = eraseRemovedWindowsFromWindowInfo(state, displayId, windowInfos);
+ forTouchAndCursorStatesOnDisplay(displayId, [&](TouchState& state) {
cancellations.splice(cancellations.end(),
- updateHoveringStateFromWindowInfo(state, displayId, windowInfos));
- }
+ eraseRemovedWindowsFromWindowInfo(state, displayId));
+ cancellations.splice(cancellations.end(),
+ updateHoveringStateFromWindowInfo(state, displayId));
+ return false;
+ });
return cancellations;
}
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos) {
+ TouchState& state, ui::LogicalDisplayId displayId) {
std::list<CancellationArgs> cancellations;
for (auto it = state.windows.begin(); it != state.windows.end();) {
TouchedWindow& touchedWindow = *it;
- if (windowInfos.isWindowPresent(touchedWindow.windowHandle)) {
+ if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
it++;
continue;
}
@@ -5534,12 +5519,11 @@
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos) {
+ TouchState& state, ui::LogicalDisplayId displayId) {
std::list<CancellationArgs> cancellations;
// Check if the hovering should stop because the window is no longer eligible to receive it
// (for example, if the touchable region changed)
- ui::Transform displayTransform = windowInfos.getDisplayTransform(displayId);
+ ui::Transform displayTransform = mWindowInfos.getDisplayTransform(displayId);
for (TouchedWindow& touchedWindow : state.windows) {
std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
[&](const PointerProperties& properties, float x, float y) {
@@ -5561,10 +5545,9 @@
void InputDispatcher::setFocusedApplication(
ui::LogicalDisplayId displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
- if (DEBUG_FOCUS) {
- ALOGD("setFocusedApplication displayId=%s %s", displayId.toString().c_str(),
- inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setFocusedApplication displayId=" << displayId.toString() << " "
+ << (inputApplicationHandle ? inputApplicationHandle->getName()
+ : "<nullptr>");
{ // acquire lock
std::scoped_lock _l(mLock);
setFocusedApplicationLocked(displayId, inputApplicationHandle);
@@ -5614,9 +5597,7 @@
* display. The display-specified events won't be affected.
*/
void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) {
- if (DEBUG_FOCUS) {
- ALOGD("setFocusedDisplay displayId=%s", displayId.toString().c_str());
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setFocusedDisplay displayId=" << displayId.toString();
{ // acquire lock
std::scoped_lock _l(mLock);
ScopedSyntheticEventTracer traceContext(mTracer);
@@ -5670,9 +5651,8 @@
}
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
- if (DEBUG_FOCUS) {
- ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setInputDispatchMode: enabled=" << enabled
+ << ", frozen=" << frozen;
bool changed;
{ // acquire lock
@@ -5702,9 +5682,7 @@
}
void InputDispatcher::setInputFilterEnabled(bool enabled) {
- if (DEBUG_FOCUS) {
- ALOGD("setInputFilterEnabled: enabled=%d", enabled);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setInputFilterEnabled: enabled=" << enabled;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -5726,14 +5704,16 @@
bool needWake = false;
{
std::scoped_lock lock(mLock);
- ALOGD_IF(DEBUG_TOUCH_MODE,
- "Request to change touch mode to %s (calling pid=%s, uid=%s, "
- "hasPermission=%s, target displayId=%s, mTouchModePerDisplay[displayId]=%s)",
- toString(inTouchMode), pid.toString().c_str(), uid.toString().c_str(),
- toString(hasPermission), displayId.toString().c_str(),
- mTouchModePerDisplay.count(displayId) == 0
- ? "not set"
- : std::to_string(mTouchModePerDisplay[displayId]).c_str());
+ LOG_IF(INFO, DEBUG_TOUCH_MODE)
+ << "Request to change touch mode to " << toString(inTouchMode)
+ << " (calling pid=" << pid.toString() << ", uid=" << uid.toString()
+ << ", hasPermission=" << toString(hasPermission)
+ << ", target displayId=" << displayId.toString()
+ << ", mTouchModePerDisplay[displayId]="
+ << (mTouchModePerDisplay.count(displayId) == 0
+ ? "not set"
+ : std::to_string(mTouchModePerDisplay[displayId]))
+ << ")";
auto touchModeIt = mTouchModePerDisplay.find(displayId);
if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
@@ -5786,9 +5766,7 @@
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) {
if (fromToken == toToken) {
- if (DEBUG_FOCUS) {
- ALOGD("Trivial transfer to same window.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Trivial transfer to same window.";
return true;
}
@@ -5800,13 +5778,12 @@
"transferring touch from this window to another window",
traceContext.getTracker());
- auto result = mTouchStates.transferTouchGesture(fromToken, toToken, mWindowInfos,
- mConnectionManager);
+ auto result = mTouchStates.transferTouchGesture(fromToken, toToken);
if (!result.has_value()) {
return false;
}
- const auto [toWindowHandle, deviceId, pointers, cancellations, pointerDowns] =
+ const auto& [toWindowHandle, deviceId, pointers, cancellations, pointerDowns] =
result.value();
for (const auto& cancellationArgs : cancellations) {
@@ -5845,9 +5822,7 @@
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>>
InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken,
- const sp<android::IBinder>& toToken,
- const DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections) {
+ const sp<android::IBinder>& toToken) {
// Find the target touch state and touched window by fromToken.
auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken);
if (!touchStateWindowAndDisplay.has_value()) {
@@ -5865,16 +5840,17 @@
const DeviceId deviceId = *deviceIds.begin();
const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle;
- const sp<WindowInfoHandle> toWindowHandle = windowInfos.findWindowHandle(toToken, displayId);
+ // TouchState displayId may not be same as window displayId, we need to lookup for toToken on
+ // all connected displays.
+ const sp<WindowInfoHandle> toWindowHandle =
+ mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId);
if (!toWindowHandle) {
ALOGW("Cannot transfer touch because the transfer target window was not found.");
return std::nullopt;
}
- if (DEBUG_FOCUS) {
- ALOGD("%s: fromWindowHandle=%s, toWindowHandle=%s", __func__,
- fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << __func__ << ": fromWindowHandle=" << fromWindowHandle->getName()
+ << ", toWindowHandle=" << toWindowHandle->getName();
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow.targetFlags;
@@ -5893,8 +5869,8 @@
deviceId, pointers, downTimeInTarget);
// Synthesize cancel for old window and down for new window.
- std::shared_ptr<Connection> fromConnection = connections.getConnection(fromToken);
- std::shared_ptr<Connection> toConnection = connections.getConnection(toToken);
+ std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken);
+ std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken);
std::list<CancellationArgs> cancellations;
std::list<PointerDownArgs> pointerDowns;
if (fromConnection != nullptr && toConnection != nullptr) {
@@ -5905,7 +5881,7 @@
// Check if the wallpaper window should deliver the corresponding event.
auto [wallpaperCancellations, wallpaperPointerDowns] =
transferWallpaperTouch(fromWindowHandle, toWindowHandle, state, deviceId, pointers,
- oldTargetFlags, newTargetFlags, windowInfos, connections);
+ oldTargetFlags, newTargetFlags);
cancellations.splice(cancellations.end(), wallpaperCancellations);
pointerDowns.splice(pointerDowns.end(), wallpaperPointerDowns);
@@ -5927,26 +5903,25 @@
*/
sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow(
ui::LogicalDisplayId displayId) const {
- const auto stateIt = mTouchStatesByDisplay.find(displayId);
- if (stateIt == mTouchStatesByDisplay.end()) {
- ALOGI("No touch state on display %s", displayId.toString().c_str());
- return nullptr;
- }
-
- const TouchState& state = stateIt->second;
sp<WindowInfoHandle> touchedForegroundWindow;
- // If multiple foreground windows are touched, return nullptr
- for (const TouchedWindow& window : state.windows) {
- if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
- if (touchedForegroundWindow != nullptr) {
- ALOGI("Two or more foreground windows: %s and %s",
- touchedForegroundWindow->getName().c_str(),
- window.windowHandle->getName().c_str());
- return nullptr;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ // If multiple foreground windows are touched, return nullptr
+ for (const TouchedWindow& window : state.windows) {
+ if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
+ if (touchedForegroundWindow != nullptr) {
+ ALOGI("Two or more foreground windows: %s and %s",
+ touchedForegroundWindow->getName().c_str(),
+ window.windowHandle->getName().c_str());
+ touchedForegroundWindow = nullptr;
+ return true;
+ }
+ touchedForegroundWindow = window.windowHandle;
}
- touchedForegroundWindow = window.windowHandle;
}
- }
+ return false;
+ });
+ ALOGI_IF(touchedForegroundWindow == nullptr,
+ "No touch state or no touched foreground window on display %d", displayId.val());
return touchedForegroundWindow;
}
@@ -5977,9 +5952,7 @@
}
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
- if (DEBUG_FOCUS) {
- ALOGD("Resetting and dropping all events (%s).", reason);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Resetting and dropping all events (" << reason << ").";
ScopedSyntheticEventTracer traceContext(mTracer);
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason,
@@ -6134,9 +6107,7 @@
};
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
- if (DEBUG_CHANNEL_CREATION) {
- ALOGD("channel '%s' ~ createInputChannel", name.c_str());
- }
+ LOG_IF(INFO, DEBUG_CHANNEL_CREATION) << "channel '" << name << "' ~ createInputChannel";
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
@@ -6252,14 +6223,14 @@
return BAD_VALUE;
}
- ScopedSyntheticEventTracer traceContext(mTracer);
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "input channel stole pointer stream", traceContext.getTracker());
const auto result = mTouchStates.pilferPointers(token, *requestingConnection);
if (!result.ok()) {
return result.error().code();
}
+ ScopedSyntheticEventTracer traceContext(mTracer);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "input channel stole pointer stream", traceContext.getTracker());
const auto cancellations = *result;
for (const auto& cancellationArgs : cancellations) {
LOG_ALWAYS_FATAL_IF(cancellationArgs.mode !=
@@ -6687,12 +6658,11 @@
// then cancel the associated fallback key, if any.
if (fallbackKeyCode) {
// 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);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Asking policy to cancel fallback action. keyCode="
+ << keyEntry.keyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
KeyEvent event = createKeyEvent(keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
@@ -6729,21 +6699,22 @@
// Then ask the policy what to do with it.
bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
if (!fallbackKeyCode && !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);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Skipping unhandled key event processing since this is "
+ "not an initial down. keyCode="
+ << originalKeyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
return {};
}
// 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);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Asking policy to perform fallback action. keyCode="
+ << keyEntry.keyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
+ ;
KeyEvent event = createKeyEvent(keyEntry);
mLock.unlock();
@@ -6840,16 +6811,13 @@
newEntry->traceTracker =
mTracer->traceDerivedEvent(*newEntry, *keyEntry.traceTracker);
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Dispatching fallback key. originalKeyCode="
+ << originalKeyCode << ", fallbackKeyCode=" << *fallbackKeyCode
+ << ", fallbackMetaState=0x" << std::hex << keyEntry.metaState;
return newEntry;
} else {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: No fallback key.");
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << "Unhandled key event: No fallback key.";
// Report the key as unhandled, since there is no fallback key.
mReporter->reportUnhandledKey(keyEntry.id);
@@ -6990,7 +6958,7 @@
return;
}
- ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
+ LOG_IF(INFO, DEBUG_FOCUS) << "Disabling Pointer Capture because the window lost focus.";
if (mCurrentPointerCaptureRequest.isEnable()) {
setPointerCaptureLocked(nullptr);
@@ -7132,8 +7100,7 @@
void InputDispatcher::DispatcherTouchState::slipWallpaperTouch(
ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle, TouchState& state, const MotionEntry& entry,
- std::vector<InputTarget>& targets, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+ std::vector<InputTarget>& targets, std::function<void()> dump) {
LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
const DeviceId deviceId = entry.deviceId;
const PointerProperties& pointerProperties = entry.pointerProperties[0];
@@ -7146,19 +7113,17 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? windowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
return;
}
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- DispatcherTouchState::addPointerWindowTarget(oldWallpaper,
- InputTarget::DispatchMode::SLIPPERY_EXIT,
- oldTouchedWindow.targetFlags,
- getPointerIds(pointers),
- oldTouchedWindow.getDownTimeInTarget(deviceId),
- connections, windowInfos, dump, targets);
+ addPointerWindowTarget(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
+ oldTouchedWindow.targetFlags, getPointerIds(pointers),
+ oldTouchedWindow.getDownTimeInTarget(deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
}
@@ -7177,8 +7142,7 @@
const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
android::DeviceId deviceId, const std::vector<PointerProperties>& pointers,
ftl::Flags<InputTarget::Flags> oldTargetFlags,
- ftl::Flags<InputTarget::Flags> newTargetFlags, const DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections) {
+ ftl::Flags<InputTarget::Flags> newTargetFlags) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7189,7 +7153,7 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? windowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
return {};
}
@@ -7210,10 +7174,10 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
deviceId, pointers, downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
- connections.getConnection(newWallpaper->getToken());
+ mConnectionManager.getConnection(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
std::shared_ptr<Connection> toConnection =
- connections.getConnection(toWindowHandle->getToken());
+ mConnectionManager.getConnection(toWindowHandle->getToken());
toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags);
}
@@ -7267,6 +7231,12 @@
}
}
+void InputDispatcher::setDisplayTopology(
+ const android::DisplayTopologyGraph& displayTopologyGraph) {
+ std::scoped_lock _l(mLock);
+ mWindowInfos.setDisplayTopology(displayTopologyGraph);
+}
+
InputDispatcher::ConnectionManager::ConnectionManager(const sp<android::Looper>& looper)
: mLooper(looper) {}
@@ -7402,9 +7372,17 @@
mMaximumObscuringOpacityForTouch = opacity;
}
+void InputDispatcher::DispatcherWindowInfo::setDisplayTopology(
+ const DisplayTopologyGraph& displayTopologyGraph) {
+ mTopology = displayTopologyGraph;
+}
+
+InputDispatcher::DispatcherTouchState::DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+ const ConnectionManager& connections)
+ : mWindowInfos(windowInfos), mConnectionManager(connections) {}
+
ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags(
- const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit,
- const DispatcherWindowInfo& windowInfos) {
+ const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit) {
ftl::Flags<InputTarget::Flags> targetFlags;
if (canReceiveForegroundTouches(*targetWindow->getInfo())) {
// There should only be one touched window that can be "foreground" for the pointer.
@@ -7413,9 +7391,9 @@
if (isSplit) {
targetFlags |= InputTarget::Flags::SPLIT;
}
- if (windowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
+ if (mWindowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
- } else if (windowInfos.isWindowObscured(targetWindow)) {
+ } else if (mWindowInfos.isWindowObscured(targetWindow)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
return targetFlags;
@@ -7423,101 +7401,212 @@
bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers(
ui::LogicalDisplayId displayId, int32_t deviceId) const {
- const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return false;
- }
- return touchStateIt->second.hasTouchingPointers(deviceId) ||
- touchStateIt->second.hasHoveringPointers(deviceId);
+ bool hasTouchingOrHoveringPointers = false;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ hasTouchingOrHoveringPointers =
+ state.hasTouchingPointers(deviceId) || state.hasHoveringPointers(deviceId);
+ return hasTouchingOrHoveringPointers;
+ });
+ return hasTouchingOrHoveringPointers;
}
bool InputDispatcher::DispatcherTouchState::isPointerInWindow(const sp<android::IBinder>& token,
ui::LogicalDisplayId displayId,
android::DeviceId deviceId,
int32_t pointerId) const {
- const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return false;
- }
- for (const TouchedWindow& window : touchStateIt->second.windows) {
- if (window.windowHandle->getToken() == token &&
- (window.hasTouchingPointer(deviceId, pointerId) ||
- window.hasHoveringPointer(deviceId, pointerId))) {
- return true;
- }
- }
- return false;
-}
-
-std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
-InputDispatcher::DispatcherTouchState::findTouchedWindowHandleAndDisplay(
- const sp<android::IBinder>& token) const {
- for (const auto& [displayId, state] : mTouchStatesByDisplay) {
- for (const TouchedWindow& w : state.windows) {
- if (w.windowHandle->getToken() == token) {
- return std::make_tuple(std::ref(w.windowHandle), displayId);
+ bool isPointerInWindow = false;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ for (const TouchedWindow& window : state.windows) {
+ if (window.windowHandle->getToken() == token &&
+ (window.hasTouchingPointer(deviceId, pointerId) ||
+ window.hasHoveringPointer(deviceId, pointerId))) {
+ isPointerInWindow = true;
+ return true;
}
}
- }
- return std::nullopt;
+ return false;
+ });
+ return isPointerInWindow;
+}
+
+std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+InputDispatcher::DispatcherTouchState::findExistingTouchedWindowHandleAndDisplay(
+ const sp<android::IBinder>& token) const {
+ std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
+ touchedWindowHandleAndDisplay;
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
+ for (const TouchedWindow& w : state.windows) {
+ if (w.windowHandle->getToken() == token) {
+ touchedWindowHandleAndDisplay.emplace(std::ref(w.windowHandle), displayId);
+ return true;
+ }
+ }
+ return false;
+ });
+ LOG_ALWAYS_FATAL_IF(!touchedWindowHandleAndDisplay.has_value(),
+ "%s : Touch state is out of sync: No touched window for token", __func__);
+ return touchedWindowHandleAndDisplay.value();
}
void InputDispatcher::DispatcherTouchState::forAllTouchedWindows(
std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
- for (const auto& [_, state] : mTouchStatesByDisplay) {
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
for (const TouchedWindow& window : state.windows) {
f(window.windowHandle);
}
- }
+ return false;
+ });
}
void InputDispatcher::DispatcherTouchState::forAllTouchedWindowsOnDisplay(
ui::LogicalDisplayId displayId,
std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
- const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return;
- }
- for (const TouchedWindow& window : touchStateIt->second.windows) {
- f(window.windowHandle);
- }
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ for (const TouchedWindow& window : state.windows) {
+ f(window.windowHandle);
+ }
+ return false;
+ });
}
std::string InputDispatcher::DispatcherTouchState::dump() const {
std::string dump;
- if (!mTouchStatesByDisplay.empty()) {
- dump += StringPrintf("TouchStatesByDisplay:\n");
+ if (mTouchStatesByDisplay.empty()) {
+ dump += "TouchStatesByDisplay: <no displays touched>\n";
+ } else {
+ dump += "TouchStatesByDisplay:\n";
for (const auto& [displayId, state] : mTouchStatesByDisplay) {
std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
dump += INDENT + displayId.toString() + " : " + touchStateDump;
}
+ }
+ if (mCursorStateByDisplay.empty()) {
+ dump += "CursorStatesByDisplay: <no displays touched by cursor>\n";
} else {
- dump += "TouchStates: <no displays touched>\n";
+ dump += "CursorStatesByDisplay:\n";
+ for (const auto& [displayId, state] : mCursorStateByDisplay) {
+ std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
+ dump += INDENT + displayId.toString() + " : " + touchStateDump;
+ }
}
return dump;
}
void InputDispatcher::DispatcherTouchState::removeAllPointersForDevice(android::DeviceId deviceId) {
- for (auto& [_, touchState] : mTouchStatesByDisplay) {
- touchState.removeAllPointersForDevice(deviceId);
- }
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
+ state.removeAllPointersForDevice(deviceId);
+ return false;
+ });
}
void InputDispatcher::DispatcherTouchState::clear() {
mTouchStatesByDisplay.clear();
+ mCursorStateByDisplay.clear();
+}
+
+void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry,
+ android::inputdispatcher::TouchState&& touchState) {
+ if (touchState.windows.empty()) {
+ eraseTouchStateForMotionEntry(entry);
+ return;
+ }
+
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay[mWindowInfos.getPrimaryDisplayId(entry.displayId)] =
+ std::move(touchState);
+ } else {
+ mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay.erase(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+ } else {
+ mTouchStatesByDisplay.erase(entry.displayId);
+ }
+}
+
+const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) const {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ auto touchStateIt =
+ mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+ if (touchStateIt != mCursorStateByDisplay.end()) {
+ return &touchStateIt->second;
+ }
+ } else {
+ auto touchStateIt = mTouchStatesByDisplay.find(entry.displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end()) {
+ return &touchStateIt->second;
+ }
+ }
+ return nullptr;
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+ ui::LogicalDisplayId displayId, std::function<bool(const TouchState&)> f) const {
+ const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end() && f(touchStateIt->second)) {
+ return;
+ }
+
+ // DisplayId for the Cursor state may not be same as supplied displayId if display is part of
+ // topology. Instead we should to check from the topology's primary display.
+ const auto cursorStateIt =
+ mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(displayId));
+ if (cursorStateIt != mCursorStateByDisplay.end()) {
+ f(cursorStateIt->second);
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+ ui::LogicalDisplayId displayId, std::function<bool(TouchState&)> f) {
+ const_cast<const DispatcherTouchState&>(*this)
+ .forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ return f(const_cast<TouchState&>(state));
+ });
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const {
+ for (auto& [displayId, state] : mTouchStatesByDisplay) {
+ if (f(displayId, state)) {
+ return;
+ }
+ }
+ for (auto& [displayId, state] : mCursorStateByDisplay) {
+ if (f(displayId, state)) {
+ return;
+ }
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, TouchState&)> f) {
+ const_cast<const DispatcherTouchState&>(*this).forAllTouchAndCursorStates(
+ [&](ui::LogicalDisplayId displayId, const TouchState& constState) {
+ return f(displayId, const_cast<TouchState&>(constState));
+ });
}
std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
InputDispatcher::DispatcherTouchState::findTouchStateWindowAndDisplay(
const sp<android::IBinder>& token) {
- for (auto& [displayId, state] : mTouchStatesByDisplay) {
+ std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
+ touchStateWindowAndDisplay;
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
for (TouchedWindow& w : state.windows) {
if (w.windowHandle->getToken() == token) {
- return std::make_tuple(std::ref(state), std::ref(w), displayId);
+ touchStateWindowAndDisplay.emplace(std::ref(state), std::ref(w), displayId);
+ return true;
}
}
- }
- return std::nullopt;
+ return false;
+ });
+ return touchStateWindowAndDisplay;
}
bool InputDispatcher::DispatcherTouchState::isStylusActiveInDisplay(
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 52657d4..38f7825 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -164,6 +164,8 @@
void setInputMethodConnectionIsActive(bool isActive) override;
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) override;
+
private:
enum class DropReason {
NOT_DROPPED,
@@ -291,6 +293,8 @@
void setMaximumObscuringOpacityForTouch(float opacity);
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+
// Get a reference to window handles by display, return an empty vector if not found.
const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
ui::LogicalDisplayId displayId) const;
@@ -303,8 +307,11 @@
// Get the transform for display, returns Identity-transform if display is missing.
ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
- // Get the raw transform to use for motion events going to the given window.
- ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+ // Get the raw transform to use for motion events going to the given window. Optionally a
+ // pointer displayId may be supplied if pointer is on a different display from the window.
+ ui::Transform getRawTransform(
+ const android::gui::WindowInfo& windowInfo,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId = std::nullopt) const;
// Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
// display-id is not provided lookup is done for all displays.
@@ -312,6 +319,11 @@
const sp<IBinder>& windowHandleToken,
std::optional<ui::LogicalDisplayId> displayId = {}) const;
+ // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected
+ // displays in the topology of the queried display.
+ sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Returns the touched window at the given location, excluding the ignoreWindow if provided.
@@ -332,15 +344,33 @@
bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const;
+ // Returns topology's primary display if the display belongs to it, otherwise the
+ // same displayId.
+ ui::LogicalDisplayId getPrimaryDisplayId(ui::LogicalDisplayId displayId) const;
+
+ bool areDisplaysConnected(ui::LogicalDisplayId display1,
+ ui::LogicalDisplayId display2) const;
+
std::string dumpDisplayAndWindowInfo() const;
private:
+ std::vector<ui::LogicalDisplayId> getConnectedDisplays(
+ ui::LogicalDisplayId displayId) const;
+
+ sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
std::unordered_map<ui::LogicalDisplayId /*displayId*/,
std::vector<sp<android::gui::WindowInfoHandle>>>
mWindowHandlesByDisplay;
std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
mDisplayInfos;
float mMaximumObscuringOpacityForTouch{1.0f};
+
+ // Topology is initialized with default-constructed value, which is an empty topology until
+ // we receive setDisplayTopology call. Meanwhile we will treat every display as an
+ // independent display.
+ DisplayTopologyGraph mTopology;
};
DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
@@ -361,20 +391,20 @@
const ftl::Flags<InputTarget::Flags> targetFlags;
};
- static void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode,
- ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
- std::optional<nsecs_t> firstDownTimeInTarget,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump,
- std::vector<InputTarget>& inputTargets);
+ DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+ const ConnectionManager& connections);
+
+ void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId,
+ std::function<void()> dump,
+ std::vector<InputTarget>& inputTargets);
base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
findTouchedWindowTargets(nsecs_t currentTime, const MotionEntry& entry,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
const sp<android::gui::WindowInfoHandle> dragWindow,
std::function<void(const MotionEntry&)> addDragEvent,
std::function<void()> dump);
@@ -387,9 +417,9 @@
bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId,
DeviceId deviceId, int32_t pointerId) const;
- // Find touched windowHandle and display by token.
- std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
- findTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const;
+ // Find an existing touched windowHandle and display by token.
+ std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+ findExistingTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const;
void forAllTouchedWindows(std::function<void(const sp<gui::WindowInfoHandle>&)> f) const;
@@ -401,8 +431,7 @@
// Updates the touchState for display from WindowInfo,
// returns list of CancellationArgs for every cancelled touch
- std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId);
void removeAllPointersForDevice(DeviceId deviceId);
@@ -411,9 +440,7 @@
std::optional<
std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>,
std::list<CancellationArgs>, std::list<PointerDownArgs>>>
- transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- const DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections);
+ transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
base::Result<std::list<CancellationArgs>, status_t> pilferPointers(
const sp<IBinder>& token, const Connection& requestingConnection);
@@ -423,6 +450,29 @@
private:
std::unordered_map<ui::LogicalDisplayId, TouchState> mTouchStatesByDisplay;
+ // As there can be only one CursorState per topology group, we will treat all displays in
+ // the topology as one connected display-group. These will be identified by
+ // DisplayTopologyGraph::primaryDisplayId.
+ // Cursor on the any of the displays that are not part of the topology will be identified by
+ // the displayId similar to mTouchStatesByDisplay.
+ std::unordered_map<ui::LogicalDisplayId, TouchState> mCursorStateByDisplay;
+
+ // The supplied lambda is invoked for each touch and cursor state of the display.
+ // The function iterates until the lambda returns true, effectively performing a 'break'
+ // from the iteration.
+ void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+ std::function<bool(const TouchState&)> f) const;
+
+ void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+ std::function<bool(TouchState&)> f);
+
+ // The supplied lambda is invoked for each touchState. The function iterates until
+ // the lambda returns true, effectively performing a 'break' from the iteration.
+ void forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const;
+
+ void forAllTouchAndCursorStates(std::function<bool(ui::LogicalDisplayId, TouchState&)> f);
+
std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
findTouchStateWindowAndDisplay(const sp<IBinder>& token);
@@ -431,30 +481,31 @@
const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
DeviceId deviceId, const std::vector<PointerProperties>& pointers,
ftl::Flags<InputTarget::Flags> oldTargetFlags,
- ftl::Flags<InputTarget::Flags> newTargetFlags,
- const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections);
+ ftl::Flags<InputTarget::Flags> newTargetFlags);
- bool canWindowReceiveMotion(const sp<android::gui::WindowInfoHandle>& window,
- const MotionEntry& motionEntry,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos) const;
+ void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState);
+
+ void eraseTouchStateForMotionEntry(const MotionEntry& entry);
+
+ const TouchState* getTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) const;
+
+ bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window,
+ const MotionEntry& motionEntry) const;
// Return true if stylus is currently down anywhere on the specified display,
// and false otherwise.
bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId) const;
- static std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
- static std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
- static std::vector<InputTarget> findOutsideTargets(
- ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
- int32_t pointerId, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump);
+ std::vector<InputTarget> findOutsideTargets(ui::LogicalDisplayId displayId,
+ const sp<gui::WindowInfoHandle>& touchedWindow,
+ int32_t pointerId, std::function<void()> dump);
/**
* Slip the wallpaper touch if necessary.
@@ -469,18 +520,18 @@
* @param targets the current targets to add the walpaper ones to
* @param eventTime the new downTime for the wallpaper target
*/
- static void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
- const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
- const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, const MotionEntry& entry,
- std::vector<InputTarget>& targets,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump);
+ void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
+ const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+ const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+ TouchState& state, const MotionEntry& entry,
+ std::vector<InputTarget>& targets, std::function<void()> dump);
- static ftl::Flags<InputTarget::Flags> getTargetFlags(
+ ftl::Flags<InputTarget::Flags> getTargetFlags(
const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition,
- bool isSplit, const DispatcherWindowInfo& windowInfos);
+ bool isSplit);
+
+ const DispatcherWindowInfo& mWindowInfos;
+ const ConnectionManager& mConnectionManager;
};
DispatcherTouchState mTouchStates GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 9b5a79b..782a54f 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -15,7 +15,9 @@
*/
#include "DebugConfig.h"
+#include "input/Input.h"
#include "input/InputDevice.h"
+#include "input/InputFlags.h"
#include "InputState.h"
@@ -221,10 +223,15 @@
}
ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
+ // If we have connected displays a mouse can move between displays and displayId may change
+ // while a gesture is in-progress.
+ const bool skipDisplayCheck =
+ InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source);
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) {
+ memento.hovering == hovering &&
+ (skipDisplayCheck || memento.displayId == entry.displayId)) {
return i;
}
}
@@ -338,7 +345,10 @@
// would receive different events from each display. Since the TouchStates are per-display,
// it's unlikely that those two streams would be consistent with each other. Therefore,
// cancel the previous gesture if the display id changes.
- if (motionEntry.displayId != lastMemento.displayId) {
+ // Except when we have connected-displays where a mouse may move across display boundaries.
+ const bool skipDisplayCheck = (InputFlags::connectedDisplaysCursorEnabled() &&
+ isMouseOrTouchpad(motionEntry.source));
+ if (!skipDisplayCheck && motionEntry.displayId != lastMemento.displayId) {
LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
<< " and new event is " << motionEntry;
return true;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 463a952..ab039c3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -24,6 +24,7 @@
#include <android/os/InputEventInjectionSync.h>
#include <gui/InputApplication.h>
#include <gui/WindowInfo.h>
+#include <input/DisplayTopologyGraph.h>
#include <input/InputDevice.h>
#include <input/InputTransport.h>
#include <unordered_map>
@@ -243,6 +244,11 @@
* Notify the dispatcher that the state of the input method connection changed.
*/
virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
+
+ /*
+ * Notify the dispatcher of the latest DisplayTopology.
+ */
+ virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
};
} // namespace android
diff --git a/services/inputflinger/docs/device_configuration.md b/services/inputflinger/docs/device_configuration.md
new file mode 100644
index 0000000..0b75eb2
--- /dev/null
+++ b/services/inputflinger/docs/device_configuration.md
@@ -0,0 +1,10 @@
+# Input Device Configuration
+
+There are a number of properties that can be specified for an input device.
+
+|Property|Value|
+|---|----|
+|`audio.mic`|A boolean (`0` or `1`) that indicates whether the device has a microphone.|
+|`device.additionalSysfsLedsNode`|A string representing the path to search for device lights to be used in addition to searching the device node itself for lights.|
+|`device.internal`|A boolean (`0` or `1`) that indicates if this input device is part of the device as opposed to be externally attached.|
+|`device.type`|A string representing if the device is of a certain type. Valid values include `rotaryEncoder` and `externalStylus`.
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 36614b2..c805b74 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -61,6 +61,16 @@
/* Notifies that mouse cursor faded due to typing. */
virtual void notifyMouseCursorFadedOnTyping() = 0;
+
+ /**
+ * Give accessibility a chance to filter motion event by pointer devices.
+ * The return values denotes the delta x and y after filtering it.
+ *
+ * This call happens on the input hot path and it is extremely performance sensitive.
+ * This also must not call back into native code.
+ */
+ virtual std::optional<vec2> filterPointerMotionForAccessibility(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3934e78..dc7f7c1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -66,9 +66,10 @@
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/gestures/GestureConverter.cpp",
- "mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/GesturesLogcatAdapter.cpp",
"mapper/gestures/HardwareProperties.cpp",
"mapper/gestures/HardwareStateConverter.cpp",
+ "mapper/gestures/Logging.cpp",
"mapper/gestures/PropertyProvider.cpp",
"mapper/gestures/TimerProvider.cpp",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 3c8b6f5..2fcb5d8 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -351,6 +351,22 @@
return colors;
}
+static base::Result<std::shared_ptr<PropertyMap>> loadConfiguration(
+ const InputDeviceIdentifier& ident) {
+ std::string configurationFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(ident,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
+ if (configurationFile.empty()) {
+ ALOGD("No input device configuration file found for device '%s'.", ident.name.c_str());
+ return base::Result<std::shared_ptr<PropertyMap>>(nullptr);
+ }
+ base::Result<std::shared_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(configurationFile.c_str());
+
+ return propertyMap;
+}
+
/**
* Read country code information exposed through the sysfs path and convert it to Layout info.
*/
@@ -409,11 +425,22 @@
* Read information about lights exposed through the sysfs path.
*/
static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration(
- const std::filesystem::path& sysfsRootPath) {
+ const std::filesystem::path& sysfsRootPath, const std::shared_ptr<PropertyMap>& config) {
std::unordered_map<int32_t, RawLightInfo> lightInfos;
int32_t nextLightId = 0;
- // Check if device has any lights.
- const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+ // Check if device has any lights. If the Input Device Configuration file specifies any lights,
+ // use those in addition to searching the device node itself for lights.
+ std::vector<std::filesystem::path> paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+
+ if (config) {
+ auto additionalLights = config->getString("device.additionalSysfsLedsNode");
+ if (additionalLights) {
+ ALOGI("IDC specifies additional path for lights at '%s'",
+ additionalLights.value().c_str());
+ paths.push_back(std::filesystem::path(additionalLights.value()));
+ }
+ }
+
for (const auto& nodePath : paths) {
RawLightInfo info;
info.id = ++nextLightId;
@@ -532,17 +559,16 @@
// --- EventHub::Device ---
EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
- std::shared_ptr<const AssociatedDevice> assocDev)
+ std::shared_ptr<PropertyMap> config)
: fd(fd),
id(id),
path(std::move(path)),
identifier(std::move(identifier)),
classes(0),
- configuration(nullptr),
+ configuration(std::move(config)),
virtualKeyMap(nullptr),
ffEffectPlaying(false),
ffEffectId(-1),
- associatedDevice(std::move(assocDev)),
controllerNumber(0),
enabled(true),
isVirtual(fd < 0),
@@ -696,26 +722,6 @@
return false;
}
-void EventHub::Device::loadConfigurationLocked() {
- configurationFile =
- getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
- InputDeviceConfigurationFileType::
- CONFIGURATION);
- if (configurationFile.empty()) {
- ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
- } else {
- android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
- PropertyMap::load(configurationFile.c_str());
- if (!propertyMap.ok()) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- identifier.name.c_str());
- } else {
- configuration = std::move(*propertyMap);
- }
- }
-}
-
bool EventHub::Device::loadVirtualKeyMapLocked() {
// The virtual key map is supplied by the kernel as a system board property file.
std::string propPath = "/sys/board_properties/virtualkeys.";
@@ -1611,7 +1617,7 @@
}
std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked(
- const std::filesystem::path& devicePath) const {
+ const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const {
const std::optional<std::filesystem::path> sysfsRootPathOpt =
getSysfsRootPath(devicePath.c_str());
if (!sysfsRootPathOpt) {
@@ -1628,8 +1634,13 @@
if (!associatedDevice) {
// Found matching associated device for the first time.
associatedDevice = dev->associatedDevice;
- // Reload this associated device if needed.
- const auto reloadedDevice = AssociatedDevice(path);
+ // Reload this associated device if needed. Use the base device
+ // config. Note that this will essentially arbitrarily pick one
+ // Device as the base for the AssociatedDevice configuration. If
+ // there are multiple Device's that have a configuration for the
+ // AssociatedDevice, only one configuration will be chosen and will
+ // be used for all other AssociatedDevices for the same sysfs path.
+ const auto reloadedDevice = AssociatedDevice(path, associatedDevice->baseDevConfig);
if (reloadedDevice != *dev->associatedDevice) {
ALOGI("The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
path.c_str(), associatedDevice->dump().c_str());
@@ -1642,16 +1653,18 @@
if (!associatedDevice) {
// No existing associated device found for this path, so create a new one.
- associatedDevice = std::make_shared<AssociatedDevice>(path);
+ associatedDevice = std::make_shared<AssociatedDevice>(path, config);
}
return associatedDevice;
}
-EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath)
+EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+ std::shared_ptr<PropertyMap> config)
: sysfsRootPath(sysfsRootPath),
+ baseDevConfig(std::move(config)),
batteryInfos(readBatteryConfiguration(sysfsRootPath)),
- lightInfos(readLightsConfiguration(sysfsRootPath)),
+ lightInfos(readLightsConfiguration(sysfsRootPath, baseDevConfig)),
layoutInfo(readLayoutConfiguration(sysfsRootPath)) {}
std::string EventHub::AssociatedDevice::dump() const {
@@ -2337,11 +2350,21 @@
// Fill in the descriptor.
assignDescriptorLocked(identifier);
+ // Load the configuration file for the device.
+ std::shared_ptr<PropertyMap> configuration = nullptr;
+ base::Result<std::shared_ptr<PropertyMap>> propertyMapResult = loadConfiguration(identifier);
+ if (!propertyMapResult.ok()) {
+ ALOGE("Error loading input device configuration file for device '%s'. "
+ "Using default configuration. Error: %s",
+ identifier.name.c_str(), propertyMapResult.error().message().c_str());
+ } else {
+ configuration = propertyMapResult.value();
+ }
+
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
std::unique_ptr<Device> device =
- std::make_unique<Device>(fd, deviceId, devicePath, identifier,
- obtainAssociatedDeviceLocked(devicePath));
+ std::make_unique<Device>(fd, deviceId, devicePath, identifier, configuration);
ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
@@ -2356,8 +2379,8 @@
ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff,
driverVersion & 0xff);
- // Load the configuration file for the device.
- device->loadConfigurationLocked();
+ // Obtain the associated device, if any.
+ device->associatedDevice = obtainAssociatedDeviceLocked(devicePath, device->configuration);
// Figure out the kinds of events the device reports.
device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
@@ -2664,7 +2687,8 @@
testedDevices.emplace(dev.associatedDevice, false);
return false;
}
- auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath);
+ auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath,
+ dev.associatedDevice->baseDevConfig);
const bool changed = *dev.associatedDevice != reloadedDevice;
testedDevices.emplace(dev.associatedDevice, changed);
return changed;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 207806d..58df692 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/Thread.h>
+#include <string>
#include "InputDevice.h"
#include "include/gestures.h"
@@ -62,6 +63,28 @@
identifier1.location == identifier2.location);
}
+/**
+ * Determines if the device classes passed for two devices represent incompatible combinations
+ * that should not be merged into into a single InputDevice.
+ */
+
+bool isCompatibleSubDevice(ftl::Flags<InputDeviceClass> classes1,
+ ftl::Flags<InputDeviceClass> classes2) {
+ if (!com::android::input::flags::prevent_merging_input_pointer_devices()) {
+ return true;
+ }
+
+ const ftl::Flags<InputDeviceClass> pointerFlags =
+ ftl::Flags<InputDeviceClass>{InputDeviceClass::TOUCH, InputDeviceClass::TOUCH_MT,
+ InputDeviceClass::CURSOR, InputDeviceClass::TOUCHPAD};
+
+ // Do not merge devices that both have any type of pointer event.
+ if (classes1.any(pointerFlags) && classes2.any(pointerFlags)) return false;
+
+ // Safe to merge
+ return true;
+}
+
bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
@@ -174,9 +197,8 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
- if (debugRawEvents()) {
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
- }
+ ALOGD_IF(debugRawEvents(), "Timeout expired, latency=%0.3fms",
+ (now - mNextTimeout) * 0.000001f);
mNextTimeout = LLONG_MAX;
mPendingArgs += timeoutExpiredLocked(now);
}
@@ -241,9 +263,7 @@
}
batchSize += 1;
}
- if (debugRawEvents()) {
- ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
- }
+ ALOGD_IF(debugRawEvents(), "BatchSize: %zu Count: %zu", batchSize, count);
out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
@@ -271,7 +291,8 @@
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
- std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier);
+ ftl::Flags<InputDeviceClass> classes = mEventHub->getDeviceClasses(eventHubId);
+ std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier, classes);
mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
mPendingArgs += device->reset(when);
@@ -354,12 +375,16 @@
}
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) {
- auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
- const InputDeviceIdentifier identifier2 =
- devicePair.second->getDeviceInfo().getIdentifier();
- return isSubDevice(identifier, identifier2);
- });
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes) {
+ auto deviceIt =
+ std::find_if(mDevices.begin(), mDevices.end(), [identifier, classes](auto& devicePair) {
+ const InputDeviceIdentifier identifier2 =
+ devicePair.second->getDeviceInfo().getIdentifier();
+ const ftl::Flags<InputDeviceClass> classes2 = devicePair.second->getClasses();
+ return isSubDevice(identifier, identifier2) &&
+ isCompatibleSubDevice(classes, classes2);
+ });
std::shared_ptr<InputDevice> device;
if (deviceIt != mDevices.end()) {
@@ -921,7 +946,10 @@
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
+ dumpLocked(dump);
+}
+void InputReader::dumpLocked(std::string& dump) {
mEventHub->dump(dump);
dump += "\n";
@@ -1008,6 +1036,12 @@
InputReader::ContextImpl::ContextImpl(InputReader* reader)
: mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
+std::string InputReader::ContextImpl::dump() {
+ std::string dump;
+ mReader->dumpLocked(dump);
+ return dump;
+}
+
void InputReader::ContextImpl::updateGlobalMetaState() {
// lock is already held by the input loop
mReader->updateGlobalMetaStateLocked();
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 7434ae4..df22890 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -78,10 +78,8 @@
if (rawMaxBrightness != MAX_BRIGHTNESS) {
brightness = brightness * ratio;
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+ rawLightId, brightness, ratio);
return brightness;
}
@@ -97,10 +95,8 @@
if (rawMaxBrightness != MAX_BRIGHTNESS) {
brightness = ceil(brightness / ratio);
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+ rawLightId, brightness, ratio);
context.setLightBrightness(rawLightId, brightness);
}
@@ -453,10 +449,9 @@
if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
rawGlobalId = rawId;
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
- rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Light rawId %d name %s max %d flags %s\n", rawInfo->id,
+ rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
+ rawInfo->flags.string().c_str());
}
// Construct a player ID light
@@ -473,10 +468,8 @@
}
// Construct a RGB light for composed RGB light
if (hasRedLed && hasGreenLed && hasBlueLed) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
- rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Rgb light ids [%d, %d, %d]\n", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
keyboardBacklightIds.end() &&
keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
@@ -518,9 +511,8 @@
// If the node is multi-color led, construct a MULTI_COLOR light
if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Multicolor light Id %d name %s\n", rawInfo.id,
+ rawInfo.name.c_str());
std::unique_ptr<Light> light =
std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
type, rawInfo.id);
@@ -528,9 +520,8 @@
continue;
}
// Construct a Mono LED light
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Mono light Id %d name %s\n", rawInfo.id,
+ rawInfo.name.c_str());
std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
++mNextId, type, rawInfo.id);
@@ -552,10 +543,8 @@
return false;
}
auto& light = it->second;
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
- ftl::enum_string(light->type).c_str(), color);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "setLightColor lightId %d type %s color 0x%x", lightId,
+ ftl::enum_string(light->type).c_str(), color);
return light->setLightColor(color);
}
@@ -566,10 +555,8 @@
}
auto& light = it->second;
std::optional<int32_t> color = light->getLightColor();
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
- ftl::enum_string(light->type).c_str(), color.value_or(0));
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "getLightColor lightId %d type %s color 0x%x", lightId,
+ ftl::enum_string(light->type).c_str(), color.value_or(0));
return color;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 31ac63f..adbfdeb 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -21,6 +21,7 @@
#include <filesystem>
#include <functional>
#include <map>
+#include <memory>
#include <optional>
#include <ostream>
#include <string>
@@ -87,6 +88,7 @@
* If any new classes are added, we need to add them in rust input side too.
*/
enum class InputDeviceClass : uint32_t {
+ // LINT.IfChange
/* The input device is a keyboard or has buttons. */
KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD,
@@ -143,6 +145,7 @@
/* The input device is external (not built-in). */
EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL,
+ // LINT.ThenChange(frameworks/native/services/inputflinger/tests/fuzzers/MapperHelpers.h)
};
enum class SysfsClass : uint32_t {
@@ -619,9 +622,12 @@
private:
// Holds information about the sysfs device associated with the Device.
struct AssociatedDevice {
- AssociatedDevice(const std::filesystem::path& sysfsRootPath);
+ AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+ std::shared_ptr<PropertyMap> baseDevConfig);
// The sysfs root path of the misc device.
std::filesystem::path sysfsRootPath;
+ // The configuration of the base device.
+ std::shared_ptr<PropertyMap> baseDevConfig;
std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
std::optional<RawLayoutInfo> layoutInfo;
@@ -658,7 +664,7 @@
std::map<int /*axis*/, AxisState> absState;
std::string configurationFile;
- std::unique_ptr<PropertyMap> configuration;
+ std::shared_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
@@ -672,7 +678,7 @@
int32_t controllerNumber;
Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
- std::shared_ptr<const AssociatedDevice> assocDev);
+ std::shared_ptr<PropertyMap> config);
~Device();
void close();
@@ -692,7 +698,6 @@
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
bool hasKeycodeInternalLocked(int keycode) const;
- void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
bool isExternalDeviceLocked();
@@ -724,7 +729,8 @@
void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked(
- const std::filesystem::path& devicePath) const REQUIRES(mLock);
+ const std::filesystem::path& devicePath,
+ const std::shared_ptr<PropertyMap>& config) const REQUIRES(mLock);
void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 931766b..6a25937 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,10 +21,12 @@
#include <utils/Mutex.h>
#include <memory>
+#include <string>
#include <unordered_map>
#include <vector>
#include "EventHub.h"
+#include "InputDevice.h"
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
@@ -127,7 +129,8 @@
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
- const InputDeviceIdentifier& identifier)
+ const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes)
REQUIRES(mLock);
// With each iteration of the loop, InputReader reads and processes one incoming message from
@@ -140,6 +143,7 @@
public:
explicit ContextImpl(InputReader* reader);
+ std::string dump() REQUIRES(mReader->mLock) override;
// lock is already held by the input loop
void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
@@ -214,6 +218,8 @@
// The input device that produced a new gesture most recently.
DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID};
+ void dumpLocked(std::string& dump) REQUIRES(mLock);
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 20ed74f..f38fd7b 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -20,6 +20,7 @@
#include <input/KeyboardClassifier.h>
#include "NotifyArgs.h"
+#include <string>
#include <vector>
namespace android {
@@ -39,6 +40,8 @@
InputReaderContext() {}
virtual ~InputReaderContext() {}
+ virtual std::string dump() = 0;
+
virtual void updateGlobalMetaState() = 0;
virtual int32_t getGlobalMetaState() = 0;
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index dd46bbc..d796af1 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -25,8 +25,6 @@
#include <linux/input-event-codes.h>
#include <log/log_main.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
namespace {
@@ -119,15 +117,10 @@
}
void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
- AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
- tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
- AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
- } else {
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
- }
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
+ AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
+ AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
@@ -213,13 +206,11 @@
}
out.push_back(
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- // For any further events we send from this sync, the pointers won't have moved relative
- // to the positions we just reported in this MOVE event, so zero out the relative axes.
- for (PointerCoords& pointer : coords) {
- pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
- pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
- }
+ // For any further events we send from this sync, the pointers won't have moved relative to
+ // the positions we just reported in this MOVE event, so zero out the relative axes.
+ for (PointerCoords& pointer : coords) {
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
}
}
@@ -275,9 +266,7 @@
/*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
freePointerIdForSlot(slotNumber);
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- mPreviousCoordsForSlotNumber.erase(slotNumber);
- }
+ mPreviousCoordsForSlotNumber.erase(slotNumber);
coords.erase(coords.begin() + indexToRemove);
properties.erase(properties.begin() + indexToRemove);
// Now that we've removed some coords and properties, we might have to update the slot
@@ -336,15 +325,13 @@
coords.clear();
coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
- it != mPreviousCoordsForSlotNumber.end()) {
- auto [oldX, oldY] = it->second;
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
- }
- mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
+ if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
+ it != mPreviousCoordsForSlotNumber.end()) {
+ auto [oldX, oldY] = it->second;
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
}
+ mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fd8224a..4d08f19 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -79,19 +79,17 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
- if (DEBUG_POINTERS) {
- ALOGI("Stop processing slot %zu for it received a palm event from device %s",
- inIndex, getDeviceName().c_str());
- }
+ ALOGI_IF(DEBUG_POINTERS,
+ "Stop processing slot %zu for it received a palm event from device %s",
+ inIndex, getDeviceName().c_str());
continue;
}
if (outCount >= MAX_POINTERS) {
- if (DEBUG_POINTERS) {
- ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
- "ignoring the rest.",
- getDeviceName().c_str(), MAX_POINTERS);
- }
+ ALOGD_IF(DEBUG_POINTERS,
+ "MultiTouch device %s emitted more than maximum of %zu pointers; ignoring the "
+ "rest.",
+ getDeviceName().c_str(), MAX_POINTERS);
break; // too many fingers!
}
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 1f6600d..0d1d884 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -235,9 +235,8 @@
// else calculate difference between previous and current MSC_TIMESTAMP
if (mPrevMscTime == 0) {
mHardwareTimestamp = evTime;
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp);
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Initialize hardware timestamp = %" PRId64,
+ mHardwareTimestamp);
} else {
// Calculate the difference between current msc_timestamp and
// previous msc_timestamp, including when msc_timestamp wraps around.
@@ -330,11 +329,10 @@
bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
- ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
- maxBatchReportLatency.count());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS,
+ "Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
+ ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
+ maxBatchReportLatency.count());
if (!setSensorEnabled(sensorType, /*enabled=*/true)) {
return false;
@@ -355,9 +353,7 @@
}
void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Disable Sensor %s", ftl::enum_string(sensorType).c_str());
if (!setSensorEnabled(sensorType, /*enabled=*/false)) {
return;
@@ -389,15 +385,12 @@
}
nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
- ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
+ ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
if (sensor.lastSampleTimeNs.has_value() &&
timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s Skip a sample.",
+ ftl::enum_string(sensorType).c_str());
} else {
// Convert to Android unit
convertFromLinuxToAndroid(values, sensorType);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
index 9ec02a6..d55df51 100644
--- a/services/inputflinger/reader/mapper/SlopController.cpp
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -54,13 +54,13 @@
mCumulativeValue += value;
if (abs(mCumulativeValue) >= mSlopThreshold) {
- ALOGD("SlopController: did not drop event with value .%3f", value);
+ ALOGD("SlopController: did not drop event with value %.3f", value);
mHasSlopBeenMet = true;
// Return the amount of value that exceeds the slop.
return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
}
- ALOGD("SlopController: dropping event with value .%3f", value);
+ ALOGD("SlopController: dropping event with value %.3f", value);
return 0;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8deff6b..4d36db8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -24,6 +24,8 @@
#include <cinttypes>
#include <cmath>
#include <cstddef>
+#include <sstream>
+#include <string>
#include <tuple>
#include <math.h>
@@ -138,6 +140,40 @@
*outY = y;
}
+std::ostream& operator<<(std::ostream& out, const RawPointerData::Pointer& p) {
+ out << "id=" << p.id << ", x=" << p.x << ", y=" << p.y << ", pressure=" << p.pressure
+ << ", touchMajor=" << p.touchMajor << ", touchMinor=" << p.touchMinor
+ << ", toolMajor=" << p.toolMajor << ", toolMinor=" << p.toolMinor
+ << ", orientation=" << p.orientation << ", tiltX=" << p.tiltX << ", tiltY=" << p.tiltY
+ << ", distance=" << p.distance << ", toolType=" << ftl::enum_string(p.toolType)
+ << ", isHovering=" << p.isHovering;
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const RawPointerData& data) {
+ out << data.pointerCount << " pointers:\n";
+ for (uint32_t i = 0; i < data.pointerCount; i++) {
+ out << INDENT << "[" << i << "]: " << data.pointers[i] << std::endl;
+ }
+ out << "ID bits: hovering = 0x" << std::hex << std::setfill('0') << std::setw(8)
+ << data.hoveringIdBits.value << ", touching = 0x" << std::setfill('0') << std::setw(8)
+ << data.touchingIdBits.value << ", canceled = 0x" << std::setfill('0') << std::setw(8)
+ << data.canceledIdBits.value << std::dec;
+ return out;
+}
+
+// --- TouchInputMapper::RawState ---
+
+std::ostream& operator<<(std::ostream& out, const TouchInputMapper::RawState& state) {
+ out << "When: " << state.when << std::endl;
+ out << "Read time: " << state.readTime << std::endl;
+ out << "Button state: 0x" << std::setfill('0') << std::setw(8) << std::hex << state.buttonState
+ << std::dec << std::endl;
+ out << "Raw pointer data:" << std::endl;
+ out << addLinePrefix(streamableToString(state.rawPointerData), INDENT);
+ return out;
+}
+
// --- TouchInputMapper ---
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext,
@@ -232,20 +268,8 @@
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=%s, 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, ftl::enum_string(pointer.toolType).c_str(),
- toString(pointer.isHovering));
- }
+ dump += INDENT3 "Last Raw Touch:\n";
+ dump += addLinePrefix(streamableToString(mLastRawState), INDENT4) + "\n";
dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n",
mLastCookedState.buttonState);
@@ -1476,6 +1500,22 @@
last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
next.rawPointerData.canceledIdBits.value);
+ if (debugRawEvents() && last.rawPointerData.pointerCount == 0 &&
+ next.rawPointerData.pointerCount == 1) {
+ // Dump a bunch of info to try to debug b/396796958.
+ // TODO(b/396796958): remove this debug dump.
+ ALOGD("pointerCount went from 0 to 1. last:\n%s",
+ addLinePrefix(streamableToString(last), INDENT).c_str());
+ ALOGD("next:\n%s", addLinePrefix(streamableToString(next), INDENT).c_str());
+ ALOGD("InputReader dump:");
+ // The dump is too long to simply add as a format parameter in one log message, so we have
+ // to split it by line and log them individually.
+ std::istringstream stream(mDeviceContext.getContext()->dump());
+ std::string line;
+ while (std::getline(stream, line, '\n')) {
+ ALOGD(INDENT "%s", line.c_str());
+ }
+ }
if (!next.rawPointerData.touchingIdBits.isEmpty() &&
!next.rawPointerData.hoveringIdBits.isEmpty() &&
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 4ef0be8..45fc6bf 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -348,6 +348,8 @@
inline void clear() { *this = RawState(); }
};
+ friend std::ostream& operator<<(std::ostream& out, const RawState& state);
+
struct CookedState {
// Cooked pointer sample data.
CookedPointerData cookedPointerData{};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 18a7102..c982dab 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -40,6 +40,7 @@
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "gestures/HardwareProperties.h"
+#include "gestures/Logging.h"
#include "gestures/TimerProvider.h"
#include "ui/Rotation.h"
@@ -49,15 +50,6 @@
namespace {
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
- ANDROID_LOG_INFO);
-
std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
bool accelerationEnabled,
size_t propertySize) {
@@ -470,7 +462,7 @@
std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs) {
- ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
+ ALOGD_IF(debugTouchpadGestures(), "New hardware state: %s", schs.state.String().c_str());
mGestureInterpreter->PushHardwareState(&schs.state);
return processGestures(when, readTime);
}
@@ -481,7 +473,7 @@
}
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
- ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
+ ALOGD_IF(debugTouchpadGestures(), "Gesture ready: %s", gesture->String().c_str());
if (mResettingInterpreter) {
// We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should
// ignore any gestures produced from the interpreter while we're resetting it.
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index a3a48ef..264ef6f 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -43,10 +43,8 @@
std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence,
ssize_t repeat, int32_t token) {
- if (DEBUG_VIBRATOR) {
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
- sequence.toString().c_str(), repeat, token);
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
+ getDeviceId(), sequence.toString().c_str(), repeat, token);
std::list<NotifyArgs> out;
mVibrating = true;
@@ -63,9 +61,7 @@
}
std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
- if (DEBUG_VIBRATOR) {
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
std::list<NotifyArgs> out;
if (mVibrating && mToken == token) {
@@ -95,9 +91,7 @@
}
std::list<NotifyArgs> VibratorInputMapper::nextStep() {
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
std::list<NotifyArgs> out;
mIndex += 1;
if (size_t(mIndex) >= mSequence.pattern.size()) {
@@ -111,16 +105,11 @@
const VibrationElement& element = mSequence.pattern[mIndex];
if (element.isOn()) {
- if (DEBUG_VIBRATOR) {
- std::string description = element.toString();
- ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
- description.c_str());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ element.toString().c_str());
getDeviceContext().vibrate(element);
} else {
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -128,17 +117,13 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: scheduled timeout in %lldms", element.duration.count());
return out;
}
NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
mVibrating = false;
- if (DEBUG_VIBRATOR) {
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
getDeviceContext().cancelVibrate();
// Request InputReader to notify InputManagerService for vibration complete.
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 480e276..3255877 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -39,9 +39,6 @@
namespace {
-// This will disable the tap to click while the user is typing on a physical keyboard
-const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
-
// In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
// re-enabling the tap to click.
const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
@@ -226,8 +223,7 @@
if (!mIsHoverCancelled) {
// handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
// for this case as subsequent handleButtonsChange may choose to ignore this tap.
- if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
- (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
enableTapToClick(when);
}
}
@@ -278,7 +274,7 @@
// return early to prevent this tap
return out;
}
- } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
+ } else if (mReaderContext.isPreventingTouchpadTaps()) {
enableTapToClick(when);
if (gesture.details.buttons.is_tap) {
// return early to prevent this tap
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
similarity index 72%
rename from services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
rename to services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
index 26028c5..51905f9 100644
--- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
@@ -22,29 +22,17 @@
#include <log/log.h>
+#include "Logging.h"
#include "include/gestures.h"
extern "C" {
-namespace {
-
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
- ANDROID_LOG_INFO);
-
-} // namespace
-
void gestures_log(int verb, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
if (verb == GESTURES_LOG_ERROR) {
LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
- } else if (DEBUG_TOUCHPAD_GESTURES) {
+ } else if (android::debugTouchpadGestures()) {
if (verb == GESTURES_LOG_INFO) {
LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
} else {
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 6885adb..3e62f36 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -25,12 +25,8 @@
#include <com_android_input_flags.h>
#include <linux/input-event-codes.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
-const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library();
-
HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
MultiTouchMotionAccumulator& motionAccumulator)
: mDeviceContext(deviceContext),
@@ -81,18 +77,11 @@
}
schs.fingers.clear();
- size_t numPalms = 0;
for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
if (!slot.isInUse()) {
continue;
}
- // Some touchpads continue to report contacts even after they've identified them as palms.
- // We want to exclude these contacts from the HardwareStates.
- if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) {
- numPalms++;
- continue;
- }
FingerState& fingerState = schs.fingers.emplace_back();
fingerState = {};
@@ -105,15 +94,13 @@
fingerState.position_x = slot.getX();
fingerState.position_y = slot.getY();
fingerState.tracking_id = slot.getTrackingId();
- if (REPORT_PALMS_TO_GESTURES_LIBRARY) {
- fingerState.tool_type = slot.getToolType() == ToolType::PALM
- ? FingerState::ToolType::kPalm
- : FingerState::ToolType::kFinger;
- }
+ fingerState.tool_type = slot.getToolType() == ToolType::PALM
+ ? FingerState::ToolType::kPalm
+ : FingerState::ToolType::kFinger;
}
schs.state.fingers = schs.fingers.data();
schs.state.finger_cnt = schs.fingers.size();
- schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount() - numPalms;
+ schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
return schs;
}
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.cpp b/services/inputflinger/reader/mapper/gestures/Logging.cpp
new file mode 100644
index 0000000..b9b97c3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2025 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 "Logging.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+} // namespace
+
+namespace android {
+
+bool debugTouchpadGestures() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_RAW_EVENTS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+ return DEBUG_RAW_EVENTS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.h b/services/inputflinger/reader/mapper/gestures/Logging.h
new file mode 100644
index 0000000..db59fb3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+/**
+ * Log details of touchpad gesture library input, output, and processing.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugTouchpadGestures();
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 353011a..c6246d9 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -33,8 +33,6 @@
#include "TestEventMatchers.h"
#include "TestInputListener.h"
-namespace input_flags = com::android::input::flags;
-
namespace android {
using testing::AllOf;
@@ -50,8 +48,6 @@
mReader(mFakeEventHub, mFakePolicy, mFakeListener),
mDevice(newDevice()),
mDeviceContext(*mDevice, EVENTHUB_ID) {
- input_flags::include_relative_axis_values_for_captured_touchpads(true);
-
const size_t slotCount = 8;
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index c4257a8..dcb148f 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -198,10 +198,6 @@
ASSERT_EQ(token, *receivedToken);
}
-void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
- mInterceptKeyTimeout = timeout;
-}
-
std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() {
return 500ms;
}
@@ -210,8 +206,9 @@
mStaleEventTimeout = timeout;
}
-void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
- mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+void FakeInputDispatcherPolicy::setInterceptKeyBeforeDispatchingResult(
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result) {
+ mInterceptKeyBeforeDispatchingResult = result;
}
void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) {
@@ -404,7 +401,9 @@
void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) {
if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
// Clear intercept state when we handled the event.
- mInterceptKeyTimeout = 0ms;
+ if (std::holds_alternative<nsecs_t>(mInterceptKeyBeforeDispatchingResult)) {
+ mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
+ }
}
}
@@ -414,17 +413,20 @@
std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&,
uint32_t) {
- if (mConsumeKeyBeforeDispatching) {
- return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
+ if (std::holds_alternative<inputdispatcher::KeyEntry::InterceptKeyResult>(
+ mInterceptKeyBeforeDispatchingResult)) {
+ return mInterceptKeyBeforeDispatchingResult;
}
- nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+ nsecs_t delay =
+ std::chrono::nanoseconds(std::get<nsecs_t>(mInterceptKeyBeforeDispatchingResult))
+ .count();
if (delay == 0) {
return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
}
// Clear intercept state so we could dispatch the event in next wake.
- mInterceptKeyTimeout = 0ms;
+ mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
return delay;
}
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index c387eac..b151686 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -96,10 +96,6 @@
void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
const sp<IBinder>& targetToken);
void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token);
- /**
- * Set policy timeout. A value of zero means next key will not be intercepted.
- */
- void setInterceptKeyTimeout(std::chrono::milliseconds timeout);
std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override;
void setStaleEventTimeout(std::chrono::nanoseconds timeout);
void assertUserActivityNotPoked();
@@ -116,7 +112,12 @@
void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
void assertUnhandledKeyReported(int32_t keycode);
void assertUnhandledKeyNotReported();
- void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
+ /**
+ * Set policy timeout or the interception result.
+ * A timeout value of zero means next key will not be intercepted.
+ */
+ void setInterceptKeyBeforeDispatchingResult(
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result);
void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay);
private:
@@ -145,11 +146,10 @@
std::condition_variable mNotifyUserActivity;
std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
- std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
-
std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
- bool mConsumeKeyBeforeDispatching = false;
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ mInterceptKeyBeforeDispatchingResult;
BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 914f5ab..35310a5 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -42,8 +42,6 @@
namespace {
-const auto TOUCHPAD_PALM_REJECTION =
- ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
const auto TOUCHPAD_PALM_REJECTION_V2 =
ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection);
@@ -1461,7 +1459,6 @@
}
TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
@@ -1574,8 +1571,7 @@
ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f))));
}
-TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
// Click should still produce button press/release events
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1644,8 +1640,7 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
// initially disable tap-to-click
mReader->getContext()->setPreventingTouchpadTaps(true);
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 34c81fc..ee125bb 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -33,13 +33,6 @@
namespace android {
-namespace {
-
-const auto REPORT_PALMS =
- ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library);
-
-} // namespace
-
class HardwareStateConverterTest : public testing::Test {
public:
HardwareStateConverterTest()
@@ -201,24 +194,7 @@
EXPECT_EQ(0u, finger2.flags);
}
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms,
- REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- EXPECT_EQ(0, schs->state.finger_cnt);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms,
- REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OnePalm) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
@@ -234,54 +210,7 @@
EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
}
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms,
- REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
-
- std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(1, schs->state.touch_cnt);
- EXPECT_EQ(1, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99);
-
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- ASSERT_EQ(0, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97);
-
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- EXPECT_EQ(0, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95);
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(1, schs->state.touch_cnt);
- ASSERT_EQ(1, schs->state.finger_cnt);
- const FingerState& newFinger = schs->state.fingers[0];
- EXPECT_EQ(123, newFinger.tracking_id);
- EXPECT_NEAR(55, newFinger.position_x, EPSILON);
- EXPECT_NEAR(95, newFinger.position_y, EPSILON);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms,
- REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7cc4ff7..835b677 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5491,7 +5491,8 @@
generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
const std::chrono::milliseconds interceptKeyTimeout = 50ms;
const nsecs_t injectTime = keyArgs.eventTime;
- mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ std::chrono::nanoseconds(interceptKeyTimeout).count());
mDispatcher->notifyKey(keyArgs);
// The dispatching time should be always greater than or equal to intercept key timeout.
window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
@@ -5519,7 +5520,7 @@
// Set a value that's significantly larger than the default consumption timeout. If the
// implementation is correct, the actual value doesn't matter; it won't slow down the test.
- mFakePolicy->setInterceptKeyTimeout(600ms);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(std::chrono::nanoseconds(600ms).count());
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
// Window should receive key event immediately when same key up.
window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
@@ -7438,7 +7439,8 @@
window->consumeFocusEvent(true);
- mFakePolicy->setConsumeKeyBeforeDispatching(true);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7464,7 +7466,8 @@
window->consumeFocusEvent(true);
- mFakePolicy->setConsumeKeyBeforeDispatching(true);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -9307,6 +9310,24 @@
mWindow->assertNoEvents();
}
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterFocusedWindowChanged) {
+ sp<FakeWindowHandle> anotherWindow =
+ sp<FakeWindowHandle>::make(mApp, mDispatcher, "AnotherWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ anotherWindow->setFocusable(true);
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *anotherWindow->getInfo()}, {}, 0, 0});
+
+ sendAndConsumeKeyDown(/*deviceId=*/1);
+ expectKeyRepeatOnce(/*repeatCount=*/1);
+ expectKeyRepeatOnce(/*repeatCount=*/2);
+ setFocusedWindow(anotherWindow);
+ anotherWindow->consumeFocusEvent(true);
+
+ // Window should receive key up event with cancel.
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT, AKEY_EVENT_FLAG_CANCELED);
+ anotherWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
sendAndConsumeKeyDown(/*deviceId=*/1);
expectKeyRepeatOnce(/*repeatCount=*/1);
@@ -9924,6 +9945,9 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
+ // Use current time as start time otherwise events may be dropped due to being stale.
+ mGestureStartTime = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+
std::shared_ptr<FakeApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
application->setDispatchingTimeout(100ms);
@@ -9941,82 +9965,81 @@
mWindow->consumeFocusEvent(true);
}
- void notifyAndConsumeMotion(int32_t action, uint32_t source, ui::LogicalDisplayId displayId,
- nsecs_t eventTime) {
- mDispatcher->notifyMotion(MotionArgsBuilder(action, source)
- .displayId(displayId)
- .eventTime(eventTime)
- .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
- .build());
+ NotifyMotionArgs notifyAndConsumeMotion(int32_t action, uint32_t source,
+ ui::LogicalDisplayId displayId,
+ std::chrono::nanoseconds timeDelay) {
+ const NotifyMotionArgs motionArgs =
+ MotionArgsBuilder(action, source)
+ .displayId(displayId)
+ .eventTime((mGestureStartTime + timeDelay).count())
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build();
+ mDispatcher->notifyMotion(motionArgs);
mWindow->consumeMotionEvent(WithMotionAction(action));
+ return motionArgs;
}
private:
sp<FakeWindowHandle> mWindow;
+ std::chrono::nanoseconds mGestureStartTime;
};
TEST_F_WITH_FLAGS(
InputDispatcherUserActivityPokeTests, MinPokeTimeObserved,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
// First event of type TOUCH. Should poke.
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(50));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(50));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// 80ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(130));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(130));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(135));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(135));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
// Within 50ns of previous TOUCH event. Should NOT poke.
notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(140));
+ std::chrono::milliseconds(140));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous OTHER event. Should NOT poke.
notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(150));
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(150));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
// Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(160));
+ std::chrono::milliseconds(160));
mFakePolicy->assertUserActivityNotPoked();
// 65ns > 50ns has passed since previous OTHER event. Should poke.
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(200));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
// 170ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(300));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
+ std::chrono::milliseconds(300));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// Assert that there's no more user activity poke event.
mFakePolicy->assertUserActivityNotPoked();
@@ -10026,39 +10049,35 @@
InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(200));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(280));
+ std::chrono::milliseconds(280));
mFakePolicy->assertUserActivityNotPoked();
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(340));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(340));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
}
TEST_F_WITH_FLAGS(
InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + 20);
+ std::chrono::milliseconds(20));
mFakePolicy->assertUserActivityPoked();
notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + 30);
+ std::chrono::milliseconds(30));
mFakePolicy->assertUserActivityPoked();
}
@@ -12376,6 +12395,11 @@
sp<FakeWindowHandle> mSecondWindow;
sp<FakeWindowHandle> mDragWindow;
sp<FakeWindowHandle> mSpyWindow;
+
+ std::vector<gui::DisplayInfo> mDisplayInfos;
+
+ std::shared_ptr<FakeApplicationHandle> mSecondApplication;
+ sp<FakeWindowHandle> mWindowOnSecondDisplay;
// Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
static constexpr int32_t MOUSE_POINTER_ID = 1;
@@ -12396,26 +12420,37 @@
mSpyWindow->setTrustedOverlay(true);
mSpyWindow->setFrame(Rect(0, 0, 200, 100));
+ mSecondApplication = std::make_shared<FakeApplicationHandle>();
+ mWindowOnSecondDisplay =
+ sp<FakeWindowHandle>::make(mSecondApplication, mDispatcher,
+ "TestWindowOnSecondDisplay", SECOND_DISPLAY_ID);
+ mWindowOnSecondDisplay->setFrame({0, 0, 100, 100});
+
mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mDispatcher->onWindowInfosChanged(
- {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
- {},
+ {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo(),
+ *mWindowOnSecondDisplay->getInfo()},
+ mDisplayInfos,
0,
0});
}
- void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) {
bool consumeButtonPress = false;
+ const PointF location =
+ displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450);
switch (fromSource) {
case AINPUT_SOURCE_TOUCHSCREEN: {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
- ui::LogicalDisplayId::DEFAULT, {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+ location))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
}
case AINPUT_SOURCE_STYLUS: {
- PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50);
+ PointerBuilder pointer =
+ PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
@@ -12438,12 +12473,14 @@
break;
}
case AINPUT_SOURCE_MOUSE: {
- PointerBuilder pointer =
- PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50);
+ PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
+ .x(location.x)
+ .y(location.y);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_MOUSE)
+ .displayId(displayId)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(pointer)
.build()));
@@ -12451,6 +12488,7 @@
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
+ .displayId(displayId)
.actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(pointer)
@@ -12464,43 +12502,59 @@
}
// Window should receive motion event.
- mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ sp<FakeWindowHandle>& targetWindow =
+ displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay;
+ targetWindow->consumeMotionDown(displayId);
if (consumeButtonPress) {
- mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
}
- // Spy window should also receive motion event
- mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+
+ // Spy window should also receive motion event if event is on the same display.
+ if (displayId == ui::LogicalDisplayId::DEFAULT) {
+ mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ }
}
// Start performing drag, we will create a drag window and transfer touch to it.
// @param sendDown : if true, send a motion down on first window before perform drag and drop.
// Returns true on success.
- bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) {
if (sendDown) {
- injectDown(fromSource);
+ injectDown(fromSource, dragStartDisplay);
}
// The drag window covers the entire display
- mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow",
- ui::LogicalDisplayId::DEFAULT);
+ mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay);
mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
- mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
- *mWindow->getInfo(), *mSecondWindow->getInfo()},
- {},
- 0,
- 0});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *mWindowOnSecondDisplay->getInfo()},
+ mDisplayInfos,
+ 0,
+ 0});
+
+ sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT
+ ? mWindow
+ : mWindowOnSecondDisplay;
// Transfer touch focus to the drag window
bool transferred =
- mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(),
+ mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(),
/*isDragDrop=*/true);
if (transferred) {
- mWindow->consumeMotionCancel();
- mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ targetWindow->consumeMotionCancel(dragStartDisplay);
+ mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
return transferred;
}
+
+ void addDisplay(ui::LogicalDisplayId displayId, ui::Transform transform) {
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = displayId;
+ displayInfo.transform = transform;
+ mDisplayInfos.push_back(displayInfo);
+ }
};
TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
@@ -15187,4 +15241,230 @@
INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool());
+class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests {
+ constexpr static int DENSITY_MEDIUM = 160;
+
+ const DisplayTopologyGraph
+ mTopology{.primaryDisplayId = DISPLAY_ID,
+ .graph = {{DISPLAY_ID,
+ {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}},
+ {SECOND_DISPLAY_ID,
+ {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}},
+ .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM},
+ {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}};
+
+protected:
+ void SetUp() override {
+ addDisplay(DISPLAY_ID, ui::Transform());
+ addDisplay(SECOND_DISPLAY_ID,
+ ui::Transform(ui::Transform::ROT_270, /*logicalDisplayWidth=*/
+ 500, /*logicalDisplayHeight=*/500));
+
+ InputDispatcherDragTests::SetUp();
+
+ mDispatcher->setDisplayTopology(mTopology);
+ }
+};
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // pointer-down
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ // pointer-move
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ // pointer-move with different display
+ // TODO (b/383092013): by default windows will not be topology aware and receive events as it
+ // was in the same display. This behaviour has not been implemented yet.
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ // events should be delivered with the second displayId and in corrosponding coordinate space
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+ // pointer-up
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ mWindow->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE));
+ // Move on window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ // Move to another window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(150).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ // Move to window on the second display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->assertNoEvents();
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+ mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+ // drop on the second display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionUp(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindowOnSecondDisplay->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID));
+ // Move on window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+ // Move to window on the primary display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50);
+
+ // drop on the primary display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+}
+
+using InputDispatcherConnectedDisplayPointerInWindowTest = InputDispatcherConnectedDisplayTest;
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+ SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnNonPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+
+ mWindow->assertNoEvents();
+ mSpyWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+ SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d9a75a5..43d2378 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1405,6 +1405,68 @@
ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(3), false);
}
+TEST_F(InputReaderTest, MergeableInputDevices) {
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::JOYSTICK, nullptr));
+
+ // The two devices will be merged to one input device as they have same identifier, and none are
+ // pointer devices.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, MergeableDevicesWithTouch) {
+ constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::GAMEPAD, nullptr));
+
+ // The three devices will be merged to one input device as they have same identifier, and only
+ // one is a pointer device.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, UnmergeableTouchDevices) {
+ SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
+
+ constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "2nd", InputDeviceClass::CURSOR, nullptr));
+
+ // The three devices will not be merged, as they have same identifier, but are all pointer
+ // devices.
+ ASSERT_EQ(3U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, MergeableMixedDevices) {
+ SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
+
+ constexpr int32_t eventHubIds[4] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2,
+ END_RESERVED_ID + 3};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::DPAD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[3], "4th", InputDeviceClass::JOYSTICK, nullptr));
+
+ // Non-touch devices can be merged with one of the touch devices, as they have same identifier,
+ // but the two touch devices will not combine with each other. It is not specified which touch
+ // device the non-touch devices merge with.
+ ASSERT_EQ(2U, mFakePolicy->getInputDevices().size());
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
index 110ca5f..53fc8a1 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.cpp
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -38,13 +38,14 @@
}
std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes) REQUIRES(mLock) {
if (!mNextDevices.empty()) {
std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
mNextDevices.pop();
return device;
}
- return InputReader::createDeviceLocked(when, eventHubId, identifier);
+ return InputReader::createDeviceLocked(when, eventHubId, identifier, classes);
}
} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index e9c7bb4..9abf30c 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -43,8 +43,9 @@
using InputReader::loopOnce;
protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier);
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t eventHubId,
+ const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes);
class FakeInputReaderContext : public ContextImpl {
public:
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index dce5472..d4e4bb0 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -50,6 +50,8 @@
class MockInputReaderContext : public InputReaderContext {
public:
+ std::string dump() override { return "(dump from MockInputReaderContext)"; }
+
MOCK_METHOD(void, updateGlobalMetaState, (), (override));
MOCK_METHOD(int32_t, getGlobalMetaState, (), (override));
@@ -191,6 +193,9 @@
(ui::LogicalDisplayId displayId, const vec2& position), (override));
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
+ MOCK_METHOD(std::optional<vec2>, filterPointerMotionForAccessibility,
+ (const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId),
+ (override));
};
class MockInputDevice : public InputDevice {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 17acdd4..d4b15bc 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -176,16 +176,22 @@
return std::get<NotifyKeyArgs>(args.front());
}
- void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) {
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1);
+ std::list<NotifyArgs> processKeyAndSync(nsecs_t when, int32_t code, int32_t value) {
+ std::list<NotifyArgs> argsList = process(when, EV_KEY, code, value);
+ argsList += process(when, EV_SYN, SYN_REPORT, 0);
+ return argsList;
+ }
+
+ void testDPadKeyRotation(int32_t originalEvdevCode, int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId = DISPLAY_ID) {
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(originalEvdevCode, args.scanCode);
@@ -205,23 +211,16 @@
.With(Args<0>(when))
.Times(keyCodes.size());
for (int32_t keyCode : keyCodes) {
- process(when, EV_KEY, keyCode, 1);
- process(when, EV_SYN, SYN_REPORT, 0);
- process(when, EV_KEY, keyCode, 0);
- process(when, EV_SYN, SYN_REPORT, 0);
+ processKeyAndSync(when, keyCode, 1);
+ processKeyAndSync(when, keyCode, 0);
}
}
TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) {
std::list<NotifyArgs> args;
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 1);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 2);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
@@ -241,7 +240,7 @@
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
// Key down by evdev code.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -255,7 +254,7 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by evdev code.
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -271,6 +270,7 @@
// Key down by usage code.
argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -284,8 +284,9 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by usage code.
- argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_A);
argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -307,6 +308,7 @@
// Key down with unknown scan code or usage code.
std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -320,8 +322,9 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up with unknown scan code or usage code.
- argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -343,10 +346,12 @@
// Key down
std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
+ argsList += process(ARBITRARY_TIME, /*readTime=*/12, EV_SYN, SYN_REPORT, 0);
ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime);
// Key up
- argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
+ argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 0);
+ argsList += process(ARBITRARY_TIME, /*readTime=*/15, EV_SYN, SYN_REPORT, 0);
ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime);
}
@@ -360,22 +365,22 @@
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
// Metakey down.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFTSHIFT, 1);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Key down.
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 1);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Key up.
- argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 2, KEY_A, 0);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Metakey up.
- argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 3, KEY_LEFTSHIFT, 0);
ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
}
@@ -387,11 +392,10 @@
addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
setDisplayOrientation(ui::Rotation::Rotation90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
}
TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) {
@@ -405,43 +409,40 @@
AINPUT_SOURCE_KEYBOARD);
setDisplayOrientation(ui::ROTATION_0);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
setDisplayOrientation(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_DOWN));
setDisplayOrientation(ui::ROTATION_180);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_RIGHT));
setDisplayOrientation(ui::ROTATION_270);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_UP));
// Special case: if orientation changes while key is down, we still emit the same keycode
// in the key up as we did in the key down.
setDisplayOrientation(ui::ROTATION_270);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
setDisplayOrientation(ui::ROTATION_180);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -454,9 +455,9 @@
addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
// Display id should be LogicalDisplayId::INVALID without any display configuration.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
}
@@ -474,9 +475,9 @@
// ^--- already checked by the previous test
setDisplayOrientation(ui::ROTATION_0);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
@@ -487,9 +488,9 @@
argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_EQ(0u, argsList.size());
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId);
}
@@ -565,48 +566,48 @@
ASSERT_FALSE(scrollLockLed);
// Toggle caps lock on.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState());
// Toggle num lock on.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState());
// Toggle caps lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState());
// Toggle scroll lock on.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_TRUE(scrollLockLed);
ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
// Toggle num lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_TRUE(scrollLockLed);
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
// Toggle scroll lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_FALSE(scrollLockLed);
@@ -618,8 +619,8 @@
addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
- // Key down by scan code.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ // Key down by evdev code.
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -685,37 +686,168 @@
addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
// Key down
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFT, 1);
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE,
expectSingleKeyArg(argsList).flags);
}
-TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- enable_alphabetic_keyboard_wake))) {
- // For internal alphabetic devices, keys will trigger wake on key down.
+// --- KeyboardInputMapperUnitTest_WakeFlagOverride ---
+
+class KeyboardInputMapperUnitTest_WakeFlagOverride : public KeyboardInputMapperUnitTest {
+protected:
+ virtual void SetUp() override {
+ SetUp(/*wakeFlag=*/com::android::input::flags::enable_alphabetic_keyboard_wake());
+ }
+
+ void SetUp(bool wakeFlag) {
+ mWakeFlagInitialValue = com::android::input::flags::enable_alphabetic_keyboard_wake();
+ com::android::input::flags::enable_alphabetic_keyboard_wake(wakeFlag);
+ KeyboardInputMapperUnitTest::SetUp();
+ }
+
+ void TearDown() override {
+ com::android::input::flags::enable_alphabetic_keyboard_wake(mWakeFlagInitialValue);
+ KeyboardInputMapperUnitTest::TearDown();
+ }
+
+ bool mWakeFlagInitialValue;
+};
+
+// --- KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled,
+ NonAlphabeticDevice_WakeBehavior) {
+ // For internal non-alphabetic devices keys will not trigger wake.
addKeyByEvdevCode(KEY_A, AKEYCODE_A);
addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior) {
+ // For internal alphabetic devices, keys will trigger wake on key down when
+ // flag is enabled.
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior_UnknownKey) {
+ // For internal alphabetic devices, unknown keys will trigger wake on key down when
+ // flag is enabled.
+
+ const int32_t USAGE_UNKNOWN = 0x07ffff;
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _))
+ .WillRepeatedly(Return(NAME_NOT_FOUND));
+
+ // Key down with unknown scan code or usage code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ // Key up with unknown scan code or usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticDevice_AlphabeticKeyboardWakeDisabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/false);
+
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled, WakeBehavior) {
+ // For internal alphabetic devices, keys will not trigger wake when flag is
+ // disabled.
+
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
}
// --- KeyboardInputMapperTest ---
@@ -730,28 +862,33 @@
}
const std::string UNIQUE_ID = "local:0";
- void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
- int32_t originalKeyCode, int32_t rotatedKeyCode,
+ void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalEvdevCode,
+ int32_t rotatedKeyCode,
ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
+
+ void processKeyAndSync(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t code,
+ int32_t value) {
+ process(mapper, when, readTime, EV_KEY, code, value);
+ process(mapper, when, readTime, EV_SYN, SYN_REPORT, 0);
+ }
};
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
- int32_t originalScanCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode,
+ int32_t originalEvdevCode, int32_t rotatedKeyCode,
ui::LogicalDisplayId displayId) {
NotifyKeyArgs args;
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
}
@@ -819,23 +956,19 @@
ASSERT_TRUE(device2->isEnabled());
// Test pad key events
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+ testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, newDisplayId));
ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, newDisplayId));
+ testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, newDisplayId));
}
TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
@@ -857,20 +990,20 @@
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
@@ -937,16 +1070,16 @@
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
mReader->resetLockedModifierState();
@@ -996,40 +1129,40 @@
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle num lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle caps lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle scroll lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
@@ -1048,10 +1181,10 @@
KeyboardInputMapper& keyboardMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
@@ -1059,10 +1192,10 @@
KeyboardInputMapper& dpadMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
}
@@ -1071,10 +1204,10 @@
KeyboardInputMapper& gamepadMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
}
@@ -1119,22 +1252,22 @@
addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
@@ -1145,16 +1278,16 @@
addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
@@ -1169,22 +1302,22 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_DOWN, 1);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_DOWN, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1286a36..2b469c5 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -24,6 +24,7 @@
#include "FakePointerController.h"
#include "InterfaceMocks.h"
#include "NotifyArgsBuilders.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#include "TestInputListener.h"
@@ -114,6 +115,10 @@
}) {}
class PointerChoreographerTest : public testing::Test {
+public:
+ static constexpr int DENSITY_MEDIUM = 160;
+ static constexpr int DENSITY_HIGH = 320;
+
protected:
TestInputListener mTestListener;
sp<gui::WindowInfosListener> mRegisteredWindowInfoListener;
@@ -124,9 +129,6 @@
mInjectedInitialWindowInfos};
void SetUp() override {
- // flag overrides
- input_flags::hide_pointer_indicators_for_secure_windows(true);
-
ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
EXPECT_FALSE(pc->isPointerShown());
@@ -140,6 +142,22 @@
});
}
+ void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+ if (input_flags::connected_displays_cursor()) {
+ // setDefaultMouseDisplayId is no-op if connected displays are enabled, mouse display is
+ // set based on primary display of the topology.
+ // Setting topology with the primary display should have same effect as calling
+ // setDefaultMouseDisplayId without topology.
+ // For this reason in tests we mock this behavior by creating topology with a single
+ // display.
+ mChoreographer.setDisplayTopology({.primaryDisplayId = displayId,
+ .graph{{displayId, {}}},
+ .displaysDensity = {{displayId, DENSITY_MEDIUM}}});
+ } else {
+ mChoreographer.setDefaultMouseDisplayId(displayId);
+ }
+ }
+
std::shared_ptr<FakePointerController> assertPointerControllerCreated(
ControllerType expectedType) {
EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created";
@@ -292,7 +310,7 @@
TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// For a mouse event without a target display, default viewport should be set for
// the PointerController.
@@ -309,7 +327,7 @@
WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) {
// Set one display as a default mouse display and emit mouse event to create PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -320,7 +338,7 @@
// Change default mouse display. Existing PointerController should be removed and a new one
// should be created.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -329,7 +347,7 @@
}
TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -340,8 +358,19 @@
assertPointerDisplayIdNotified(DISPLAY_ID);
}
+TEST_F(PointerChoreographerTest, NoDefaultMouseSetFallbackToDefaultDisplayId) {
+ mChoreographer.setDisplayViewports(createViewports({ui::LogicalDisplayId::DEFAULT}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+
+ assertPointerDisplayIdNotified(ui::LogicalDisplayId::DEFAULT);
+}
+
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -354,7 +383,7 @@
}
TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -373,7 +402,7 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
// Set one viewport as a default mouse display ID.
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -382,7 +411,7 @@
assertPointerDisplayIdNotified(DISPLAY_ID);
// Set another viewport as a default mouse display ID. The mouse is moved to the other display.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
assertPointerControllerCreated(ControllerType::MOUSE);
@@ -391,7 +420,7 @@
TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -421,7 +450,7 @@
TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -457,7 +486,7 @@
AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
// Add two displays and set one to default.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Add two devices, one unassociated and the other associated with non-default mouse display.
mChoreographer.notifyInputDevicesChanged(
@@ -496,7 +525,7 @@
TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -543,7 +572,7 @@
TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -562,7 +591,7 @@
TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// A mouse is connected, and the pointer is shown.
mChoreographer.notifyInputDevicesChanged(
@@ -599,7 +628,7 @@
TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -628,7 +657,7 @@
TEST_F(PointerChoreographerTest, DisabledMouseConnected) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo mouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
// Disable this mouse device.
@@ -641,7 +670,7 @@
TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo mouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
@@ -660,7 +689,7 @@
TEST_F(PointerChoreographerTest, MultipleEnabledAndDisabledMiceConnectionAndRemoval) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo disabledMouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
disabledMouseDeviceInfo.setEnabled(false);
@@ -1008,6 +1037,34 @@
pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
}
+TEST_F(PointerChoreographerTest, StylusHoverEnterFadesMouseOnDisplay) {
+ // Make sure there are PointerControllers for a mouse and a stylus.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+ auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(mousePc->isPointerShown());
+
+ // Start hovering with a stylus. This should fade the mouse cursor.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ ASSERT_FALSE(mousePc->isPointerShown());
+}
+
using StylusFixtureParam =
std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
@@ -1378,7 +1435,7 @@
TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// For a touchpad event without a target display, default viewport should be set for
// the PointerController.
@@ -1394,7 +1451,7 @@
WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
// Set one display as a default touchpad display and create PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1403,7 +1460,7 @@
firstDisplayPc->assertViewportSet(DISPLAY_ID);
// Change default mouse display. Existing PointerController should be removed.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1411,7 +1468,7 @@
}
TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -1423,7 +1480,7 @@
}
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1436,7 +1493,7 @@
}
TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -1456,7 +1513,7 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
// Set one viewport as a default mouse display ID.
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1466,7 +1523,7 @@
// Set another viewport as a default mouse display ID. ui::LogicalDisplayId::INVALID will be
// notified before a touchpad event.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1475,7 +1532,7 @@
TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1505,7 +1562,7 @@
TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1582,7 +1639,7 @@
AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
// Add two displays and set one to default.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Add two devices, one unassociated and the other associated with non-default mouse display.
mChoreographer.notifyInputDevicesChanged(
@@ -1623,7 +1680,7 @@
TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1660,7 +1717,7 @@
TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1680,7 +1737,7 @@
TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1696,7 +1753,7 @@
TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1713,7 +1770,7 @@
TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1730,7 +1787,7 @@
TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1754,7 +1811,7 @@
TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) {
// Make sure there are two PointerControllers on different displays.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -1776,6 +1833,89 @@
firstMousePc->assertPointerIconNotSet();
}
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ pc->setPosition(100, 200);
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy,
+ filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+ testing::Eq(vec2{10.f, 20.f}),
+ testing::Eq(DISPLAY_ID)))
+ .Times(1)
+ .WillOnce(testing::Return(vec2{4, 13}));
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+ pc->assertPosition(104, 213);
+ mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+ WithCursorPosition(104, 213),
+ WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ pc->setPosition(100, 200);
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy,
+ filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+ testing::Eq(vec2{10.f, 20.f}),
+ testing::Eq(DISPLAY_ID)))
+ .Times(1)
+ .WillOnce(testing::Return(vec2{4, 13}));
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+ pc->assertPosition(104, 213);
+ mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+ WithCursorPosition(104, 213),
+ WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility).Times(0);
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+}
+
using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
std::function<void(PointerChoreographer&)>, int32_t /*action*/>;
@@ -1966,10 +2106,7 @@
pc->assertSkipScreenshotFlagNotChanged();
}
-TEST_F_WITH_FLAGS(
- PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Add a first mouse device
@@ -2127,7 +2264,7 @@
// Make sure there are PointerControllers for a mouse and a stylus.
mChoreographer.setStylusPointerIconEnabled(true);
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2162,7 +2299,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) {
// Make sure there are two PointerControllers on different displays.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2216,7 +2353,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Hide the pointer on the display, and then connect the mouse.
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2233,7 +2370,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Hide the pointer on the display.
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2282,7 +2419,7 @@
TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -2309,7 +2446,7 @@
TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// First drawing tablet is added
mChoreographer.notifyInputDevicesChanged(
@@ -2357,7 +2494,7 @@
TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Mouse and drawing tablet connected
mChoreographer.notifyInputDevicesChanged(
@@ -2601,15 +2738,29 @@
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
}
-using PointerChoreographerDisplayTopologyTestFixtureParam =
+class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest {
+protected:
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+ ui::Rotation orientation) {
+ DisplayViewport viewport;
+ viewport.displayId = displayId;
+ viewport.logicalRight = width;
+ viewport.logicalBottom = height;
+ viewport.orientation = orientation;
+ return viewport;
+ }
+};
+
+using PointerChoreographerDisplayTopologyCursorTestFixtureParam =
std::tuple<std::string_view /*name*/, int32_t /*source device*/,
ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
vec2 /*source position*/, vec2 /*hover move X/Y*/,
ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
-class PointerChoreographerDisplayTopologyTestFixture
- : public PointerChoreographerTest,
- public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
+class PointerChoreographerDisplayTopologyCursorTestFixture
+ : public PointerChoreographerDisplayTopologyTests,
+ public testing::WithParamInterface<
+ PointerChoreographerDisplayTopologyCursorTestFixtureParam> {
public:
static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
@@ -2619,13 +2770,6 @@
static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70};
- static constexpr int DENSITY_MEDIUM = 160;
- static constexpr int DENSITY_HIGH = 320;
-
- PointerChoreographerDisplayTopologyTestFixture() {
- com::android::input::flags::connected_displays_cursor(true);
- }
-
protected:
// Note: viewport size is in pixels and offsets in topology are in dp
std::vector<DisplayViewport> mViewports{
@@ -2658,34 +2802,24 @@
{DISPLAY_LEFT_ID, DENSITY_MEDIUM},
{DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM},
{DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}};
-
-private:
- DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
- ui::Rotation orientation) {
- DisplayViewport viewport;
- viewport.displayId = displayId;
- viewport.logicalRight = width;
- viewport.logicalBottom = height;
- viewport.orientation = orientation;
- return viewport;
- }
};
-TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture,
+ PointerChoreographerDisplayTopologyTest) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
destinationDisplay, destinationPosition] = GetParam();
mChoreographer.setDisplayViewports(mViewports);
- mChoreographer.setDefaultMouseDisplayId(
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+ setDefaultMouseDisplayId(DISPLAY_CENTER_ID);
mChoreographer.setDisplayTopology(mTopology);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(pointerControllerType);
- ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- pc->getDisplayId());
+ ASSERT_EQ(DISPLAY_CENTER_ID, pc->getDisplayId());
// Set initial position of the PointerController.
pc->setPosition(initialPosition.x, initialPosition.y);
@@ -2717,92 +2851,320 @@
}
INSTANTIATE_TEST_SUITE_P(
- PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+ PointerChoreographerTest, PointerChoreographerDisplayTopologyCursorTestFixture,
testing::Values(
// Note: Upon viewport transition cursor will be positioned at the boundary of the
// destination, as we drop any unconsumed delta.
- std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(50, 50) /* destination x/y */),
- std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
- ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
- vec2(25, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(75, 75) /* destination x/y */),
- std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(50, 50) /* initial x/y */, vec2(100, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
- vec2(0,
- 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
+ std::make_tuple(
+ "PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(50, 50) /* destination x/y */),
+ std::make_tuple(
+ "UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(25, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(75, 75) /* destination x/y */),
+ std::make_tuple(
+ "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_RIGHT_ID,
+ vec2(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
std::make_tuple(
"TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
vec2(-100, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_LEFT_ID,
vec2(90, 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
- std::make_tuple("TransitionToTopDisplay",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(50, 50) /* initial x/y */,
- vec2(25, -100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
- vec2(50 + 25 - 50,
- 90) /* Bottom edge: (source + delta - offset, height) */),
- std::make_tuple("TransitionToBottomDisplay",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(50, 50) /* initial x/y */,
- vec2(25, 100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
- vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+ std::make_tuple(
+ "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, -100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_TOP_ID,
+ vec2(50 + 25 - 50,
+ 90) /* Bottom edge: (source + delta - offset, height) */),
+ std::make_tuple(
+ "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, 100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_BOTTOM_ID,
+ vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
// move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID
- std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(35, 0) /* Top edge */),
- std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(99, 5) /* Top edge */),
- std::make_tuple("NoTransitionAtBottomOffset",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(5, 95) /* initial x/y */,
- vec2(0, 100) /* Move Down */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(5, 99) /* Bottom edge */),
- std::make_tuple("NoTransitionAtLeftOffset",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(5, 5) /* initial x/y */,
- vec2(-100, 0) /* Move Left */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(0, 5) /* Left edge */),
std::make_tuple(
- "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
- vec2(10, -10) /* Move diagonally to top right corner */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
- vec2(0, 90) /* bottom left corner */),
+ "NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(35, 50) /* initial x/y */,
+ vec2(0, -100) /* Move Up */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(35, 0) /* Top edge */),
std::make_tuple(
- "TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
- vec2(0, -50) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID,
- /* Bottom edge: ((source + delta - offset) * density, height) */
- vec2((20 + 0 + 75) * 2, 200))),
- [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
- return std::string{std::get<0>(p.param)};
- });
+ "NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(95, 5) /* initial x/y */,
+ vec2(100, 0) /* Move Right */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(99, 5) /* Top edge */),
+ std::make_tuple(
+ "NoTransitionAtBottomOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(5, 95) /* initial x/y */,
+ vec2(0, 100) /* Move Down */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(5, 99) /* Bottom edge */),
+ std::make_tuple(
+ "NoTransitionAtLeftOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(5, 5) /* initial x/y */,
+ vec2(-100, 0) /* Move Left */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(0, 5) /* Left edge */),
+ std::make_tuple("TransitionAtTopRightCorner",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(95, 5) /* initial x/y */,
+ vec2(10, -10) /* Move diagonally to top right corner */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::
+ DISPLAY_TOP_RIGHT_CORNER_ID,
+ vec2(0, 90) /* bottom left corner */),
+ std::make_tuple("TransitionToHighDpDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
+ vec2(0, -50) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::
+ DISPLAY_HIGH_DENSITY_ID,
+ /* Bottom edge: ((source + delta - offset) * density, height) */
+ vec2((20 + 0 + 75) * 2, 200))),
+ [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyCursorTestFixtureParam>&
+ p) { return std::string{std::get<0>(p.param)}; });
+
+class PointerChoreographerDisplayTopologyDefaultMouseDisplayTests
+ : public PointerChoreographerDisplayTopologyTests {
+protected:
+ static constexpr ui::LogicalDisplayId FIRST_DISPLAY_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId THIRD_DISPLAY_ID = ui::LogicalDisplayId{30};
+
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId) {
+ return PointerChoreographerDisplayTopologyTests::createViewport(displayId, /*width=*/100,
+ /*height=*/100,
+ ui::ROTATION_0);
+ }
+
+ void setDisplayTopologyWithDisplays(
+ ui::LogicalDisplayId primaryDisplayId,
+ const std::vector<ui::LogicalDisplayId>& adjacentDisplays = {}) {
+ // Prepare a topology with all display connected from left to right.
+ ui::LogicalDisplayId previousDisplay = primaryDisplayId;
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>
+ topologyGraph;
+ topologyGraph[primaryDisplayId] = {};
+
+ std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
+ displaysDensity[primaryDisplayId] = DENSITY_MEDIUM;
+
+ for (ui::LogicalDisplayId adjacentDisplayId : adjacentDisplays) {
+ topologyGraph[previousDisplay].push_back({.displayId = adjacentDisplayId,
+ .position = DisplayTopologyPosition::RIGHT,
+ .offsetDp = 0.0f});
+ topologyGraph[adjacentDisplayId].push_back({.displayId = previousDisplay,
+ .position = DisplayTopologyPosition::LEFT,
+ .offsetDp = 0.0f});
+
+ displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM;
+ }
+
+ mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity});
+ }
+};
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ UnrelatedTopologyUpdatesDoNotChangeCursorDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // Set first display as primary display and emit mouse event to create PointerController.
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Add another display keeping the primary display unchanged
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Move cursor to second display and add a third display
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+ createViewport(SECOND_DISPLAY_ID),
+ createViewport(THIRD_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, /*adjacentDisplays=*/
+ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Change the primary display to the third display
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/
+ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ PrimaryDisplayIsFallbackOnPointerDisplayRemoved) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // Add two displays and move cursor to the secondary display
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Remove the secondary display
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+ assertPointerControllerRemoved(pc);
+ pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ UsePrimaryDisplayIfAssociatedDisplayIsInTopology) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add two displays
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/SECOND_DISPLAY_ID,
+ /*adjacentDisplays=*/{FIRST_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, FIRST_DISPLAY_ID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ AllowCrossingDisplayEvenWithAssociatedDisplaySet) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add two displays
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Move cursor to the secondary display
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ AddAssociatedDisplayCursorOutsideOfDisplayTopology) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add three displays, with only first and second display in DisplayTopolgoy
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+ createViewport(SECOND_DISPLAY_ID),
+ createViewport(THIRD_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Adds a new mouse associated with third display
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/1, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, THIRD_DISPLAY_ID)}});
+
+ pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(THIRD_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
-TEST_F_WITH_FLAGS(
- PointerChoreographerWindowInfoListenerTest,
- doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerWindowInfoListenerTest,
+ doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed) {
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
{
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
index 767f9cd..43975f0 100644
--- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -21,6 +21,14 @@
static constexpr int32_t MAX_RANDOM_POINTERS = 4;
static constexpr int32_t MAX_RANDOM_DEVICES = 4;
+// The maximum value that we use for the action button field of NotifyMotionArgs. (We allow multiple
+// bits to be set for this since we're just trying to generate a fuzzed event stream that doesn't
+// cause crashes when enum values are converted to Rust — we don't necessarily want it to be valid.)
+//
+// AMOTION_EVENT_BUTTON_STYLUS_SECONDARY should be replaced with whatever AMOTION_EVENT_BUTTON_
+// value is highest if the enum is edited.
+static constexpr int8_t MAX_ACTION_BUTTON_VALUE = (AMOTION_EVENT_BUTTON_STYLUS_SECONDARY << 1) - 1;
+
int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
int actionMasked = fdp.PickValueInArray<int>({
AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
@@ -185,18 +193,16 @@
fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
const nsecs_t readTime = downTime;
const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
+ const int32_t actionButton = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_ACTION_BUTTON_VALUE);
const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action,
- /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
+ POLICY_FLAG_PASS_TO_USER, action, actionButton,
getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties.data(), pointerCoords.data(),
- /*xPrecision=*/0,
- /*yPrecision=*/0, cursorX, cursorY, downTime,
- /*videoFrames=*/{});
+ pointerProperties.data(), pointerCoords.data(), /*xPrecision=*/0,
+ /*yPrecision=*/0, cursorX, cursorY, downTime, /*videoFrames=*/{});
}
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 846260a..9a59039 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -34,6 +34,28 @@
android::EventHubInterface::DEVICE_ADDED,
android::EventHubInterface::DEVICE_REMOVED};
+static const android::InputDeviceClass kInputDeviceClasses[] = {
+ android::InputDeviceClass::KEYBOARD,
+ android::InputDeviceClass::ALPHAKEY,
+ android::InputDeviceClass::TOUCH,
+ android::InputDeviceClass::CURSOR,
+ android::InputDeviceClass::TOUCH_MT,
+ android::InputDeviceClass::DPAD,
+ android::InputDeviceClass::GAMEPAD,
+ android::InputDeviceClass::SWITCH,
+ android::InputDeviceClass::JOYSTICK,
+ android::InputDeviceClass::VIBRATOR,
+ android::InputDeviceClass::MIC,
+ android::InputDeviceClass::EXTERNAL_STYLUS,
+ android::InputDeviceClass::ROTARY_ENCODER,
+ android::InputDeviceClass::SENSOR,
+ android::InputDeviceClass::BATTERY,
+ android::InputDeviceClass::LIGHT,
+ android::InputDeviceClass::TOUCHPAD,
+ android::InputDeviceClass::VIRTUAL,
+ android::InputDeviceClass::EXTERNAL,
+};
+
constexpr size_t kValidCodes[] = {
SYN_REPORT,
ABS_MT_SLOT,
@@ -105,7 +127,13 @@
void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
- return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
+ uint32_t flags = 0;
+ for (auto inputDeviceClass : kInputDeviceClasses) {
+ if (mFdp->ConsumeBool()) {
+ flags |= static_cast<uint32_t>(inputDeviceClass);
+ }
+ }
+ return ftl::Flags<InputDeviceClass>(flags);
}
InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
return mIdentifier;
@@ -332,6 +360,7 @@
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp)
: mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {}
~FuzzInputReaderContext() {}
+ std::string dump() { return "(dump from FuzzInputReaderContext)"; }
void updateGlobalMetaState() override {}
int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
void disableVirtualKeysUntil(nsecs_t time) override {}
@@ -367,8 +396,8 @@
template <class Fdp>
InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) {
InputDeviceIdentifier identifier;
- identifier.name = fdp.ConsumeRandomLengthString(16);
- identifier.location = fdp.ConsumeRandomLengthString(12);
+ identifier.name = fdp.ConsumeRandomLengthUtf8String(16);
+ identifier.location = fdp.ConsumeRandomLengthUtf8String(12);
int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5);
int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5);
return InputDevice(context, deviceID, deviceGeneration, identifier);
diff --git a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
index 2f76f18..b258118 100644
--- a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
+++ b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
@@ -15,7 +15,7 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
-
+#include <algorithm>
/**
* A thread-safe interface to the FuzzedDataProvider
*/
@@ -60,6 +60,44 @@
return FuzzedDataProvider::ConsumeRandomLengthString();
}
+ // Converting the string to a UTF-8 string by setting the prefix bits of each
+ // byte according to UTF-8 encoding rules.
+ std::string ConsumeRandomLengthUtf8String(size_t max_length) {
+ std::scoped_lock _l(mLock);
+ std::string result = FuzzedDataProvider::ConsumeRandomLengthString(max_length);
+ size_t remaining_bytes = result.length(), idx = 0;
+ while (remaining_bytes > 0) {
+ size_t random_byte_size = FuzzedDataProvider::ConsumeIntegralInRange(1, 4);
+ size_t byte_size = std::min(random_byte_size, remaining_bytes);
+ switch (byte_size) {
+ // Prefix byte: 0xxxxxxx
+ case 1:
+ result[idx++] &= 0b01111111;
+ break;
+ // Prefix bytes: 110xxxxx 10xxxxxx
+ case 2:
+ result[idx++] = (result[idx] & 0b00011111) | 0b11000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ // Prefix bytes: 1110xxxx 10xxxxxx 10xxxxxx
+ case 3:
+ result[idx++] = (result[idx] & 0b00001111) | 0b11100000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ // Prefix bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ case 4:
+ result[idx++] = (result[idx] & 0b00000111) | 0b11110000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ }
+ remaining_bytes -= byte_size;
+ }
+ return result;
+ }
+
std::string ConsumeRemainingBytesAsString() {
std::scoped_lock _l(mLock);
return FuzzedDataProvider::ConsumeRemainingBytesAsString();
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 880df08..f38cf5a 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -22,6 +22,7 @@
"android.hardware.sensors-V1-convert",
"android.hardware.sensors-V3-ndk",
"android.hardware.common-V2-ndk",
+ "framework-permission-aidl-cpp",
"libsensor",
"libfakeservicemanager",
"libcutils",
diff --git a/services/sensorservice/sensorservice_flags.aconfig b/services/sensorservice/sensorservice_flags.aconfig
index 452b428..6308973 100644
--- a/services/sensorservice/sensorservice_flags.aconfig
+++ b/services/sensorservice/sensorservice_flags.aconfig
@@ -37,4 +37,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "disable_vndk_forged_name"
+ namespace: "sensors"
+ description: "When this flag is enabled, sensor manager will not use forged name to determine if an access is from VNDK"
+ bug: "398253250"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/services/stats/OWNERS b/services/stats/OWNERS
index a61babf..791b711 100644
--- a/services/stats/OWNERS
+++ b/services/stats/OWNERS
@@ -1,9 +1,7 @@
jeffreyhuang@google.com
joeo@google.com
-jtnguyen@google.com
muhammadq@google.com
ruchirr@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 88ff370..9aa1ffa 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -93,7 +93,6 @@
"iinputflinger_aidl_lib_static",
"libaidlcommonsupport",
"libcompositionengine",
- "libframetimeline",
"libgui_aidl_static",
"libperfetto_client_experimental",
"librenderengine",
@@ -208,6 +207,7 @@
"DisplayDevice.cpp",
"Effects/Daltonizer.cpp",
"FpsReporter.cpp",
+ "FrameTimeline/FrameTimeline.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"FrontEnd/LayerCreationArgs.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index c1b864d..c0243b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -145,7 +145,7 @@
// Applies a HWC device layer lut
virtual void applyDeviceLayerLut(
- ndk::ScopedFileDescriptor,
+ ::android::base::unique_fd,
std::vector<std::pair<
int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 0063eee..efddc85 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -68,7 +68,7 @@
aidl::android::hardware::graphics::composer3::Composition) override;
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
- void applyDeviceLayerLut(ndk::ScopedFileDescriptor,
+ void applyDeviceLayerLut(::android::base::unique_fd,
std::vector<std::pair<int, LutProperties>>) override;
bool needsFiltering() const override;
std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index f934cb2..e42b9b1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -253,7 +253,6 @@
std::unordered_map<size_t, size_t> mFinalLayerCounts;
size_t mCachedSetCreationCount = 0;
size_t mCachedSetCreationCost = 0;
- std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
};
} // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 09c47f0..be36db6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -60,7 +60,7 @@
MOCK_CONST_METHOD0(needsFiltering, bool());
MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
MOCK_METHOD(void, applyDeviceLayerLut,
- (ndk::ScopedFileDescriptor,
+ (::android::base::unique_fd,
(std::vector<std::pair<
int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index d9018bc..dc84195 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -38,7 +38,8 @@
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
lhs.stretchEffect == rhs.stretchEffect &&
- lhs.edgeExtensionEffect == rhs.edgeExtensionEffect;
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
+ lhs.whitePointNits == rhs.whitePointNits;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index e37ce0a..8364f4e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -373,7 +373,7 @@
if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
- layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()),
+ layer->applyDeviceLayerLut(::android::base::unique_fd(mapperIt->second.release()),
lutsIt->second);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 9d67122..ea36011 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -369,8 +369,11 @@
layerFEState->buffer->getPixelFormat()))
: std::nullopt;
- auto hdrRenderType =
- getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+ // prefer querying this from gralloc instead to catch 2094-10 metadata
+ const bool hasHdrMetadata = layerFEState->hdrMetadata.validTypes != 0;
+
+ auto hdrRenderType = getHdrRenderType(outputState.dataspace, pixelFormat,
+ layerFEState->desiredHdrSdrRatio, hasHdrMetadata);
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
@@ -393,8 +396,8 @@
}
// re-get HdrRenderType after the dataspace gets changed.
- hdrRenderType =
- getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+ hdrRenderType = getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio,
+ hasHdrMetadata);
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
@@ -416,12 +419,20 @@
state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
} else {
+ const bool isLayerFp16 = pixelFormat && *pixelFormat == ui::PixelFormat::RGBA_FP16;
float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
// RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
// range that we may need to re-adjust to the current display conditions
+ // Do NOT do this when we may render fp16 to an fp16 client target, to avoid applying
+ // and additional gain to the layer. This is because the fp16 client target should
+ // already be adapted to remap 1.0 to the SDR white point in the panel's luminance
+ // space.
if (hdrRenderType == HdrRenderType::DISPLAY_HDR) {
- layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+ if (!FlagManager::getInstance().fp16_client_target() || !isLayerFp16) {
+ layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+ }
}
+
state.dimmingRatio =
std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
1.f);
@@ -619,7 +630,7 @@
lutProperties[i].samplingKey)}});
}
- luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+ luts.pfd.set(dup(lutFileDescriptor.get()));
luts.offsets = lutOffsets;
luts.lutProperties = std::move(aidlProperties);
}
@@ -1002,7 +1013,7 @@
}
void OutputLayer::applyDeviceLayerLut(
- ndk::ScopedFileDescriptor lutFileDescriptor,
+ ::android::base::unique_fd lutFd,
std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
@@ -1021,9 +1032,9 @@
samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0]));
}
}
- hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()),
- std::move(offsets), std::move(dimensions),
- std::move(sizes), std::move(samplingKeys));
+ hwcState.luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), std::move(offsets),
+ std::move(dimensions), std::move(sizes),
+ std::move(samplingKeys));
}
bool OutputLayer::needsFiltering() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 783209c..2081cd5 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -243,17 +243,9 @@
mCurrentGeometry = hash;
mLastGeometryUpdate = now;
-
- for (const CachedSet& cachedSet : mLayers) {
- if (cachedSet.getLayerCount() > 1) {
- ++mInvalidatedCachedSetAges[cachedSet.getAge()];
- }
- }
-
mLayers.clear();
if (mNewCachedSet) {
- ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
mNewCachedSet = std::nullopt;
}
}
@@ -312,7 +304,6 @@
mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
if (mNewCachedSet->hasBufferUpdate()) {
ALOGV("[%s] Dropping new cached set", __func__);
- ++mInvalidatedCachedSetAges[0];
mNewCachedSet = std::nullopt;
} else if (mNewCachedSet->hasReadyBuffer()) {
ALOGV("[%s] Found ready buffer", __func__);
@@ -325,6 +316,7 @@
priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
OutputLayer::CompositionState& state =
(*incomingLayerIter)->getOutputLayer()->editState();
+
state.overrideInfo = {
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
@@ -338,10 +330,6 @@
};
++incomingLayerIter;
}
-
- if (currentLayerIter->getLayerCount() > 1) {
- ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
- }
++currentLayerIter;
skipCount -= layerCount;
@@ -378,9 +366,9 @@
};
++incomingLayerIter;
}
+ priorBlurLayer = currentLayerIter->getBlurLayer();
} else if (currentLayerIter->getLayerCount() > 1) {
// Break the current layer into its constituent layers
- ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
for (CachedSet& layer : currentLayerIter->decompose()) {
bool disableBlur =
priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
@@ -400,8 +388,8 @@
currentLayerIter->updateAge(now);
merged.emplace_back(*currentLayerIter);
++incomingLayerIter;
+ priorBlurLayer = currentLayerIter->getBlurLayer();
}
- priorBlurLayer = currentLayerIter->getBlurLayer();
++currentLayerIter;
}
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index a086aee..87a677c 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -97,9 +97,7 @@
const bool force = desiredModeOpt->force;
desiredModeOpt = std::move(desiredMode);
desiredModeOpt->emitEvent |= emitEvent;
- if (FlagManager::getInstance().connected_display()) {
- desiredModeOpt->force |= force;
- }
+ desiredModeOpt->force |= force;
return DesiredModeAction::None;
}
@@ -191,7 +189,7 @@
// cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
// consumed at this point, so clear the `force` flag to prevent an endless loop of
// `initiateModeChange`.
- if (FlagManager::getInstance().connected_display()) {
+ {
std::scoped_lock lock(displayPtr->desiredModeLock);
if (displayPtr->desiredModeOpt) {
displayPtr->desiredModeOpt->force = false;
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
index ec3ec52..2e9dc1e 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -26,7 +26,8 @@
struct DisplayModeRequest {
scheduler::FrameRateMode mode;
- // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
+ // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE for a change in refresh rate
+ // or render rate. Ignored for resolution changes, which always emit the event.
bool emitEvent = false;
// Whether to force the request to be applied, even if the mode is unchanged.
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c743ea2..07770f1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -51,6 +51,17 @@
namespace hal = hardware::graphics::composer::hal;
+namespace gui {
+inline std::string_view to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+ switch (optimizationPolicy) {
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
+ return "optimizeForPower";
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
+ return "optimizeForPerformance";
+ }
+}
+} // namespace gui
+
DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -223,9 +234,7 @@
mFlags = flags;
}
-void DisplayDevice::setDisplaySize(int width, int height) {
- LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
- const auto size = ui::Size(width, height);
+void DisplayDevice::setDisplaySize(ui::Size size) {
mCompositionDisplay->setDisplaySize(size);
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setViewport(size);
@@ -285,6 +294,7 @@
dumper.dump("name"sv, '"' + mDisplayName + '"');
dumper.dump("powerMode"sv, mPowerMode);
+ dumper.dump("optimizationPolicy"sv, mOptimizationPolicy);
if (mRefreshRateSelector) {
mRefreshRateSelector->dump(dumper);
@@ -307,6 +317,15 @@
mCompositionDisplay->setSecure(secure);
}
+gui::ISurfaceComposer::OptimizationPolicy DisplayDevice::getOptimizationPolicy() const {
+ return mOptimizationPolicy;
+}
+
+void DisplayDevice::setOptimizationPolicy(
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+ mOptimizationPolicy = optimizationPolicy;
+}
+
const Rect DisplayDevice::getBounds() const {
return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index af2b48f..be37429 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -93,12 +93,17 @@
bool isSecure() const;
void setSecure(bool secure);
+ // The optimization policy influences whether this display is optimized for power or
+ // performance.
+ gui::ISurfaceComposer::OptimizationPolicy getOptimizationPolicy() const;
+ void setOptimizationPolicy(gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
+
int getWidth() const;
int getHeight() const;
ui::Size getSize() const { return {getWidth(), getHeight()}; }
void setLayerFilter(ui::LayerFilter);
- void setDisplaySize(int width, int height);
+ void setDisplaySize(ui::Size);
void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
void persistBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -236,6 +241,9 @@
// TODO(b/182939859): Remove special cases for primary display.
const bool mIsPrimary;
+ gui::ISurfaceComposer::OptimizationPolicy mOptimizationPolicy =
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
+
uint32_t mFlags = 0;
// Requested refresh rate in fps, supported only for virtual displays.
@@ -260,6 +268,7 @@
struct Physical {
PhysicalDisplayId id;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
+ uint8_t port;
DisplayModePtr activeMode;
bool operator==(const Physical& other) const {
@@ -282,6 +291,9 @@
std::string displayName;
std::string uniqueId;
bool isSecure = false;
+
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy =
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
bool isProtected = false;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
Fps requestedRefreshRate;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 252c6b6..8d16a6b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -439,11 +439,8 @@
// FIXME (b/319505580): At least the first config set on an external display must be
// `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints`
// for simplicity.
- const bool connected_display = FlagManager::getInstance().connected_display();
-
if (isVsyncPeriodSwitchSupported() &&
- (!connected_display ||
- getConnectionType().value_opt() != ui::DisplayConnectionType::External)) {
+ getConnectionType().value_opt() != ui::DisplayConnectionType::External) {
Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
@@ -638,7 +635,7 @@
[](int32_t i, LutProperties j) { return std::make_pair(i, j); });
outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties);
lutFileDescriptorMapper.emplace_or_replace(layer.get(),
- ndk::ScopedFileDescriptor(
+ ::android::base::unique_fd(
layerLut.luts.pfd.release()));
} else {
ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found"
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c3deb84..7c1f8e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -115,7 +115,7 @@
using LayerLuts =
ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>;
using LutFileDescriptorMapper =
- ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+ ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, kLutFileDescriptorMapperSize>;
[[nodiscard]] virtual hal::Error acceptChanges() = 0;
[[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c47943f..14088a6 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -145,7 +145,7 @@
case HotplugEvent::Disconnected:
return onHotplugDisconnect(hwcDisplayId);
case HotplugEvent::LinkUnstable:
- return {};
+ return onHotplugLinkTrainingFailure(hwcDisplayId);
}
}
@@ -225,7 +225,11 @@
}
void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId,
- std::optional<ui::Size> physicalSize) {
+ uint8_t port, std::optional<ui::Size> physicalSize) {
+ LOG_ALWAYS_FATAL_IF(!mActivePorts.try_emplace(port).second,
+ "Cannot attach display %" PRIu64 " to an already active port %" PRIu8 ".",
+ hwcDisplayId, port);
+
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
if (!mPrimaryHwcDisplayId) {
@@ -239,6 +243,7 @@
newDisplay->setConnected(true);
newDisplay->setPhysicalSizeInMm(physicalSize);
displayData.hwcDisplay = std::move(newDisplay);
+ displayData.port = port;
}
int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
@@ -758,6 +763,9 @@
const auto hwcDisplayId = displayData.hwcDisplay->getId();
mPhysicalDisplayIdMap.erase(hwcDisplayId);
+ if (const auto port = displayData.port) {
+ mActivePorts.erase(port.value());
+ }
mDisplayData.erase(displayId);
// Reset the primary display ID if we're disconnecting it.
@@ -1060,7 +1068,7 @@
return mSupportedLayerGenericMetadata;
}
-ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, 20>&
HWComposer::getLutFileDescriptorMapper() {
return mLutFileDescriptorMapper;
}
@@ -1123,8 +1131,15 @@
return {};
}
-bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
+bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, uint8_t port,
bool hasDisplayIdentificationData) const {
+ if (mActivePorts.contains(port)) {
+ ALOGE("Ignoring connection of display %" PRIu64 ". Port %" PRIu8
+ " is already in active use.",
+ hwcDisplayId, port);
+ return true;
+ }
+
if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
hwcDisplayId);
@@ -1170,7 +1185,7 @@
mHasMultiDisplaySupport ? "generalized" : "legacy");
}
- if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
+ if (shouldIgnoreHotplugConnect(hwcDisplayId, port, hasDisplayIdentificationData)) {
return {};
}
@@ -1202,7 +1217,7 @@
if (info->preferredDetailedTimingDescriptor) {
size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
}
- allocatePhysicalDisplay(hwcDisplayId, info->id, size);
+ allocatePhysicalDisplay(hwcDisplayId, info->id, info->port, size);
}
return info;
}
@@ -1230,6 +1245,16 @@
return DisplayIdentificationInfo{.id = *displayId};
}
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugLinkTrainingFailure(
+ hal::HWDisplayId hwcDisplayId) {
+ const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+ if (!displayId) {
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
+ return {};
+ }
+ return DisplayIdentificationInfo{.id = *displayId};
+}
+
void HWComposer::loadCapabilities() {
static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed");
auto capabilities = mComposer->getCapabilities();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index d60f6ff..472411c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -28,6 +28,7 @@
#include <ftl/expected.h>
#include <ftl/future.h>
#include <ui/DisplayIdentification.h>
+#include <ui/DisplayMap.h>
#include <ui/FenceTime.h>
#include <ui/PictureProfileHandle.h>
@@ -144,7 +145,7 @@
// supported by the HWC can be queried in advance, but allocation may fail for other reasons.
virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, uint8_t port,
std::optional<ui::Size> physicalSize) = 0;
// Attempts to create a new layer on this display
@@ -362,7 +363,7 @@
bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, uint8_t port,
std::optional<ui::Size> physicalSize) override;
// Attempts to create a new layer on this display
@@ -525,6 +526,7 @@
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
+ std::optional<uint8_t> port; // Set on hotplug for physical displays
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
@@ -542,7 +544,9 @@
std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
- bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ std::optional<DisplayIdentificationInfo> onHotplugLinkTrainingFailure(hal::HWDisplayId);
+ bool shouldIgnoreHotplugConnect(hal::HWDisplayId, uint8_t port,
+ bool hasDisplayIdentificationData) const;
aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi
getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const;
@@ -564,6 +568,7 @@
void loadHdrConversionCapabilities();
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
+ ui::PhysicalDisplaySet<uint8_t> mActivePorts;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
deleted file mode 100644
index 8e28cc3..0000000
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
- default_team: "trendy_team_android_core_graphics_stack",
-}
-
-cc_library_static {
- name: "libframetimeline",
- defaults: ["surfaceflinger_defaults"],
- srcs: [
- "FrameTimeline.cpp",
- ],
- header_libs: [
- "libscheduler_headers",
- ],
- shared_libs: [
- "android.hardware.graphics.composer@2.4",
- "libbase",
- "libcutils",
- "liblog",
- "libgui",
- "libtimestats",
- "libui",
- "libutils",
- ],
- static_libs: [
- "libperfetto_client_experimental",
- "libsurfaceflinger_common",
- ],
- export_include_dirs: ["."],
-}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 839bd79..964a970 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -41,7 +41,7 @@
if (forceFullDamage) {
outSurfaceDamageRegion = Region::INVALID_REGION;
} else {
- outSurfaceDamageRegion = requested.surfaceDamageRegion;
+ outSurfaceDamageRegion = requested.getSurfaceDamageRegion();
}
}
@@ -305,7 +305,11 @@
out << rootId << ",";
}
}
- out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
+ out << "] ";
+ if (obj.isSecure) {
+ out << "(Secure) ";
+ }
+ out << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
<< " reason=" << obj.getIsVisibleReason();
if (!obj.geomLayerBounds.isEmpty()) {
@@ -372,7 +376,7 @@
updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
- transparentRegionHint = requested.transparentRegion;
+ transparentRegionHint = requested.getTransparentRegion();
}
if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
layerOpaqueFlagSet =
@@ -444,15 +448,7 @@
}
if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
- if (requested.windowInfoHandle) {
- inputInfo = *requested.windowInfoHandle->getInfo();
- } else {
- inputInfo = {};
- // b/271132344 revisit this and see if we can always use the layers uid/pid
- inputInfo.name = requested.name;
- inputInfo.ownerUid = requested.ownerUid;
- inputInfo.ownerPid = requested.ownerPid;
- }
+ inputInfo = requested.getWindowInfo();
inputInfo.id = static_cast<int32_t>(uniqueSequence);
touchCropId = requested.touchCropId;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 86ef6ca..28a6031 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -1090,7 +1090,7 @@
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
const Rect geomLayerBoundsWithoutTransparentRegion =
RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
- requested.transparentRegion);
+ requested.getTransparentRegion());
snapshot.transformedBoundsWithoutTransparentRegion =
snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
snapshot.parentTransform = parentSnapshot.geomLayerTransform;
@@ -1098,7 +1098,7 @@
if (requested.potentialCursor) {
// Subtract the transparent region and snap to the bounds
const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
- requested.transparentRegion);
+ requested.getTransparentRegion());
snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
}
}
@@ -1132,22 +1132,14 @@
const Args& args) {
using InputConfig = gui::WindowInfo::InputConfig;
- if (requested.windowInfoHandle) {
- snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
- } else {
- snapshot.inputInfo = {};
- // b/271132344 revisit this and see if we can always use the layers uid/pid
- snapshot.inputInfo.name = requested.name;
- snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid};
- snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid};
- }
+ snapshot.inputInfo = requested.getWindowInfo();
snapshot.touchCropId = requested.touchCropId;
snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
snapshot.inputInfo.displayId =
ui::LogicalDisplayId{static_cast<int32_t>(snapshot.outputFilter.layerStack.id)};
snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
- ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+ ? requested.getWindowInfo().touchOcclusionMode
: parentSnapshot.inputInfo.touchOcclusionMode;
snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation ||
(requested.flags & layer_state_t::eCanOccludePresentation);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 58c235e..d322a61 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -63,8 +63,11 @@
metadata.merge(args.metadata);
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
- // TODO: b/305254099 remove once we don't pass invisible windows to input
- windowInfoHandle = nullptr;
+ // b/271132344 revisit this and see if we can always use the layers uid/pid
+ auto* windowInfo = editWindowInfo();
+ windowInfo->name = name;
+ windowInfo->ownerPid = ownerPid;
+ windowInfo->ownerUid = ownerUid;
if (parentId != UNASSIGNED_LAYER_ID) {
canBeRoot = false;
}
@@ -105,7 +108,7 @@
currentHdrSdrRatio = 1.f;
dataspaceRequested = false;
hdrMetadata.validTypes = 0;
- surfaceDamageRegion = Region::INVALID_REGION;
+ mNotDefCmpState.surfaceDamageRegion = Region::INVALID_REGION;
cornerRadius = 0.0f;
clientDrawnCornerRadius = 0.0f;
backgroundBlurRadius = 0;
@@ -278,7 +281,7 @@
if (clientState.what & layer_state_t::eReparent) {
changes |= RequestedLayerState::Changes::Parent;
parentId = resolvedComposerState.parentId;
- parentSurfaceControlForChild = nullptr;
+ mNotDefCmpState.parentSurfaceControlForChild = nullptr;
// Once a layer has be reparented, it cannot be placed at the root. It sounds odd
// but thats the existing logic and until we make this behavior more explicit, we need
// to maintain this logic.
@@ -288,7 +291,7 @@
changes |= RequestedLayerState::Changes::RelativeParent;
relativeParentId = resolvedComposerState.relativeParentId;
isRelativeOf = true;
- relativeLayerSurfaceControl = nullptr;
+ mNotDefCmpState.relativeLayerSurfaceControl = nullptr;
}
if ((clientState.what & layer_state_t::eLayerChanged ||
(clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
@@ -304,7 +307,7 @@
}
if (clientState.what & layer_state_t::eInputInfoChanged) {
touchCropId = resolvedComposerState.touchCropId;
- windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
+ editWindowInfo()->touchableRegionCropHandle.clear();
}
if (clientState.what & layer_state_t::eStretchChanged) {
stretchEffect.sanitize();
@@ -554,12 +557,9 @@
}
bool RequestedLayerState::hasInputInfo() const {
- if (!windowInfoHandle) {
- return false;
- }
- const auto windowInfo = windowInfoHandle->getInfo();
- return windowInfo->token != nullptr ||
- windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ const auto& windowInfo = getWindowInfo();
+ return windowInfo.token != nullptr ||
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
bool RequestedLayerState::needsInputInfo() const {
@@ -571,13 +571,9 @@
return true;
}
- if (!windowInfoHandle) {
- return false;
- }
-
- const auto windowInfo = windowInfoHandle->getInfo();
- return windowInfo->token != nullptr ||
- windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ const auto& windowInfo = getWindowInfo();
+ return windowInfo.token != nullptr ||
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
bool RequestedLayerState::hasBufferOrSidebandStream() const {
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 614f33f..758b111 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -19,11 +19,11 @@
#include <android-base/thread_annotations.h>
#include <android/gui/IHdrLayerInfoListener.h>
#include <binder/IBinder.h>
+#include <ui/RingBuffer.h>
#include <utils/Timers.h>
#include <unordered_map>
-#include "Utils/RingBuffer.h"
#include "WpHash.h"
namespace android {
@@ -102,7 +102,7 @@
EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); }
};
- utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
+ ui::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Jank/JankTracker.cpp b/services/surfaceflinger/Jank/JankTracker.cpp
index 8e0e084..5e6267d 100644
--- a/services/surfaceflinger/Jank/JankTracker.cpp
+++ b/services/surfaceflinger/Jank/JankTracker.cpp
@@ -88,7 +88,8 @@
}
void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) {
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
// Undo the duplicate increment in addJankListener.
sListenerCount--;
@@ -106,7 +107,8 @@
std::vector<sp<IBinder>> toSend;
mLock.lock();
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second;) {
if (!jankData.empty()) {
toSend.emplace_back(it->second.mListener);
}
@@ -133,7 +135,8 @@
void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener,
int64_t afterVysnc) {
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc);
return;
@@ -156,7 +159,8 @@
void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) {
const std::lock_guard<std::mutex> _l(mLock);
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
mJankListeners.erase(it);
sListenerCount--;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 195461f..95a7170 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -64,7 +64,7 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
@@ -362,7 +362,7 @@
// transaction
// ----------------------------------------------------------------------------
-void Layer::commitTransaction() {
+void Layer::commitTransaction() REQUIRES(mFlinger->mStateLock) {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -394,7 +394,8 @@
};
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime, gui::GameMode gameMode) {
+ nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
mDrawingState.postTime = postTime;
// Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
@@ -458,7 +459,7 @@
void Layer::addSurfaceFramePresentedForBuffer(
std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
- nsecs_t currentLatchTime) {
+ nsecs_t currentLatchTime) REQUIRES(mFlinger->mStateLock) {
surfaceFrame->setAcquireFenceTime(acquireFenceTime);
surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -466,7 +467,8 @@
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) {
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
@@ -488,7 +490,7 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
- gui::GameMode gameMode) {
+ gui::GameMode gameMode) REQUIRES(mFlinger->mStateLock) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
@@ -506,7 +508,8 @@
}
void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName, gui::GameMode gameMode) {
+ std::string debugName, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return;
}
@@ -842,7 +845,7 @@
return true;
}
-void Layer::releasePreviousBuffer() {
+void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) {
mReleasePreviousBuffer = true;
if (!mBufferInfo.mBuffer ||
(!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
@@ -884,7 +887,8 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) {
+ bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
@@ -1074,7 +1078,8 @@
}
bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
- nsecs_t postTime, gui::GameMode gameMode) {
+ nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
if (mDrawingState.sidebandStream == sidebandStream) return false;
if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -1207,7 +1212,7 @@
return false;
}
-void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) REQUIRES(mFlinger->mStateLock) {
const State& s(getDrawingState());
if (!s.buffer) {
@@ -1457,7 +1462,8 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
-bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly)
+ REQUIRES(mFlinger->mStateLock) {
SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 6af0f59..081bb22 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -288,7 +288,7 @@
bool leaveState);
inline bool hasTrustedPresentationListener() {
- return mTrustedPresentationListener.callbackInterface != nullptr;
+ return mTrustedPresentationListener.getCallback() != nullptr;
}
// Sets the masked bits.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 44cd319..84b1a73 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -447,7 +447,7 @@
}
layerInfo->set_type("Layer");
- LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+ LayerProtoHelper::writeToProto(requestedState.getTransparentRegion(),
[&]() { return layerInfo->mutable_transparent_region(); });
layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index cd7210c..788448d 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -515,7 +515,7 @@
}
void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
- mExpectedPresentTimes.append(expectedPresentTime);
+ mExpectedPresentTimes.next() = expectedPresentTime;
}
void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
@@ -532,7 +532,7 @@
}
void PowerAdvisor::setCommitStart(TimePoint commitStartTime) {
- mCommitStartTimes.append(commitStartTime);
+ mCommitStartTimes.next() = commitStartTime;
}
void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) {
@@ -579,7 +579,7 @@
}
// Tracks when we finish presenting to hwc
- TimePoint estimatedHwcEndTime = mCommitStartTimes[0];
+ TimePoint estimatedHwcEndTime = mCommitStartTimes.back();
// How long we spent this frame not doing anything, waiting for fences or vsync
Duration idleDuration = 0ns;
@@ -643,13 +643,13 @@
// Also add the frame delay duration since the target did not move while we were delayed
Duration totalDuration = mFrameDelayDuration +
std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
- mCommitStartTimes[0];
+ mCommitStartTimes.back();
Duration totalDurationWithoutGpu =
- mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0];
+ mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes.back();
// We finish SurfaceFlinger when post-composition finishes, so add that in here
Duration flingerDuration =
- estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+ estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes.back();
Duration estimatedGpuDuration = firstGpuTimeline.has_value()
? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime
: Duration::fromNs(0);
@@ -661,7 +661,7 @@
hal::WorkDuration duration{
.timeStampNanos = TimePoint::now().ns(),
.durationNanos = combinedDuration.ns(),
- .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes.back().ns(),
.cpuDurationNanos = supportsGpuReporting() ? cpuDuration.ns() : 0,
.gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0,
};
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 540a9df..b97160a 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -23,6 +23,7 @@
#include <ui/DisplayId.h>
#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
#include <utils/Mutex.h>
// FMQ library in IPower does questionable conversions
@@ -247,27 +248,6 @@
std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime);
};
- template <class T, size_t N>
- class RingBuffer {
- std::array<T, N> elements = {};
- size_t mIndex = 0;
- size_t numElements = 0;
-
- public:
- void append(T item) {
- mIndex = (mIndex + 1) % N;
- numElements = std::min(N, numElements + 1);
- elements[mIndex] = item;
- }
- bool isFull() const { return numElements == N; }
- // Allows access like [0] == current, [-1] = previous, etc..
- T& operator[](int offset) {
- size_t positiveOffset =
- static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
- return elements[(mIndex + positiveOffset) % N];
- }
- };
-
// Filter and sort the display ids by a given property
std::vector<DisplayId> getOrderedDisplayIds(
std::optional<TimePoint> DisplayTimingData::*sortBy);
@@ -287,9 +267,9 @@
// Last frame's post-composition duration
Duration mLastPostcompDuration{0ns};
// Buffer of recent commit start times
- RingBuffer<TimePoint, 2> mCommitStartTimes;
+ ui::RingBuffer<TimePoint, 2> mCommitStartTimes;
// Buffer of recent expected present times
- RingBuffer<TimePoint, 2> mExpectedPresentTimes;
+ ui::RingBuffer<TimePoint, 2> mExpectedPresentTimes;
// Most recent present fence time, provided by SF after composition engine finishes presenting
TimePoint mLastPresentFenceTime;
// Most recent composition engine present end time, returned with the present fence from SF
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 1c4a11a..514adac 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -356,12 +356,10 @@
screenshotArgs.seamlessTransition = false;
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- auto displayState =
- mFlinger.getSnapshotsFromMainThread(screenshotArgs, getLayerSnapshotsFn, layers);
- FenceResult fenceResult =
- mFlinger.captureScreenshot(screenshotArgs, buffer, kRegionSampling, kGrayscale,
- kIsProtected, nullptr, displayState, layers)
- .get();
+ mFlinger.getSnapshotsFromMainThread(screenshotArgs, getLayerSnapshotsFn, layers);
+ FenceResult fenceResult = mFlinger.captureScreenshot(screenshotArgs, buffer, kRegionSampling,
+ kGrayscale, kIsProtected, nullptr, layers)
+ .get();
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 0efc396..c37b965 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,7 +45,7 @@
#include <common/FlagManager.h>
#include <scheduler/FrameRateMode.h>
#include <scheduler/VsyncConfig.h>
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "VSyncDispatch.h"
#include "EventThread.h"
@@ -86,36 +86,43 @@
std::string toString(const DisplayEventReceiver::Event& event) {
switch (event.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
return StringPrintf("Hotplug{displayId=%s, %s}",
to_string(event.header.displayId).c_str(),
event.hotplug.connected ? "connected" : "disconnected");
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ case DisplayEventType::DISPLAY_EVENT_VSYNC:
return StringPrintf("VSync{displayId=%s, count=%u, expectedPresentationTime=%" PRId64
"}",
to_string(event.header.displayId).c_str(), event.vsync.count,
event.vsync.vsyncData.preferredExpectedPresentationTime());
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE:
return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(), event.modeChange.modeId);
- case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}",
to_string(event.header.displayId).c_str(),
event.hdcpLevelsChange.connectedLevel,
event.hdcpLevelsChange.maxLevel);
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(),
event.modeRejection.modeId);
- default:
- return "Event{}";
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ return StringPrintf("FrameRateOverride{displayId=%s, frameRateHz=%f}",
+ to_string(event.header.displayId).c_str(),
+ event.frameRateOverride.frameRateHz);
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ return StringPrintf("FrameRateOverrideFlush{displayId=%s}",
+ to_string(event.header.displayId).c_str());
+ case DisplayEventType::DISPLAY_EVENT_NULL:
+ return "NULL";
}
}
DisplayEventReceiver::Event makeHotplug(PhysicalDisplayId displayId, nsecs_t timestamp,
bool connected) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
event.hotplug.connected = connected;
return event;
}
@@ -123,7 +130,7 @@
DisplayEventReceiver::Event makeHotplugError(nsecs_t timestamp, int32_t connectionError) {
DisplayEventReceiver::Event event;
PhysicalDisplayId unusedDisplayId;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, unusedDisplayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_HOTPLUG, unusedDisplayId, timestamp};
event.hotplug.connected = false;
event.hotplug.connectionError = connectionError;
return event;
@@ -133,7 +140,7 @@
uint32_t count, nsecs_t expectedPresentationTime,
nsecs_t deadlineTimestamp) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
event.vsync.vsyncData.preferredFrameTimelineIndex = 0;
// Temporarily store the current vsync information in frameTimelines[0], marked as
@@ -148,7 +155,7 @@
DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ event.header = {DisplayEventType::DISPLAY_EVENT_MODE_CHANGE,
mode.modePtr->getPhysicalDisplayId(), systemTime()};
event.modeChange.modeId = ftl::to_underlying(mode.modePtr->getId());
event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
@@ -160,7 +167,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+ .type = DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -171,7 +178,7 @@
DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId displayId) {
return DisplayEventReceiver::Event{
.header = DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
+ .type = DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
.displayId = displayId,
.timestamp = systemTime(),
}};
@@ -182,7 +189,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
+ .type = DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -195,7 +202,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+ .type = DisplayEventType::DISPLAY_EVENT_MODE_REJECTION,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -263,10 +270,10 @@
return size < 0 ? status_t(size) : status_t(NO_ERROR);
};
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
- event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
+ event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
mPendingEvents.emplace_back(event);
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
return status_t(NO_ERROR);
}
@@ -524,7 +531,7 @@
event = mPendingEvents.front();
mPendingEvents.pop_front();
- if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+ if (event->header.type == DisplayEventType::DISPLAY_EVENT_HOTPLUG) {
if (event->hotplug.connectionError == 0) {
if (event->hotplug.connected && !mVSyncState) {
mVSyncState.emplace();
@@ -636,18 +643,21 @@
};
switch (event.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
return true;
- case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
return true;
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
+ case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE: {
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::modeChanged);
}
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
+ return true;
+
+ case DisplayEventType::DISPLAY_EVENT_VSYNC:
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
@@ -673,13 +683,12 @@
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
[[fallthrough]];
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::frameRateOverride);
-
- default:
+ case DisplayEventType::DISPLAY_EVENT_NULL:
return false;
}
}
@@ -758,7 +767,7 @@
ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) {
const Period frameInterval = mCallback.getVsyncPeriod(consumer->mOwnerUid);
copy.vsync.vsyncData.frameInterval = frameInterval.ns();
generateFrameTimeline(copy.vsync.vsyncData, frameInterval.ns(), copy.header.timestamp,
@@ -793,7 +802,7 @@
for (auto uid : uidsPostedQueuedBuffers) {
mBufferStuffedUids.erase(uid);
}
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
mLastCommittedVsyncTime =
TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 18bf416..a91dde7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -144,7 +144,7 @@
// An elevated number of queued buffers in the server is detected. This propagates a
// flag to Choreographer indicating that buffer stuffing recovery should begin.
- virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
+ virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) = 0;
};
struct IEventThreadCallback {
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 2e1f938..91a798e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -24,7 +24,7 @@
#include <scheduler/interface/ICompositor.h>
#include "EventThread.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "MessageQueue.h"
namespace android::impl {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4da76f6..e587178 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -986,7 +986,7 @@
if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
const Display& pacesetter = *pacesetterOpt;
- if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ if (params.toggleIdleTimer) {
pacesetter.selectorPtr->setIdleTimerCallbacks(
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
@@ -1018,7 +1018,7 @@
}
void Scheduler::demotePacesetterDisplay(PromotionParams params) {
- if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ if (params.toggleIdleTimer) {
// No need to lock for reads on kMainThreadContext.
if (const auto pacesetterPtr =
FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3fdddac..81389e7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -386,7 +386,7 @@
// a deadlock where the main thread joins with the timer thread as the timer thread waits to
// lock a mutex held by the main thread.
struct PromotionParams {
- // Whether to stop and start the idle timer. Ignored unless connected_display flag is set.
+ // Whether to stop and start the idle timer.
bool toggleIdleTimer;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ff360b7..bb04d12 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -206,7 +206,12 @@
// Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(idealPeriod());
- auto const currentPeriod = it->second.slope;
+ // Calculated slope over the period of time can become outdated as the new timestamps are
+ // stored. Using idealPeriod instead provides a rate which is valid at all the times.
+ auto const currentPeriod =
+ mDisplayModePtr->getVrrConfig() && FlagManager::getInstance().vsync_predictor_recovery()
+ ? idealPeriod()
+ : it->second.slope;
// The mean of the ordinals must be precise for the intercept calculation, so scale them up for
// fixed-point arithmetic.
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
index f2be316..4dd3ab6 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -33,6 +33,10 @@
}
bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+
+ bool matchesResolution(const FrameRateMode& other) const {
+ return modePtr->getResolution() == other.modePtr->getResolution();
+ }
};
inline std::string to_string(const FrameRateMode& mode) {
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 813d4de..ff461d2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -24,6 +24,7 @@
#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
#include <scheduler/Features.h>
#include <scheduler/FrameTime.h>
@@ -34,7 +35,6 @@
// TODO(b/185536303): Pull to FTL.
#include "../../../TracedOrdinal.h"
#include "../../../Utils/Dumper.h"
-#include "../../../Utils/RingBuffer.h"
namespace android::scheduler {
@@ -108,7 +108,7 @@
std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
Period vsyncPeriod, Period minFramePeriod) const;
std::array<PresentFence, 2> mPresentFencesLegacy;
- utils::RingBuffer<PresentFence, 5> mPresentFences;
+ ui::RingBuffer<PresentFence, 5> mPresentFences;
FrameTime mLastSignaledFrameTime;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 7123905..af6d4d3 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -209,6 +209,7 @@
}
compositionengine::LayerFE::LayerSettings fillLayer;
+ fillLayer.name = "ScreenCaptureFillLayer";
fillLayer.source.buffer.buffer = nullptr;
fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f);
fillLayer.geometry.boundaries =
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1163390..9d759df 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -384,6 +384,7 @@
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
int64_t SurfaceFlinger::minAcquiredBuffers = 1;
+std::optional<int64_t> SurfaceFlinger::maxAcquiredBuffersOpt;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::useContextPriority;
@@ -452,6 +453,7 @@
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
minAcquiredBuffers =
SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
+ maxAcquiredBuffersOpt = SurfaceFlingerProperties::max_acquired_buffers();
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -576,9 +578,10 @@
mScheduler->run();
}
-sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure,
- const std::string& uniqueId,
- float requestedRefreshRate) {
+sp<IBinder> SurfaceFlinger::createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+ float requestedRefreshRate) {
// SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
// This is to ensure that only root, system, and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
@@ -611,6 +614,7 @@
// Set display as protected when marked as secure to ensure no behavior change
// TODO (b/314820005): separate as a different arg when creating the display.
state.isProtected = isSecure;
+ state.optimizationPolicy = optimizationPolicy;
state.displayName = displayName;
state.uniqueId = uniqueId;
state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
@@ -1009,9 +1013,8 @@
mPowerAdvisor->init();
if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Can't set SCHED_OTHER for primeCache");
- }
+ constexpr const char* kWhence = "primeCache";
+ setSchedFifo(false, kWhence);
mRenderEnginePrimeCacheFuture.callOnce([this] {
renderengine::PrimeCacheConfig config;
@@ -1047,9 +1050,7 @@
return getRenderEngine().primeCache(config);
});
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Can't set SCHED_FIFO after primeCache");
- }
+ setSchedFifo(true, kWhence);
}
// Avoid blocking the main thread on `init` to set properties.
@@ -1363,7 +1364,8 @@
const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
if (!selectorPtr) break;
- const Fps renderRate = selectorPtr->getActiveMode().fps;
+ const auto activeMode = selectorPtr->getActiveMode();
+ const Fps renderRate = activeMode.fps;
// DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
@@ -1382,6 +1384,15 @@
mScheduler->updatePhaseConfiguration(displayId, mode.fps);
mScheduler->setModeChangePending(true);
+
+ // The mode set to switch resolution is not initiated until the display transaction that
+ // resizes the display. DM sends this transaction in response to a mode change event, so
+ // emit the event now, not when finalizing the mode change as for a refresh rate switch.
+ if (FlagManager::getInstance().synced_resolution_switch() &&
+ !mode.matchesResolution(activeMode)) {
+ mScheduler->onDisplayModeChanged(displayId, mode,
+ /*clearContentRequirements*/ true);
+ }
break;
}
case DesiredModeAction::InitiateRenderRateSwitch:
@@ -1460,19 +1471,25 @@
}
const auto& activeMode = pendingModeOpt->mode;
+ const bool resolutionMatch = !FlagManager::getInstance().synced_resolution_switch() ||
+ activeMode.matchesResolution(mDisplayModeController.getActiveMode(displayId));
- if (const auto oldResolution =
- mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
- oldResolution != activeMode.modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
- // We need to generate new sequenceId in order to recreate the display (and this
- // way the framebuffer).
- state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = activeMode.modePtr.get();
- processDisplayChangesLocked();
+ if (!FlagManager::getInstance().synced_resolution_switch()) {
+ if (const auto oldResolution =
+ mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
+ oldResolution != activeMode.modePtr->getResolution()) {
+ auto& state =
+ mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
+ // We need to generate new sequenceId in order to recreate the display (and this
+ // way the framebuffer).
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = activeMode.modePtr.get();
+ processDisplayChangesLocked();
- // The DisplayDevice has been destroyed, so abort the commit for the now dead FrameTargeter.
- return false;
+ // The DisplayDevice has been destroyed, so abort the commit for the now dead
+ // FrameTargeter.
+ return false;
+ }
}
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
@@ -1480,7 +1497,8 @@
mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
- if (pendingModeOpt->emitEvent) {
+ // Skip for resolution changes, since the event was already emitted on setting the desired mode.
+ if (resolutionMatch && pendingModeOpt->emitEvent) {
mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
}
@@ -1532,8 +1550,9 @@
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(displayId).c_str());
- if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
- mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ const auto activeMode = mDisplayModeController.getActiveMode(displayId);
+
+ if (!desiredModeOpt->force && desiredModeOpt->mode == activeMode) {
applyActiveMode(displayId);
continue;
}
@@ -1554,6 +1573,15 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
+ // When initiating a resolution change, wait until the commit that resizes the display.
+ if (FlagManager::getInstance().synced_resolution_switch() &&
+ !activeMode.matchesResolution(desiredModeOpt->mode)) {
+ const auto display = getDisplayDeviceLocked(displayId);
+ if (display->getSize() != desiredModeOpt->mode.modePtr->getResolution()) {
+ continue;
+ }
+ }
+
const auto error =
mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
constraints, outTimeline);
@@ -2259,8 +2287,7 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
- vsyncPeriod.has_value()) {
+ if (timestamp < 0 && vsyncPeriod.has_value()) {
if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
const int32_t value = static_cast<int32_t>(-timestamp);
// one byte is good enough to encode android.hardware.drm.HdcpLevel
@@ -2312,9 +2339,19 @@
return;
}
- if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
- !FlagManager::getInstance().display_config_error_hal()) {
- return;
+ if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+ if (!FlagManager::getInstance().display_config_error_hal()) {
+ return;
+ }
+ {
+ std::lock_guard<std::mutex> lock(mHotplugMutex);
+ mPendingHotplugEvents.push_back(
+ HotplugEvent{hwcDisplayId, HWComposer::HotplugEvent::LinkUnstable});
+ }
+ if (mScheduler) {
+ mScheduler->scheduleConfigure();
+ }
+ // do not return to also report the error.
}
// TODO(b/311403559): use enum type instead of int
@@ -2456,7 +2493,8 @@
}
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
- bool flushTransactions, bool& outTransactionsAreEmpty) {
+ bool flushTransactions, bool& outTransactionsAreEmpty)
+ EXCLUDES(mStateLock) {
using Changes = frontend::RequestedLayerState::Changes;
SFTRACE_CALL();
SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Transaction Handling");
@@ -2653,7 +2691,7 @@
}
bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
- const scheduler::FrameTargets& frameTargets) {
+ const scheduler::FrameTargets& frameTargets) EXCLUDES(mStateLock) {
const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
@@ -2828,18 +2866,20 @@
// Tracks layer stacks of displays that are added to CompositionEngine output.
ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks;
- auto isOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
- if (FlagManager::getInstance().reject_dupe_layerstacks() &&
- outputLayerStacks.contains(layerStack)) {
- // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
- // removed
- ALOGD("Existing layer stack ID %d output to another display %" PRIu64
- ", dropping display from outputs",
- layerStack.id, id.value);
- return true;
+ auto isUniqueOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
+ if (FlagManager::getInstance().reject_dupe_layerstacks()) {
+ if (layerStack != ui::INVALID_LAYER_STACK && outputLayerStacks.contains(layerStack)) {
+ // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
+ // removed
+ ALOGD("Existing layer stack ID %d output to another display %" PRIu64
+ ", dropping display from outputs",
+ layerStack.id, id.value);
+ return false;
+ }
}
+
outputLayerStacks.try_emplace(layerStack);
- return false;
+ return true;
};
// Add outputs for physical displays.
@@ -2848,7 +2888,7 @@
if (const auto display = getCompositionDisplayLocked(id)) {
const auto layerStack = physicalDisplayLayerStacks.get(id)->get();
- if (!isOutputLayerStack(display->getId(), layerStack)) {
+ if (isUniqueOutputLayerStack(display->getId(), layerStack)) {
refreshArgs.outputs.push_back(display);
}
}
@@ -2867,7 +2907,7 @@
if (!refreshRate.isValid() ||
mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
- if (!isOutputLayerStack(display->getId(), display->getLayerStack())) {
+ if (isUniqueOutputLayerStack(display->getId(), display->getLayerStack())) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
@@ -3459,13 +3499,7 @@
mTimeStats->setPresentFenceGlobal(pacesetterPresentFenceTime);
for (auto&& [id, presentFence] : presentFences) {
- ftl::FakeGuard guard(mStateLock);
- const bool isInternalDisplay =
- mPhysicalDisplays.get(id).transform(&PhysicalDisplay::isInternal).value_or(false);
-
- if (isInternalDisplay) {
- mScheduler->addPresentFence(id, std::move(presentFence));
- }
+ mScheduler->addPresentFence(id, std::move(presentFence));
}
const bool hasPacesetterDisplay =
@@ -3530,9 +3564,8 @@
std::vector<HWComposer::HWCDisplayMode> hwcModes;
std::optional<hal::HWConfigId> activeModeHwcIdOpt;
- const bool isExternalDisplay = FlagManager::getInstance().connected_display() &&
- getHwComposer().getDisplayConnectionType(displayId) ==
- ui::DisplayConnectionType::External;
+ const bool isExternalDisplay = getHwComposer().getDisplayConnectionType(displayId) ==
+ ui::DisplayConnectionType::External;
int attempt = 0;
constexpr int kMaxAttempts = 3;
@@ -3695,11 +3728,12 @@
const auto displayId = info->id;
const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId,
')');
-
- if (event == HWComposer::HotplugEvent::Connected) {
+ // TODO: b/393126541 - replace if with switch as all cases are handled.
+ if (event == HWComposer::HotplugEvent::Connected ||
+ event == HWComposer::HotplugEvent::LinkUnstable) {
const auto activeModeIdOpt =
processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
- displayString.c_str());
+ displayString.c_str(), event);
if (!activeModeIdOpt) {
mScheduler->dispatchHotplugError(
static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
@@ -3725,7 +3759,7 @@
LOG_ALWAYS_FATAL_IF(!snapshotOpt);
mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config);
- } else {
+ } else { // event == HWComposer::HotplugEvent::Disconnected
// Unregister before destroying the DisplaySnapshot below.
mDisplayModeController.unregisterDisplay(displayId);
@@ -3740,7 +3774,8 @@
std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
hal::HWDisplayId hwcDisplayId,
DisplayIdentificationInfo&& info,
- const char* displayString) {
+ const char* displayString,
+ HWComposer::HotplugEvent event) {
auto [displayModes, activeMode] = loadDisplayModes(displayId);
if (!activeMode) {
ALOGE("Failed to hotplug %s", displayString);
@@ -3753,6 +3788,7 @@
if (const auto displayOpt = mPhysicalDisplays.get(displayId)) {
const auto& display = displayOpt->get();
const auto& snapshot = display.snapshot();
+ const uint8_t port = snapshot.port();
std::optional<DeviceProductInfo> deviceProductInfo;
if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
@@ -3764,16 +3800,19 @@
// Use the cached port via snapshot because we are updating an existing
// display on reconnect.
const auto it =
- mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
- snapshot.port(), snapshot.connectionType(),
- std::move(displayModes), std::move(colorModes),
- std::move(deviceProductInfo));
+ mPhysicalDisplays.try_replace(displayId, display.token(), displayId, port,
+ snapshot.connectionType(), std::move(displayModes),
+ std::move(colorModes), std::move(deviceProductInfo));
auto& state = mCurrentState.displays.editValueFor(it->second.token());
state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
state.physical->activeMode = std::move(activeMode);
+ state.physical->port = port;
ALOGI("Reconnecting %s", displayString);
return activeModeId;
+ } else if (event == HWComposer::HotplugEvent::LinkUnstable) {
+ ALOGE("Failed to reconnect unknown %s", displayString);
+ return std::nullopt;
}
const sp<IBinder> token = sp<BBinder>::make();
@@ -3787,6 +3826,7 @@
DisplayDeviceState state;
state.physical = {.id = displayId,
.hwcDisplayId = hwcDisplayId,
+ .port = info.port,
.activeMode = std::move(activeMode)};
if (mIsHdcpViaNegVsync) {
state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
@@ -3901,6 +3941,7 @@
display->setProjection(state.orientation, state.layerStackSpaceRect,
state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
+ display->setOptimizationPolicy(state.optimizationPolicy);
display->setFlags(state.flags);
return display;
@@ -4025,8 +4066,7 @@
// For an external display, loadDisplayModes already attempted to select the same mode
// as DM, but SF still needs to be updated to match.
// TODO (b/318534874): Let DM decide the initial mode.
- if (const auto& physical = state.physical;
- mScheduler && physical && FlagManager::getInstance().connected_display()) {
+ if (const auto& physical = state.physical; mScheduler && physical) {
const bool isInternalDisplay = mPhysicalDisplays.get(physical->id)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
@@ -4102,7 +4142,7 @@
if (const auto& physical = currentState.physical) {
getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id,
- /*physicalSize=*/std::nullopt);
+ physical->port, /*physicalSize=*/std::nullopt);
}
processDisplayAdded(displayToken, currentState);
@@ -4128,6 +4168,35 @@
if (currentState.flags != drawingState.flags) {
display->setFlags(currentState.flags);
}
+
+ const auto updateDisplaySize = [&]() {
+ if (currentState.width != drawingState.width ||
+ currentState.height != drawingState.height) {
+ const ui::Size resolution = ui::Size(currentState.width, currentState.height);
+
+ // Resize the framebuffer. For a virtual display, always do so. For a physical
+ // display, only do so if it has a pending modeset for the matching resolution.
+ if (!currentState.physical ||
+ (FlagManager::getInstance().synced_resolution_switch() &&
+ mDisplayModeController.getDesiredMode(display->getPhysicalId())
+ .transform([resolution](const auto& request) {
+ return resolution == request.mode.modePtr->getResolution();
+ })
+ .value_or(false))) {
+ display->setDisplaySize(resolution);
+ }
+
+ if (display->getId() == mActiveDisplayId) {
+ onActiveDisplaySizeChanged(*display);
+ }
+ }
+ };
+
+ if (FlagManager::getInstance().synced_resolution_switch()) {
+ // Update display size first, as display projection below depends on it.
+ updateDisplaySize();
+ }
+
if ((currentState.orientation != drawingState.orientation) ||
(currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
@@ -4139,13 +4208,9 @@
ui::Transform::toRotationFlags(display->getOrientation());
}
}
- if (currentState.width != drawingState.width ||
- currentState.height != drawingState.height) {
- display->setDisplaySize(currentState.width, currentState.height);
- if (display->getId() == mActiveDisplayId) {
- onActiveDisplaySizeChanged(*display);
- }
+ if (!FlagManager::getInstance().synced_resolution_switch()) {
+ updateDisplaySize();
}
}
}
@@ -4269,20 +4334,19 @@
std::move(displayInfos),
ftl::to_underlying(vsyncId),
frameTime.ns()},
- std::move(
- inputWindowCommands.windowInfosReportedListeners),
+ std::move(inputWindowCommands.releaseListeners()),
/* forceImmediateCall= */ visibleWindowsChanged ||
- !inputWindowCommands.focusRequests.empty());
+ !inputWindowCommands.getFocusRequests().empty());
} else {
// If there are listeners but no changes to input windows, call the listeners
// immediately.
- for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
+ for (const auto& listener : inputWindowCommands.getListeners()) {
if (IInterface::asBinder(listener)->isBinderAlive()) {
listener->onWindowInfosReported();
}
}
}
- for (const auto& focusRequest : inputWindowCommands.focusRequests) {
+ for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) {
inputFlinger->setFocusedWindow(focusRequest);
}
}});
@@ -4845,12 +4909,14 @@
return applyTransactions(transactions);
}
-bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions)
+ EXCLUDES(mStateLock) {
Mutex::Autolock lock(mStateLock);
return applyTransactionsLocked(transactions);
}
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
+ REQUIRES(mStateLock) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -5007,16 +5073,16 @@
mBufferCountTracker.increment(resolvedState.layerId);
}
if (resolvedState.state.what & layer_state_t::eReparent) {
- resolvedState.parentId =
- getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+ resolvedState.parentId = getLayerIdFromSurfaceControl(
+ resolvedState.state.getParentSurfaceControlForChild());
}
if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
- resolvedState.relativeParentId =
- getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+ resolvedState.relativeParentId = getLayerIdFromSurfaceControl(
+ resolvedState.state.getRelativeLayerSurfaceControl());
}
if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
wp<IBinder>& touchableRegionCropHandle =
- resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+ resolvedState.state.editWindowInfo()->touchableRegionCropHandle;
resolvedState.touchCropId =
LayerHandle::getLayerId(touchableRegionCropHandle.promote());
}
@@ -5067,15 +5133,13 @@
return NO_ERROR;
}
-bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- std::vector<ResolvedComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags,
- const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<uint64_t>& uncacheBufferIds,
- const int64_t postTime, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId) {
+bool SurfaceFlinger::applyTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states,
+ Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock) {
uint32_t transactionFlags = 0;
// start and end registration for listeners w/ no surface so they can get their callback. Note
@@ -5227,7 +5291,7 @@
ResolvedComposerState& composerState,
int64_t desiredPresentTime,
bool isAutoTimestamp, int64_t postTime,
- uint64_t transactionId) {
+ uint64_t transactionId) REQUIRES(mStateLock) {
layer_state_t& s = composerState.state;
std::vector<ListenerCallbacks> filteredListeners;
@@ -5644,16 +5708,11 @@
}
if (displayId == mActiveDisplayId) {
- // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
- // set it before SCHED_FIFO due to b/190237315.
- if (setSchedAttr(true) != NO_ERROR) {
- ALOGW("Failed to set uclamp.min after powering on active display: %s",
- strerror(errno));
- }
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
- strerror(errno));
- }
+ // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall
+ // and set it before SCHED_FIFO due to b/190237315.
+ constexpr const char* kWhence = "setPowerMode(ON)";
+ setSchedAttr(true, kWhence);
+ setSchedFifo(true, kWhence);
}
getHwComposer().setPowerMode(displayId, mode);
@@ -5680,14 +5739,9 @@
if (const auto display = getActivatableDisplay()) {
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
} else {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
- strerror(errno));
- }
- if (setSchedAttr(false) != NO_ERROR) {
- ALOGW("Failed set uclamp.min after powering off active display: %s",
- strerror(errno));
- }
+ constexpr const char* kWhence = "setPowerMode(OFF)";
+ setSchedFifo(false, kWhence);
+ setSchedAttr(false, kWhence);
if (currentModeNotDozeSuspend) {
if (!FlagManager::getInstance().multithreaded_present()) {
@@ -7150,7 +7204,7 @@
return PERMISSION_DENIED;
}
-status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+void SurfaceFlinger::setSchedFifo(bool enabled, const char* whence) {
static constexpr int kFifoPriority = 2;
static constexpr int kOtherPriority = 0;
@@ -7165,19 +7219,19 @@
}
if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
- return -errno;
+ const char* kPolicy[] = {"SCHED_OTHER", "SCHED_FIFO"};
+ ALOGW("%s: Failed to set %s: %s", whence, kPolicy[sched_policy == SCHED_FIFO],
+ strerror(errno));
}
-
- return NO_ERROR;
}
-status_t SurfaceFlinger::setSchedAttr(bool enabled) {
+void SurfaceFlinger::setSchedAttr(bool enabled, const char* whence) {
static const unsigned int kUclampMin =
base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U);
if (!kUclampMin) {
// uclamp.min set to 0 (default), skip setting
- return NO_ERROR;
+ return;
}
sched_attr attr = {};
@@ -7188,22 +7242,20 @@
attr.sched_util_max = 1024;
if (syscall(__NR_sched_setattr, 0, &attr, 0)) {
- return -errno;
+ const char* kAction[] = {"disable", "enable"};
+ ALOGW("%s: Failed to %s uclamp.min: %s", whence, kAction[enabled], strerror(errno));
}
-
- return NO_ERROR;
}
namespace {
-ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace,
- const compositionengine::impl::OutputCompositionState& state,
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, ui::ColorMode colorMode,
bool capturingHdrLayers, bool hintForSeamlessTransition) {
if (requestedDataspace != ui::Dataspace::UNKNOWN) {
return requestedDataspace;
}
- const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
+ const auto dataspaceForColorMode = ui::pickDataspaceFor(colorMode);
// TODO: Enable once HDR screenshots are ready.
if constexpr (/* DISABLES CODE */ (false)) {
@@ -7515,11 +7567,11 @@
return protectedLayerFound;
}
-// Getting layer snapshots and display should take place on main thread.
-// Accessing display requires mStateLock, and contention for this lock
-// is reduced when grabbed from the main thread, thus also reducing
-// risk of deadlocks.
-std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread(
+// Getting layer snapshots and accessing display state should take place on
+// main thread. Accessing display requires mStateLock, and contention for
+// this lock is reduced when grabbed from the main thread, thus also reducing
+// risk of deadlocks. Returns false if no display is found.
+bool SurfaceFlinger::getSnapshotsFromMainThread(
ScreenshotArgs& args, GetLayerSnapshotsFunction getLayerSnapshotsFn,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
return mScheduler
@@ -7559,8 +7611,8 @@
}
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- auto displayState = getSnapshotsFromMainThread(args, getLayerSnapshotsFn, layers);
- if (!displayState) {
+ bool hasDisplayState = getSnapshotsFromMainThread(args, getLayerSnapshotsFn, layers);
+ if (!hasDisplayState) {
ALOGD("Display state not found");
invokeScreenCaptureError(NO_MEMORY, captureListener);
}
@@ -7637,12 +7689,13 @@
auto futureFence =
captureScreenshot(args, texture, false /* regionSampling */, grayscale, isProtected,
- captureListener, displayState, layers, hdrTexture, gainmapTexture);
+ captureListener, layers, hdrTexture, gainmapTexture);
futureFence.get();
}
-std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getDisplayStateOnMainThread(
- ScreenshotArgs& args) {
+// Returns true if display is found and args was populated with display state
+// data. Otherwise, returns false.
+bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) {
sp<const DisplayDevice> display = nullptr;
{
Mutex::Autolock lock(mStateLock);
@@ -7677,49 +7730,49 @@
}
if (display != nullptr) {
- return std::optional{display->getCompositionDisplay()->getState()};
+ const auto& state = display->getCompositionDisplay()->getState();
+ args.displayBrightnessNits = state.displayBrightnessNits;
+ args.sdrWhitePointNits = state.sdrWhitePointNits;
+ args.renderIntent = state.renderIntent;
+ args.colorMode = state.colorMode;
+ return true;
}
}
- return std::nullopt;
+ return false;
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
- const ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling, bool grayscale, bool isProtected,
const sp<IScreenCaptureListener>& captureListener,
- const std::optional<OutputCompositionState>& displayState,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer,
const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer) {
SFTRACE_CALL();
ScreenCaptureResults captureResults;
-
- float displayBrightnessNits = displayState.value().displayBrightnessNits;
- float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
-
ftl::SharedFuture<FenceResult> renderFuture;
+ float hdrSdrRatio = args.displayBrightnessNits / args.sdrWhitePointNits;
+
if (hdrBuffer && gainmapBuffer) {
ftl::SharedFuture<FenceResult> hdrRenderFuture =
renderScreenImpl(args, hdrBuffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers);
+ captureResults, layers);
captureResults.buffer = buffer->getBuffer();
captureResults.optionalGainMap = gainmapBuffer->getBuffer();
renderFuture =
ftl::Future(std::move(hdrRenderFuture))
- .then([&, displayBrightnessNits, sdrWhitePointNits,
- dataspace = captureResults.capturedDataspace, buffer, hdrBuffer,
- gainmapBuffer](FenceResult fenceResult) -> FenceResult {
+ .then([&, hdrSdrRatio, dataspace = captureResults.capturedDataspace, buffer,
+ hdrBuffer, gainmapBuffer](FenceResult fenceResult) -> FenceResult {
if (!fenceResult.ok()) {
return fenceResult;
}
return getRenderEngine()
.tonemapAndDrawGainmap(hdrBuffer, fenceResult.value()->get(),
- displayBrightnessNits /
- sdrWhitePointNits,
+ hdrSdrRatio,
static_cast<ui::Dataspace>(dataspace),
buffer, gainmapBuffer)
.get();
@@ -7727,17 +7780,16 @@
.share();
} else {
renderFuture = renderScreenImpl(args, buffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers);
+ captureResults, layers);
}
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
return ftl::Future(std::move(renderFuture))
.then([captureListener, captureResults = std::move(captureResults),
- displayBrightnessNits,
- sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult {
+ hdrSdrRatio](FenceResult fenceResult) mutable -> FenceResult {
captureResults.fenceResult = std::move(fenceResult);
- captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits;
+ captureResults.hdrSdrRatio = hdrSdrRatio;
captureListener->onScreenCaptureCompleted(captureResults);
return base::unexpected(NO_ERROR);
})
@@ -7747,9 +7799,8 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- const ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
- const std::optional<OutputCompositionState>& displayState,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
SFTRACE_CALL();
@@ -7763,49 +7814,38 @@
layerFE->mSnapshot->geomLayerTransform.inverse();
}
- auto capturedBuffer = buffer;
-
- auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
- auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
- auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
-
- captureResults.capturedDataspace = args.dataspace;
-
const bool enableLocalTonemapping =
FlagManager::getInstance().local_tonemap_screenshots() && !args.seamlessTransition;
- if (displayState) {
- const auto& state = displayState.value();
- captureResults.capturedDataspace =
- pickBestDataspace(args.dataspace, state, captureResults.capturedHdrLayers,
- args.seamlessTransition);
- sdrWhitePointNits = state.sdrWhitePointNits;
+ captureResults.capturedDataspace =
+ pickBestDataspace(args.dataspace, args.colorMode, captureResults.capturedHdrLayers,
+ args.seamlessTransition);
- if (!captureResults.capturedHdrLayers) {
- displayBrightnessNits = sdrWhitePointNits;
- } else {
- displayBrightnessNits = state.displayBrightnessNits;
- if (!enableLocalTonemapping) {
- // Only clamp the display brightness if this is not a seamless transition.
- // Otherwise for seamless transitions it's important to match the current
- // display state as the buffer will be shown under these same conditions, and we
- // want to avoid any flickers
- if (sdrWhitePointNits > 1.0f && !args.seamlessTransition) {
- // Restrict the amount of HDR "headroom" in the screenshot to avoid
- // over-dimming the SDR portion. 2.0 chosen by experimentation
- constexpr float kMaxScreenshotHeadroom = 2.0f;
- displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
- displayBrightnessNits);
- }
- }
+ // Only clamp the display brightness if this is not a seamless transition.
+ // Otherwise for seamless transitions it's important to match the current
+ // display state as the buffer will be shown under these same conditions, and we
+ // want to avoid any flickers.
+ if (captureResults.capturedHdrLayers) {
+ if (!enableLocalTonemapping && args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) {
+ // Restrict the amount of HDR "headroom" in the screenshot to avoid
+ // over-dimming the SDR portion. 2.0 chosen by experimentation
+ constexpr float kMaxScreenshotHeadroom = 2.0f;
+ // TODO: Aim to update displayBrightnessNits earlier in screenshot
+ // path so ScreenshotArgs can be passed as const
+ args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom,
+ args.displayBrightnessNits);
}
-
- // Screenshots leaving the device should be colorimetric
- if (args.dataspace == ui::Dataspace::UNKNOWN && args.seamlessTransition) {
- renderIntent = state.renderIntent;
- }
+ } else {
+ args.displayBrightnessNits = args.sdrWhitePointNits;
}
+ auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
+ // Screenshots leaving the device should be colorimetric
+ if (args.dataspace == ui::Dataspace::UNKNOWN && args.seamlessTransition) {
+ renderIntent = args.renderIntent;
+ }
+
+ auto capturedBuffer = buffer;
captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
@@ -7815,8 +7855,7 @@
}
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
- sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, layers,
- layerStack, regionSampling, args, renderIntent,
+ grayscale, isProtected, layers, layerStack, regionSampling, args, renderIntent,
enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
@@ -7842,9 +7881,10 @@
if (enableLocalTonemapping) {
// Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr
// sdr ratio via display brightness / sdrWhite nits.
- targetBrightness = sdrWhitePointNits / displayBrightnessNits;
+ targetBrightness = args.sdrWhitePointNits / args.displayBrightnessNits;
} else if (dataspace == ui::Dataspace::BT2020_HLG) {
- const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
+ const float maxBrightnessNits =
+ args.displayBrightnessNits / args.sdrWhitePointNits * 203;
// With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
// will appear way too bright.
if (maxBrightnessNits < 1000.f) {
@@ -7870,8 +7910,8 @@
.buffer = std::move(buffer),
.displayId = args.displayId,
.reqBufferSize = args.reqSize,
- .sdrWhitePointNits = sdrWhitePointNits,
- .displayBrightnessNits = displayBrightnessNits,
+ .sdrWhitePointNits = args.sdrWhitePointNits,
+ .displayBrightnessNits = args.displayBrightnessNits,
.targetBrightness = targetBrightness,
.layerAlpha = layerAlpha,
.regionSampling = regionSampling,
@@ -8202,11 +8242,13 @@
int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate,
std::chrono::nanoseconds presentLatency) {
- auto pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
+ int64_t pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
pipelineDepth++;
}
- return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
+ const int64_t maxAcquiredBuffers =
+ std::min(pipelineDepth - 1, maxAcquiredBuffersOpt.value_or(pipelineDepth - 1));
+ return std::max(minAcquiredBuffers, maxAcquiredBuffers);
}
status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -8324,10 +8366,6 @@
void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel,
int32_t maxLevel) {
- if (!FlagManager::getInstance().connected_display()) {
- return;
- }
-
Mutex::Autolock lock(mStateLock);
const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -8711,16 +8749,16 @@
}
}
-binder::Status SurfaceComposerAIDL::createVirtualDisplay(const std::string& displayName,
- bool isSecure, const std::string& uniqueId,
- float requestedRefreshRate,
- sp<IBinder>* outDisplay) {
+binder::Status SurfaceComposerAIDL::createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+ float requestedRefreshRate, sp<IBinder>* outDisplay) {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
- *outDisplay =
- mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, requestedRefreshRate);
+ *outDisplay = mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy,
+ uniqueId, requestedRefreshRate);
return binder::Status::ok();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 935a2da..39e237b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -211,11 +211,9 @@
SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
- // set main thread scheduling policy
- static status_t setSchedFifo(bool enabled) ANDROID_API;
-
- // set main thread scheduling attributes
- static status_t setSchedAttr(bool enabled);
+ // Set scheduling policy and attributes of main thread.
+ static void setSchedFifo(bool enabled, const char* whence);
+ static void setSchedAttr(bool enabled, const char* whence);
static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
@@ -240,6 +238,11 @@
// ISurfaceComposer.getMaxAcquiredBufferCount().
static int64_t minAcquiredBuffers;
+ // Controls the maximum acquired buffers SurfaceFlinger will suggest via
+ // ISurfaceComposer.getMaxAcquiredBufferCount().
+ // Value is set through ro.surface_flinger.max_acquired_buffers.
+ static std::optional<int64_t> maxAcquiredBuffersOpt;
+
// Controls the maximum width and height in pixels that the graphics pipeline can support for
// GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
static uint32_t maxGraphicsWidth;
@@ -531,6 +534,7 @@
// ISurfaceComposer implementation:
sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
const std::string& uniqueId,
float requestedRefreshRate = 0.0f);
status_t destroyVirtualDisplay(const sp<IBinder>& displayToken);
@@ -896,32 +900,41 @@
// If true, the render result may be used for system animations
// that must preserve the exact colors of the display
bool seamlessTransition{false};
+
+ // Current display brightness of the output composition state
+ float displayBrightnessNits{-1.f};
+
+ // SDR white point of the output composition state
+ float sdrWhitePointNits{-1.f};
+
+ // Current active color mode of the output composition state
+ ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+ // Current active render intent of the output composition state
+ ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
};
- std::optional<OutputCompositionState> getSnapshotsFromMainThread(
- ScreenshotArgs& args, GetLayerSnapshotsFunction getLayerSnapshotsFn,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ bool getSnapshotsFromMainThread(ScreenshotArgs& args,
+ GetLayerSnapshotsFunction getLayerSnapshotsFn,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
void captureScreenCommon(ScreenshotArgs& args, GetLayerSnapshotsFunction, ui::Size bufferSize,
ui::PixelFormat, bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>&);
- std::optional<OutputCompositionState> getDisplayStateOnMainThread(ScreenshotArgs& args)
- REQUIRES(kMainThreadContext);
+ bool getDisplayStateOnMainThread(ScreenshotArgs& args) REQUIRES(kMainThreadContext);
ftl::SharedFuture<FenceResult> captureScreenshot(
- const ScreenshotArgs& args,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
- const std::optional<OutputCompositionState>& displayState,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool regionSampling, bool grayscale, bool isProtected,
+ const sp<IScreenCaptureListener>& captureListener,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer = nullptr,
const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- const ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>&,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>&,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
- const std::optional<OutputCompositionState>& displayState,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
void readPersistentProperties();
@@ -1058,7 +1071,8 @@
// Returns the active mode ID, or nullopt on hotplug failure.
std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId,
DisplayIdentificationInfo&&,
- const char* displayString)
+ const char* displayString,
+ HWComposer::HotplugEvent event)
REQUIRES(mStateLock, kMainThreadContext);
void processHotplugDisconnect(PhysicalDisplayId, const char* displayString)
REQUIRES(mStateLock, kMainThreadContext);
@@ -1555,9 +1569,11 @@
const sp<IBinder>& layerHandle,
sp<gui::IDisplayEventConnection>* outConnection) override;
binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
- binder::Status createVirtualDisplay(const std::string& displayName, bool isSecure,
- const std::string& uniqueId, float requestedRefreshRate,
- sp<IBinder>* outDisplay) override;
+ binder::Status createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
+ const std::string& uniqueId, float requestedRefreshRate,
+ sp<IBinder>* outDisplay) override;
binder::Status destroyVirtualDisplay(const sp<IBinder>& displayToken) override;
binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 2676ca6..3297c16 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -139,7 +139,8 @@
colorProto->set_b(layer.color.b);
}
if (layer.what & layer_state_t::eTransparentRegionChanged) {
- LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+ LayerProtoHelper::writeToProto(layer.getTransparentRegion(),
+ proto.mutable_transparent_region());
}
if (layer.what & layer_state_t::eBufferTransformChanged) {
proto.set_transform(layer.bufferTransform);
@@ -191,33 +192,30 @@
}
if (layer.what & layer_state_t::eInputInfoChanged) {
- if (layer.windowInfoHandle) {
- const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
- perfetto::protos::LayerState_WindowInfo* windowInfoProto =
- proto.mutable_window_info_handle();
- windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
- windowInfoProto->set_layout_params_type(
- static_cast<int32_t>(inputInfo->layoutParamsType));
- windowInfoProto->set_input_config(inputInfo->inputConfig.get());
- LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
- windowInfoProto->mutable_touchable_region());
- windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
- windowInfoProto->set_focusable(
- !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
- windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
- gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
- windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
- perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
- transformProto->set_dsdx(inputInfo->transform.dsdx());
- transformProto->set_dtdx(inputInfo->transform.dtdx());
- transformProto->set_dtdy(inputInfo->transform.dtdy());
- transformProto->set_dsdy(inputInfo->transform.dsdy());
- transformProto->set_tx(inputInfo->transform.tx());
- transformProto->set_ty(inputInfo->transform.ty());
- windowInfoProto->set_replace_touchable_region_with_crop(
- inputInfo->replaceTouchableRegionWithCrop);
- windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
- }
+ const gui::WindowInfo* inputInfo = &layer.getWindowInfo();
+ perfetto::protos::LayerState_WindowInfo* windowInfoProto =
+ proto.mutable_window_info_handle();
+ windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
+ windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->layoutParamsType));
+ windowInfoProto->set_input_config(inputInfo->inputConfig.get());
+ LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+ windowInfoProto->mutable_touchable_region());
+ windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+ windowInfoProto->set_focusable(
+ !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
+ windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
+ windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+ perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
+ transformProto->set_dsdx(inputInfo->transform.dsdx());
+ transformProto->set_dtdx(inputInfo->transform.dtdx());
+ transformProto->set_dtdy(inputInfo->transform.dtdy());
+ transformProto->set_dsdy(inputInfo->transform.dsdy());
+ transformProto->set_tx(inputInfo->transform.tx());
+ transformProto->set_ty(inputInfo->transform.ty());
+ windowInfoProto->set_replace_touchable_region_with_crop(
+ inputInfo->replaceTouchableRegionWithCrop);
+ windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
}
if (layer.what & layer_state_t::eBackgroundColorChanged) {
proto.set_bg_color_alpha(layer.bgColor.a);
@@ -410,7 +408,9 @@
layer.color.b = colorProto.b();
}
if (proto.what() & layer_state_t::eTransparentRegionChanged) {
- LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+ Region transparentRegion;
+ LayerProtoHelper::readFromProto(proto.transparent_region(), transparentRegion);
+ layer.updateTransparentRegion(transparentRegion);
}
if (proto.what() & layer_state_t::eBufferTransformChanged) {
layer.bufferTransform = proto.transform();
@@ -486,7 +486,7 @@
windowInfoProto.replace_touchable_region_with_crop();
resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
- layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+ *layer.editWindowInfo() = inputInfo;
}
if (proto.what() & layer_state_t::eBackgroundColorChanged) {
layer.bgColor.a = proto.bg_color_alpha();
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 5cf4244..84d837c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -102,11 +102,10 @@
QueuedTransactionState transaction = parser.fromProto(entry.transactions(j));
for (auto& resolvedComposerState : transaction.states) {
if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) {
- if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test(
+ if (!resolvedComposerState.state.getWindowInfo().inputConfig.test(
gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
// create a fake token since the FE expects a valid token
- resolvedComposerState.state.windowInfoHandle->editInfo()->token =
- sp<BBinder>::make();
+ resolvedComposerState.state.editWindowInfo()->token = sp<BBinder>::make();
}
}
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b1552e6..8d1b51b 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -128,6 +128,7 @@
DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_ACONFIG_FLAG(vsync_predictor_recovery);
/// Trunk stable readonly flags ///
/// IMPORTANT - please keep alphabetize to reduce merge conflicts
@@ -138,7 +139,6 @@
DUMP_ACONFIG_FLAG(begone_bright_hlg);
DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
DUMP_ACONFIG_FLAG(commit_not_composited);
- DUMP_ACONFIG_FLAG(connected_display);
DUMP_ACONFIG_FLAG(connected_display_hdr);
DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
@@ -171,6 +171,7 @@
DUMP_ACONFIG_FLAG(restore_blur_step);
DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
DUMP_ACONFIG_FLAG(stable_edid_ids);
+ DUMP_ACONFIG_FLAG(synced_resolution_switch);
DUMP_ACONFIG_FLAG(trace_frame_rate_override);
DUMP_ACONFIG_FLAG(true_hdr_screenshots);
DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
@@ -250,7 +251,6 @@
/// Trunk stable readonly flags ///
FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum")
-FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
@@ -295,6 +295,7 @@
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
FLAG_MANAGER_ACONFIG_FLAG(reject_dupe_layerstacks, "");
+FLAG_MANAGER_ACONFIG_FLAG(synced_resolution_switch, "");
/// Trunk stable server (R/W) flags ///
FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
@@ -302,6 +303,7 @@
FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, "");
+FLAG_MANAGER_ACONFIG_FLAG(vsync_predictor_recovery, "");
/// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 073302e..603139e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -63,6 +63,7 @@
bool graphite_renderengine_preview_rollout() const;
bool increase_missed_frame_jank_threshold() const;
bool refresh_rate_overlay_on_external_display() const;
+ bool vsync_predictor_recovery() const;
/// Trunk stable readonly flags ///
/// IMPORTANT - please keep alphabetize to reduce merge conflicts
@@ -73,7 +74,6 @@
bool begone_bright_hlg() const;
bool cache_when_source_crop_layer_only_moved() const;
bool commit_not_composited() const;
- bool connected_display() const;
bool connected_display_hdr() const;
bool correct_dpi_with_display_size() const;
bool deprecate_frame_tracker() const;
@@ -107,6 +107,7 @@
bool restore_blur_step() const;
bool skip_invisible_windows_in_input() const;
bool stable_edid_ids() const;
+ bool synced_resolution_switch() const;
bool trace_frame_rate_override() const;
bool true_hdr_screenshots() const;
bool use_known_refresh_rate_for_fps_consistency() const;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 73dfa9f..4afcd00 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -77,7 +77,7 @@
}
}
-int main(int, char**) {
+int main() {
signal(SIGPIPE, SIG_IGN);
hardware::configureRpcThreadpool(1 /* maxThreads */,
@@ -91,9 +91,7 @@
// Set uclamp.min setting on all threads, maybe an overkill but we want
// to cover important threads like RenderEngine.
- if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
- ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
- }
+ SurfaceFlinger::setSchedAttr(true, __func__);
// The binder threadpool we start will inherit sched policy and priority
// of (this) creating thread. We want the binder thread pool to have
@@ -160,14 +158,8 @@
startDisplayService(); // dependency on SF getting registered above
- if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
- ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
- }
-
- // run surface flinger in this thread
+ SurfaceFlinger::setSchedFifo(true, __func__);
flinger->run();
-
- return 0;
}
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 96ab7ab..d8f51fe 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -265,6 +265,13 @@
} # stable_edid_ids
flag {
+ name: "synced_resolution_switch"
+ namespace: "core_graphics"
+ description: "Synchronize resolution modeset with framebuffer resizing"
+ bug: "355427258"
+} # synced_resolution_switch
+
+flag {
name: "true_hdr_screenshots"
namespace: "core_graphics"
description: "Enables screenshotting display content in HDR, sans tone mapping"
@@ -314,6 +321,16 @@
} # vrr_bugfix_dropped_frame
flag {
+ name: "vsync_predictor_recovery"
+ namespace: "core_graphics"
+ description: "Recover the vsync predictor from bad vsync model"
+ bug: "385059265"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # vsync_predictor_recovery
+
+flag {
name: "window_blur_kawase2"
namespace: "core_graphics"
description: "Flag for using Kawase2 algorithm for window blur"
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 0ad5ac9..bfafb65 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -483,6 +483,16 @@
prop_name: "ro.surface_flinger.min_acquired_buffers"
}
+# Defines the maximum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+ api_name: "max_acquired_buffers"
+ type: Long
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_acquired_buffers"
+}
+
# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
# buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0017300..e2ac233 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -82,6 +82,11 @@
prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
}
prop {
+ api_name: "max_acquired_buffers"
+ type: Long
+ prop_name: "ro.surface_flinger.max_acquired_buffers"
+ }
+ prop {
api_name: "max_frame_buffer_acquired_buffers"
type: Long
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 000628f..ad43cdc 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -19,6 +19,9 @@
<option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index b4496d3..5a82914 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -147,7 +147,7 @@
<< "Timeout waiting for vsync event";
DisplayEventReceiver::Event event;
while (mDisplayEventReceiver.getEvents(&event, 1) > 0) {
- if (event.header.type != DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (event.header.type != DisplayEventType::DISPLAY_EVENT_VSYNC) {
continue;
}
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index c95c875..c91f1ea 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -48,7 +48,11 @@
ui::DisplayMode displayMode;
SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
- const ui::Size& resolution = displayMode.resolution;
+ ui::Size resolution = displayMode.resolution;
+ if (displayState.orientation == ui::Rotation::Rotation90 ||
+ displayState.orientation == ui::Rotation::Rotation270) {
+ std::swap(resolution.width, resolution.height);
+ }
sp<IBinder> vDisplay;
@@ -93,8 +97,8 @@
#else
t.setDisplaySurface(vDisplay, producer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- t.setDisplayProjection(vDisplay, displayState.orientation,
- Rect(displayState.layerStackSpaceRect), Rect(resolution));
+ t.setDisplayProjection(vDisplay, ui::Rotation::Rotation0, Rect(resolution),
+ Rect(resolution));
t.setDisplayLayerStack(vDisplay, layerStack);
t.setLayerStack(mirrorSc, layerStack);
t.apply();
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 7910e77..82390ac 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -265,9 +265,8 @@
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->touchableRegion = region;
inputInfo->token = sp<BBinder>::make();
mLifecycleManager.applyTransactions(transactions);
@@ -280,9 +279,8 @@
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
if (!inputInfo->token) {
inputInfo->token = sp<BBinder>::make();
}
@@ -299,9 +297,8 @@
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->touchableRegion = region;
inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
transactions.back().states.front().touchCropId = touchCropId;
@@ -455,9 +452,8 @@
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+ transactions.back().states.front().state.updateSurfaceDamageRegion(damageRegion);
mLifecycleManager.applyTransactions(transactions);
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 42259af..f1c1549 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -65,9 +65,9 @@
"mock/MockFrameTracer.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockTimeStats.cpp",
- "mock/MockVsyncController.cpp",
"mock/MockVSyncDispatch.cpp",
"mock/MockVSyncTracker.cpp",
+ "mock/MockVsyncController.cpp",
],
}
@@ -87,10 +87,10 @@
test_suites: ["device-tests"],
header_libs: ["surfaceflinger_tests_common_headers"],
srcs: [
+ "*.cpp",
":libsurfaceflinger_backend_mock_sources",
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
- "*.cpp",
],
}
@@ -117,9 +117,8 @@
"android.hardware.power@1.2",
"android.hardware.power@1.3",
"libaidlcommonsupport",
- "libcompositionengine_mocks",
"libcompositionengine",
- "libframetimeline",
+ "libcompositionengine_mocks",
"libgmock",
"libgui_mocks",
"libperfetto_client_experimental",
@@ -140,14 +139,15 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.common@1.2",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libSurfaceFlingerProp",
"libbase",
"libbinder",
"libbinder_ndk",
"libcutils",
- "libEGL",
"libfmq",
- "libGLESv1_CM",
- "libGLESv2",
"libgui",
"libhidlbase",
"libinput",
@@ -157,11 +157,10 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstatslog_surfaceflinger",
- "libSurfaceFlingerProp",
"libsync",
+ "libtracing_perfetto",
"libui",
"libutils",
- "libtracing_perfetto",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 9ece312..c342e1e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -468,7 +468,7 @@
layer.externalTexture = buffer;
layer.bufferData->acquireFence = Fence::NO_FENCE;
layer.dataspace = ui::Dataspace::UNKNOWN;
- layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH));
+ layer.setSurfaceDamageRegion(Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
Mock::VerifyAndClear(test->mRenderEngine);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 81bfc97..4322af7 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -114,7 +114,11 @@
// Test instances
TestableSurfaceFlinger mFlinger;
+
sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+ sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+ sp<compositionengine::mock::DisplaySurface>::make();
+
sp<GraphicBuffer> mBuffer =
sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
@@ -294,6 +298,7 @@
injector.setSecure(static_cast<bool>(SECURE));
injector.setNativeWindow(test->mNativeWindow);
+ injector.setDisplaySurface(test->mDisplaySurface);
// Creating a DisplayDevice requires getting default dimensions from the
// native window along with some other initial setup.
@@ -533,13 +538,14 @@
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
-template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure>
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure,
+ HWDisplayId hwDisplayId = 1002>
struct SecondaryDisplay {
static constexpr auto CONNECTION_TYPE = connectionType;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr bool SECURE = secure;
static constexpr uint8_t PORT = 254;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = hwDisplayId;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
static constexpr auto GET_IDENTIFICATION_DATA =
connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid
@@ -571,10 +577,11 @@
/*hasIdentificationData=*/true, kNonSecure>,
1080, 2092>;
-using ExternalDisplayWithIdentificationVariant =
- PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
- /*hasIdentificationData=*/true, kNonSecure>,
- 1920, 1280>;
+template <HWDisplayId hwDisplayId = 1002>
+using ExternalDisplayWithIdentificationVariant = PhysicalDisplayVariant<
+ SecondaryDisplay<ui::DisplayConnectionType::External,
+ /*hasIdentificationData=*/true, kNonSecure, hwDisplayId>,
+ 1920, 1280>;
using ExternalDisplayVariant =
PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
/*hasIdentificationData=*/false, kSecure>,
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 268a6c4..6f15db8 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -30,7 +30,7 @@
#include "AsyncCallRecorder.h"
#include "DisplayHardware/DisplayMode.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "Scheduler/EventThread.h"
#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
@@ -270,7 +270,7 @@
ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp "
<< expectedTimestamp;
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type)
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_VSYNC, event.header.type)
<< name << " did not get the correct event for timestamp " << expectedTimestamp;
EXPECT_EQ(expectedTimestamp, event.header.timestamp)
<< name << " did not get the expected timestamp for timestamp " << expectedTimestamp;
@@ -344,7 +344,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_HOTPLUG, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConnected, event.hotplug.connected);
}
@@ -355,7 +355,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConfigId, event.modeChange.modeId);
EXPECT_EQ(expectedVsyncPeriod, event.modeChange.vsyncPeriod);
@@ -367,7 +367,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(uid, event.frameRateOverride.uid);
EXPECT_EQ(frameRateHz, event.frameRateOverride.frameRateHz);
@@ -376,7 +376,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
}
@@ -874,7 +874,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId);
EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel);
EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
diff --git a/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
index 2941a14..0f16073 100644
--- a/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
@@ -213,4 +213,33 @@
EXPECT_EQ(listenerCount(), 0u);
}
-} // namespace android
\ No newline at end of file
+TEST_F(JankTrackerTest, multipleLayersAreTrackedIndependently) {
+ size_t jankDataReceived = 0;
+ size_t numBatchesReceived = 0;
+
+ EXPECT_CALL(*mListener.get(), onJankData(_))
+ .WillRepeatedly([&](const std::vector<gui::JankData>& jankData) {
+ jankDataReceived += jankData.size();
+ numBatchesReceived++;
+ return binder::Status::ok();
+ });
+ addJankListener(123);
+ addJankListener(321);
+ addJankData(123, 1);
+ addJankData(123, 2);
+ addJankData(123, 3);
+ addJankData(321, 4);
+ addJankData(321, 5);
+
+ JankTracker::flushJankData(123);
+ flushBackgroundThread();
+ EXPECT_EQ(numBatchesReceived, 1u);
+ EXPECT_EQ(jankDataReceived, 3u);
+
+ JankTracker::flushJankData(321);
+ flushBackgroundThread();
+ EXPECT_EQ(numBatchesReceived, 2u);
+ EXPECT_EQ(jankDataReceived, 5u);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 7aad84b..3ed038b 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1642,8 +1642,8 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = 3;
- transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->token = sp<BBinder>::make();
mLifecycleManager.applyTransactions(transactions);
@@ -1671,8 +1671,8 @@
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = 3;
- transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->token = sp<BBinder>::make();
hideLayer(3);
mLifecycleManager.applyTransactions(transactions);
@@ -1879,9 +1879,6 @@
}
TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
setBuffer(1221,
std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */,
@@ -1920,9 +1917,6 @@
TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The left bound is extended when shifting to the right
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
const int texSize = 10;
setBuffer(1221,
@@ -1942,9 +1936,6 @@
TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The right bound is extended when shifting to the left
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
@@ -1965,9 +1956,6 @@
TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The top bound is extended when shifting to the bottom
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
const int texSize = 10;
setBuffer(1221,
@@ -1987,9 +1975,6 @@
TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The bottom bound is extended when shifting to the top
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
@@ -2010,9 +1995,6 @@
TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The left bound is extended when shifting to the right
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 908637a..e9b86b2 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -22,7 +22,7 @@
#include <scheduler/interface/ICompositor.h>
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "Scheduler/MessageQueue.h"
#include "mock/MockVSyncDispatch.h"
#include "utils/Timers.h"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1fc874d..cbcfe03 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -254,18 +254,43 @@
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
- EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms));
+ struct TestCase {
+ Fps refreshRate;
+ std::chrono::nanoseconds presentLatency;
+ int expectedBufferCount;
+ };
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
+ const auto verifyTestCases = [&](std::vector<TestCase> tests) {
+ for (const auto testCase : tests) {
+ EXPECT_EQ(testCase.expectedBufferCount,
+ mFlinger.calculateMaxAcquiredBufferCount(testCase.refreshRate,
+ testCase.presentLatency));
+ }
+ };
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ std::vector<TestCase> testCases{{60_Hz, 30ms, 1},
+ {90_Hz, 30ms, 2},
+ {120_Hz, 30ms, 3},
+ {60_Hz, 40ms, 2},
+ {60_Hz, 10ms, 1}};
+ verifyTestCases(testCases);
const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
mFlinger.mutableMinAcquiredBuffers() = 2;
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ verifyTestCases({{60_Hz, 10ms, 2}});
mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
+
+ const auto savedMaxAcquiredBuffers = mFlinger.mutableMaxAcquiredBuffers();
+ mFlinger.mutableMaxAcquiredBuffers() = 2;
+ testCases = {{60_Hz, 30ms, 1},
+ {90_Hz, 30ms, 2},
+ {120_Hz, 30ms, 2}, // max buffers allowed is 2
+ {60_Hz, 40ms, 2},
+ {60_Hz, 10ms, 1}};
+ verifyTestCases(testCases);
+ mFlinger.mutableMaxAcquiredBuffers() = 3; // max buffers allowed is 3
+ verifyTestCases({{120_Hz, 30ms, 3}});
+ mFlinger.mutableMaxAcquiredBuffers() = savedMaxAcquiredBuffers;
}
MATCHER(Is120Hz, "") {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index aadff76..aa5b786 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -141,7 +141,11 @@
// --------------------------------------------------------------------
// Invocation
- sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false, kUniqueId);
+ sp<IBinder> displayToken =
+ mFlinger.createVirtualDisplay(kDisplayName, false,
+ gui::ISurfaceComposer::OptimizationPolicy::
+ optimizeForPower,
+ kUniqueId);
// --------------------------------------------------------------------
// Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index a506873..525a940 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -171,16 +171,22 @@
static constexpr DisplayModeId kModeId90{1};
static constexpr DisplayModeId kModeId120{2};
static constexpr DisplayModeId kModeId90_4K{3};
+ static constexpr DisplayModeId kModeId60_8K{4};
static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz, 0);
static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz, 1);
static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz, 2);
static constexpr ui::Size kResolution4K{3840, 2160};
+ static constexpr ui::Size kResolution8K{7680, 4320};
+
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+ static inline const DisplayModePtr kMode60_8K =
+ createDisplayMode(kModeId60_8K, 60_Hz, 4, kResolution8K);
- static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
+ static inline const DisplayModes kModes =
+ makeModes(kMode60, kMode90, kMode120, kMode90_4K, kMode60_8K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -326,6 +332,8 @@
}
TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) {
+ SET_FLAG_FOR_TEST(flags::synced_resolution_switch, false);
+
EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_EQ(NO_ERROR,
@@ -360,9 +368,45 @@
EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K));
}
-TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
+TEST_F(DisplayModeSwitchingTest, changeResolutionSynced) {
+ SET_FLAG_FOR_TEST(flags::synced_resolution_switch, true);
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ // PrimaryDisplayVariant has a 4K size, so switch to 8K.
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60_8K,
+ 60_Hz)));
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+ // The mode should not be set until the commit that resizes the display.
+ mFlinger.commit();
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+ mFlinger.commit();
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+ // Set the display size to match the resolution.
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = mDisplay->getDisplayToken().promote();
+ state.width = static_cast<uint32_t>(kResolution8K.width);
+ state.height = static_cast<uint32_t>(kResolution8K.height);
+
+ // The next commit should set the mode and resize the framebuffer.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mDisplaySurface, resizeBuffers(kResolution8K));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60_8K);
+
+ constexpr bool kModeset = true;
+ mFlinger.setDisplayStateLocked(state);
+ mFlinger.configureAndCommit(kModeset);
+
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60_8K));
+}
+
+TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -427,8 +471,6 @@
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -497,8 +539,6 @@
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 2d986c6..49972b0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -66,7 +66,7 @@
PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -77,12 +77,12 @@
mFlinger.configure();
// Configure an external display with identification info.
- using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
+ using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -125,7 +125,7 @@
PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -136,12 +136,12 @@
mFlinger.configure();
// Configure an external display with identification info.
- using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
+ using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -198,7 +198,7 @@
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -224,8 +224,6 @@
}
TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -242,7 +240,7 @@
EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
.WillRepeatedly(Return(Error::BAD_DISPLAY));
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -262,4 +260,51 @@
EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
}
+TEST_F(HotplugTest, rejectsHotplugOnActivePortsDuplicate) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ // Second display should come up properly.
+ using SecondDisplay = ExternalDisplayWithIdentificationVariant<>;
+ SecondDisplay::setupHwcHotplugCallExpectations(this);
+ SecondDisplay::setupHwcGetActiveConfigCallExpectations(this);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(SecondDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+ SecondDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+ mFlinger.configure();
+
+ EXPECT_TRUE(hasPhysicalHwcDisplay(SecondDisplay::HWC_DISPLAY_ID));
+
+ // Third display will return the same port ID as the second, and the hotplug
+ // should fail.
+ constexpr HWDisplayId kHwDisplayId = 1234;
+ using DuplicatePortDisplay = ExternalDisplayWithIdentificationVariant<kHwDisplayId>;
+
+ // We expect display identification to be fetched correctly, since EDID and
+ // port are available and successfully retrieved from HAL.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(DuplicatePortDisplay::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(*DuplicatePortDisplay::PORT::value),
+ SetArgPointee<2>(getExternalEedid()), Return(Error::NONE)));
+
+ DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+ mFlinger.configure();
+
+ // The hotplug should be rejected due to an attempt to connect a display to an already active
+ // port. No HWComposer::DisplayData should be created.
+ EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
+
+ // Disconnecting a display that was not successfully configured should be a no-op.
+ DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
+ mFlinger.configure();
+
+ EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 6951eaf..cd554ea 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -241,7 +241,8 @@
ASSERT_TRUE(hwcDisplayId);
const auto port = Case::Display::PORT::value;
ASSERT_TRUE(port);
- mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt);
+ mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, *port,
+ std::nullopt);
DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
.setResolution(Case::Display::RESOLUTION)
.setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
@@ -252,6 +253,7 @@
state.physical = {.id = *displayId,
.hwcDisplayId = *hwcDisplayId,
+ .port = *port,
.activeMode = activeMode};
ui::ColorModes colorModes;
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9a2e254..bb377ba 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -337,9 +337,9 @@
mFlinger->configure();
}
- void configureAndCommit() {
+ void configureAndCommit(bool modeset = false) {
configure();
- commitTransactionsLocked(eDisplayTransactionNeeded);
+ commitTransactionsLocked(eDisplayTransactionNeeded, modeset);
}
void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
@@ -399,12 +399,16 @@
float requestedRefreshRate = 0.0f) {
static const std::string kTestId =
"virtual:libsurfaceflinger_unittest:TestableSurfaceFlinger";
- return mFlinger->createVirtualDisplay(displayName, isSecure, kTestId, requestedRefreshRate);
+ return mFlinger
+ ->createVirtualDisplay(displayName, isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower,
+ kTestId, requestedRefreshRate);
}
auto createVirtualDisplay(const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
const std::string& uniqueId, float requestedRefreshRate = 0.0f) {
- return mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId,
+ return mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy, uniqueId,
requestedRefreshRate);
}
@@ -429,11 +433,14 @@
dispSurface, producer);
}
- void commitTransactionsLocked(uint32_t transactionFlags) {
+ void commitTransactionsLocked(uint32_t transactionFlags, bool modeset = false) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
mFlinger->processDisplayChangesLocked();
mFlinger->commitTransactionsLocked(transactionFlags);
+ if (modeset) {
+ mFlinger->initiateDisplayModeChanges();
+ }
}
void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) {
@@ -469,7 +476,7 @@
ftl::FakeGuard guard(kMainThreadContext);
ScreenCaptureResults captureResults;
- auto displayState = std::optional{display->getCompositionDisplay()->getState()};
+ const auto& state = display->getCompositionDisplay()->getState();
auto layers = getLayerSnapshotsFn();
SurfaceFlinger::ScreenshotArgs screenshotArgs;
@@ -480,10 +487,14 @@
screenshotArgs.dataspace = dataspace;
screenshotArgs.isSecure = isSecure;
screenshotArgs.seamlessTransition = seamlessTransition;
+ screenshotArgs.displayBrightnessNits = state.displayBrightnessNits;
+ screenshotArgs.sdrWhitePointNits = state.sdrWhitePointNits;
+ screenshotArgs.renderIntent = state.renderIntent;
+ screenshotArgs.colorMode = state.colorMode;
return mFlinger->renderScreenImpl(screenshotArgs, buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
- captureResults, displayState, layers);
+ captureResults, layers);
}
auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
@@ -709,6 +720,7 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+ auto& mutableMaxAcquiredBuffers() { return SurfaceFlinger::maxAcquiredBuffersOpt; }
auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
return mFlinger->mLayerSnapshotBuilder;
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 918107d..ccf6a9c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -304,6 +304,53 @@
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
}
+TEST_F(VSyncPredictorTest, recoverAfterDriftedVSyncAreReplacedWithCorrectVSync) {
+ SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true);
+ auto constexpr idealPeriodNs = 4166666;
+ auto constexpr minFrameIntervalNs = 8333333;
+ auto constexpr idealPeriod = Fps::fromPeriodNsecs(idealPeriodNs);
+ auto constexpr minFrameRate = Fps::fromPeriodNsecs(minFrameIntervalNs);
+ hal::VrrConfig vrrConfig{.minFrameIntervalNs = minFrameIntervalNs};
+ ftl::NonNull<DisplayModePtr> mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), idealPeriod, vrrConfig));
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ true);
+ // Curated list of VSyncs that causes the VSync drift.
+ std::vector<nsecs_t> const simulatedVsyncs{74473665741, 74481774375, 74489911818, 74497993491,
+ 74506000833, 74510002150, 74513904390, 74517748707,
+ 74521550947, 74525383187, 74529165427, 74533067667,
+ 74536751484, 74540653724, 74544282649, 74548084889,
+ 74551917129, 74555699369, 74559601609, 74563601611,
+ 74567503851, 74571358168, 74575260408, 74578889333,
+ 74582691573, 74586523813, 74590306053, 74593589870,
+ 74597492110, 74601121035, 74604923275, 74608755515,
+ 74612537755, 74616166680, 74619795605, 74623424530,
+ 74627043455, 74630645695, 74634245935, 74637778175,
+ 74641291992, 74644794232, 74648275157, 74651575397,
+ 74654807637, 74658007877, 74661176117, 74664345357};
+ for (auto const& timestamp : simulatedVsyncs) {
+ vrrTracker.addVsyncTimestamp(timestamp);
+ }
+ auto slope = vrrTracker.getVSyncPredictionModel().slope;
+ // Without using the idealPeriod for the calculation of the VSync predictor mode the
+ // the slope would be 3343031
+ EXPECT_THAT(slope, IsCloseTo(4347805, mMaxRoundingError));
+ EXPECT_FALSE(vrrTracker.needsMoreSamples());
+
+ auto lastVsync = 74664345357;
+ // Add valid VSyncs to replace the drifted VSyncs
+ for (int i = 0; i <= kHistorySize; i++) {
+ lastVsync += minFrameIntervalNs;
+ EXPECT_TRUE(vrrTracker.addVsyncTimestamp(lastVsync));
+ }
+ EXPECT_FALSE(vrrTracker.needsMoreSamples());
+ slope = vrrTracker.getVSyncPredictionModel().slope;
+ // Corrected slop is closer to the idealPeriod
+ // when valid vsync are inserted otherwise this would still be 3349673
+ EXPECT_THAT(slope, IsCloseTo(idealPeriodNs, mMaxRoundingError));
+}
+
TEST_F(VSyncPredictorTest, handlesVsyncChange) {
auto const fastPeriod = 100;
auto const fastTimeBase = 100;
@@ -402,11 +449,7 @@
std::vector<nsecs_t> const simulatedVsyncs{
158929578733000,
158929306806205, // oldest TS in ringbuffer
- 158929650879052,
- 158929661969209,
- 158929684198847,
- 158929695268171,
- 158929706370359,
+ 158929650879052, 158929661969209, 158929684198847, 158929695268171, 158929706370359,
};
auto const idealPeriod = 11111111;
auto const expectedPeriod = 11113919;
@@ -421,9 +464,9 @@
EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
- // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
- // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
- // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+ // (timePoint - oldestTS) % expectedPeriod works out to be: 10702663
+ // (timePoint - oldestTS) / expectedPeriod works out to be: 37.96
+ // so failure to account for the offset will floor the ordinal to 37, which was in the past.
auto const timePoint = 158929728723871;
auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
EXPECT_THAT(prediction, Ge(timePoint));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index 3fa4093..01d078b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -44,7 +44,8 @@
MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*),
(override));
MOCK_METHOD(void, allocatePhysicalDisplay,
- (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override));
+ (hal::HWDisplayId, PhysicalDisplayId, uint8_t port, std::optional<ui::Size>),
+ (override));
MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override));
MOCK_METHOD(status_t, getDeviceCompositionChanges,
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
index 8b4a6be..77a68d9 100644
--- a/services/surfaceflinger/tests/vsync/vsync.cpp
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -41,7 +41,7 @@
while ((n = q->getEvents(buffer, 1)) > 0) {
for (int i=0 ; i<n ; i++) {
- if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (buffer[i].header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) {
printf("event vsync: count=%d\t", buffer[i].vsync.count);
}
if (oldTimeStamp) {
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4735ae5..ed03cfc 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -42,14 +42,9 @@
shared_libs: [
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libutils",
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
],
cflags: [
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 283a5f0..302e3e1 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -18,8 +18,6 @@
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <hardware/vibrator.h>
#include <utils/Log.h>
@@ -31,15 +29,10 @@
using aidl::android::hardware::vibrator::CompositePrimitive;
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
using std::chrono::milliseconds;
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
namespace android {
namespace vibrator {
@@ -53,9 +46,9 @@
return nullptr;
}
- auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default";
+ auto serviceName = std::string(IVibrator::descriptor) + "/default";
if (AServiceManager_isDeclared(serviceName.c_str())) {
- std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder(
+ std::shared_ptr<IVibrator> hal = IVibrator::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
if (hal) {
ALOGV("Successfully connected to Vibrator HAL AIDL service.");
@@ -63,30 +56,9 @@
}
}
- sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
- if (halV1_0 == nullptr) {
- ALOGV("Vibrator HAL service not available.");
- gHalExists = false;
- return nullptr;
- }
-
- sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
- if (halV1_3) {
- ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
- return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
- }
- sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
- if (halV1_2) {
- ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
- return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
- }
- sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
- if (halV1_1) {
- ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
- return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
- }
- ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
- return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
+ return nullptr;
}
// -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 536a6b3..5d4c17d 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "VibratorHalWrapper"
#include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <hardware/vibrator.h>
#include <cmath>
@@ -33,32 +32,18 @@
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
+using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::PrimitivePwle;
using aidl::android::hardware::vibrator::VendorEffect;
using std::chrono::milliseconds;
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
namespace android {
namespace vibrator {
// -------------------------------------------------------------------------------------------------
-template <class T>
-bool isStaticCastValid(Effect effect) {
- T castEffect = static_cast<T>(effect);
- auto iter = hardware::hidl_enum_range<T>();
- return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
-}
-
-// -------------------------------------------------------------------------------------------------
-
Info HalWrapper::getInfo() {
getCapabilities();
getPrimitiveDurations();
@@ -261,7 +246,7 @@
if (!result.isOk()) {
return;
}
- std::shared_ptr<Aidl::IVibrator> newHandle = result.value();
+ std::shared_ptr<IVibrator> newHandle = result.value();
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
mHandle = std::move(newHandle);
@@ -514,219 +499,13 @@
frequencyToOutputAccelerationMap);
}
-std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
+std::shared_ptr<IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
// -------------------------------------------------------------------------------------------------
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::ping() {
- return HalResultFactory::fromReturn(getHal()->ping());
-}
-
-template <typename I>
-void HidlHalWrapper<I>::tryReconnect() {
- sp<I> newHandle = I::tryGetService();
- if (newHandle) {
- std::lock_guard<std::mutex> lock(mHandleMutex);
- mHandle = std::move(newHandle);
- }
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
- const std::function<void()>& completionCallback) {
- auto status = getHal()->on(timeout.count());
- auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
- if (ret.isOk()) {
- mCallbackScheduler->schedule(completionCallback, timeout);
- }
- return ret;
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::off() {
- auto status = getHal()->off();
- return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
- uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
- auto status = getHal()->setAmplitude(amp);
- return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
- ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
- ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
- ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
- hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
- Capabilities capabilities =
- result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
- return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities);
-}
-
-template <typename I>
-template <typename T>
-HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
- perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
- const std::function<void()>& completionCallback) {
- V1_0::Status status;
- int32_t lengthMs;
- auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
- status = retStatus;
- lengthMs = retLengthMs;
- };
-
- V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
- auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
- milliseconds length = milliseconds(lengthMs);
-
- auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length);
- if (ret.isOk()) {
- mCallbackScheduler->schedule(completionCallback, length);
- }
-
- return ret;
-}
-
-template <typename I>
-sp<I> HidlHalWrapper<I>::getHal() {
- std::lock_guard<std::mutex> lock(mHandleMutex);
- return mHandle;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_0::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_1::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_2::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
- static_cast<V1_2::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
- auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
- return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
- static_cast<V1_2::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_3::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
- static_cast<V1_3::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
- Capabilities capabilities = Capabilities::NONE;
-
- sp<V1_3::IVibrator> hal = getHal();
- auto amplitudeResult = hal->supportsAmplitudeControl();
- if (!amplitudeResult.isOk()) {
- return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities);
- }
-
- auto externalControlResult = hal->supportsExternalControl();
- if (amplitudeResult.withDefault(false)) {
- capabilities |= Capabilities::AMPLITUDE_CONTROL;
- }
- if (externalControlResult.withDefault(false)) {
- capabilities |= Capabilities::EXTERNAL_CONTROL;
-
- if (amplitudeResult.withDefault(false)) {
- capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
- }
- }
-
- return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult),
- capabilities);
-}
-
-// -------------------------------------------------------------------------------------------------
-
}; // namespace vibrator
}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 494f88f..31b6ed0 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -20,7 +20,10 @@
#include <vibratorservice/VibratorManagerHalController.h>
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
namespace android {
@@ -29,9 +32,9 @@
std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (gHalExists) {
- auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
+ auto serviceName = std::string(IVibratorManager::descriptor) + "/default";
if (AServiceManager_isDeclared(serviceName.c_str())) {
- std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder(
+ std::shared_ptr<IVibratorManager> hal = IVibratorManager::fromBinder(
ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
if (hal) {
ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
@@ -41,6 +44,7 @@
}
}
+ ALOGV("VibratorManager HAL service not available.");
gHalExists = false;
return std::make_shared<LegacyManagerHalWrapper>();
}
@@ -150,10 +154,10 @@
return apply(cancelSyncedFn, "cancelSynced");
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession(
- const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalController::startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
const std::function<void()>& completionCallback) {
- hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn =
+ hal_fn<std::shared_ptr<IVibrationSession>> startSessionFn =
[&](std::shared_ptr<ManagerHalWrapper> hal) {
return hal->startSession(ids, config, completionCallback);
};
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 3db8ff8..bab3f58 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -20,7 +20,10 @@
#include <vibratorservice/VibratorManagerHalWrapper.h>
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
namespace android {
@@ -41,10 +44,9 @@
return HalResult<void>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession(
- const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&,
- const std::function<void()>&) {
- return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported();
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalWrapper::startSession(
+ const std::vector<int32_t>&, const VibrationSessionConfig&, const std::function<void()>&) {
+ return HalResult<std::shared_ptr<IVibrationSession>>::unsupported();
}
HalResult<void> ManagerHalWrapper::clearSessions() {
@@ -87,11 +89,11 @@
std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
- std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
- std::shared_ptr<Aidl::IVibrator> vibrator;
+ std::function<HalResult<std::shared_ptr<IVibrator>>()> reconnectFn = [=, this]() {
+ std::shared_ptr<IVibrator> vibrator;
auto status = this->getHal()->getVibrator(vibratorId, &vibrator);
- return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status),
- vibrator);
+ return HalResultFactory::fromStatus<std::shared_ptr<IVibrator>>(std::move(status),
+ vibrator);
};
auto result = reconnectFn();
if (!result.isOk()) {
@@ -110,8 +112,8 @@
}
void AidlManagerHalWrapper::tryReconnect() {
- auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
- std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder(
+ auto aidlServiceName = std::string(IVibratorManager::descriptor) + "/default";
+ std::shared_ptr<IVibratorManager> newHandle = IVibratorManager::fromBinder(
ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str())));
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
@@ -198,15 +200,14 @@
return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession(
- const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> AidlManagerHalWrapper::startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
const std::function<void()>& completionCallback) {
auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
- std::shared_ptr<Aidl::IVibrationSession> session;
+ std::shared_ptr<IVibrationSession> session;
auto status = getHal()->startSession(ids, config, cb, &session);
- return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status),
- std::move(
- session));
+ return HalResultFactory::fromStatus<std::shared_ptr<IVibrationSession>>(std::move(status),
+ std::move(session));
}
HalResult<void> AidlManagerHalWrapper::cancelSynced() {
@@ -227,7 +228,7 @@
return HalResultFactory::fromStatus(getHal()->clearSessions());
}
-std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
+std::shared_ptr<IVibratorManager> AidlManagerHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 915d6c7..6fc5cf3 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -29,15 +29,10 @@
],
shared_libs: [
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libutils",
"libvibratorservice",
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
],
cflags: [
"-Wall",
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 9a39ad4..0652278 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -22,7 +22,6 @@
#include <android-base/thread_annotations.h>
#include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <binder/IServiceManager.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
@@ -105,26 +104,6 @@
: fromFailedStatus<T>(std::move(status));
}
- template <typename T>
- static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status&& status, T data) {
- return (status == hardware::vibrator::V1_0::Status::OK)
- ? HalResult<T>::ok(std::move(data))
- : fromFailedStatus<T>(std::move(status));
- }
-
- template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(std::move(data))
- : fromFailedReturn<T, R>(std::move(ret));
- }
-
- template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>&& ret,
- hardware::vibrator::V1_0::Status status, T data) {
- return ret.isOk() ? fromStatus<T>(std::move(status), std::move(data))
- : fromFailedReturn<T, R>(std::move(ret));
- }
-
static HalResult<void> fromStatus(status_t status) {
return (status == android::OK) ? HalResult<void>::ok()
: fromFailedStatus<void>(std::move(status));
@@ -134,17 +113,6 @@
return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status));
}
- static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) {
- return (status == hardware::vibrator::V1_0::Status::OK)
- ? HalResult<void>::ok()
- : fromFailedStatus<void>(std::move(status));
- }
-
- template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>&& ret) {
- return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret));
- }
-
private:
template <typename T>
static HalResult<T> fromFailedStatus(status_t status) {
@@ -166,23 +134,6 @@
}
return HalResult<T>::failed(status.getMessage());
}
-
- template <typename T>
- static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status&& status) {
- switch (status) {
- case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION:
- return HalResult<T>::unsupported();
- default:
- auto msg = "android::hardware::vibrator::V1_0::Status = " + toString(status);
- return HalResult<T>::failed(msg.c_str());
- }
- }
-
- template <typename T, typename R>
- static HalResult<T> fromFailedReturn(hardware::Return<R>&& ret) {
- return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str())
- : HalResult<T>::failed(ret.description().c_str());
- }
};
// -------------------------------------------------------------------------------------------------
@@ -548,108 +499,6 @@
std::shared_ptr<IVibrator> getHal();
};
-// Wrapper for the HDIL Vibrator HALs.
-template <typename I>
-class HidlHalWrapper : public HalWrapper {
-public:
- HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
- : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
- virtual ~HidlHalWrapper() = default;
-
- HalResult<void> ping() override final;
- void tryReconnect() override final;
-
- HalResult<void> on(std::chrono::milliseconds timeout,
- const std::function<void()>& completionCallback) override final;
- HalResult<void> off() override final;
-
- HalResult<void> setAmplitude(float amplitude) override final;
- virtual HalResult<void> setExternalControl(bool enabled) override;
-
- HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect,
- HalWrapper::EffectStrength strength) override final;
- HalResult<void> alwaysOnDisable(int32_t id) override final;
-
-protected:
- std::mutex mHandleMutex;
- sp<I> mHandle GUARDED_BY(mHandleMutex);
-
- virtual HalResult<Capabilities> getCapabilitiesInternal() override;
-
- template <class T>
- using perform_fn =
- hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
- hardware::vibrator::V1_0::IVibrator::perform_cb);
-
- template <class T>
- HalResult<std::chrono::milliseconds> performInternal(
- perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback);
-
- sp<I> getHal();
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.0.
-class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
-public:
- HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_0() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.1.
-class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
-public:
- HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_1::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_1() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.2.
-class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
-public:
- HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_2::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_2() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.3.
-class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
-public:
- HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_3::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_3() = default;
-
- HalResult<void> setExternalControl(bool enabled) override final;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-
-protected:
- HalResult<Capabilities> getCapabilitiesInternal() override final;
-};
-
// -------------------------------------------------------------------------------------------------
}; // namespace vibrator
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 92527eb..038248e 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -29,10 +29,6 @@
"VibratorCallbackSchedulerTest.cpp",
"VibratorHalControllerTest.cpp",
"VibratorHalWrapperAidlTest.cpp",
- "VibratorHalWrapperHidlV1_0Test.cpp",
- "VibratorHalWrapperHidlV1_1Test.cpp",
- "VibratorHalWrapperHidlV1_2Test.cpp",
- "VibratorHalWrapperHidlV1_3Test.cpp",
"VibratorManagerHalControllerTest.cpp",
"VibratorManagerHalWrapperAidlTest.cpp",
"VibratorManagerHalWrapperLegacyTest.cpp",
@@ -45,15 +41,10 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libvibratorservice",
"libutils",
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
],
static_libs: [
"libgmock",
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
deleted file mode 100644
index 04dbe4e..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_0Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/persistable_bundle_aidl.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-
-using aidl::android::hardware::vibrator::Braking;
-using aidl::android::hardware::vibrator::CompositeEffect;
-using aidl::android::hardware::vibrator::CompositePrimitive;
-using aidl::android::hardware::vibrator::CompositePwleV2;
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2Primitive;
-using aidl::android::hardware::vibrator::VendorEffect;
-using aidl::android::os::PersistableBundle;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_0 : public V1_0::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<void>, ping, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_0Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_0>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
- EXPECT_CALL(*mMockHal.get(), ping())
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<void>(); })
- .WillRepeatedly([]() {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
-
- ASSERT_TRUE(mWrapper->ping().isOk());
- ASSERT_TRUE(mWrapper->ping().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
- });
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
- ASSERT_EQ(1, *callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
- ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
- EXPECT_CALL(*mMockHal.get(), off())
- .Times(Exactly(4))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
- .WillOnce([]() {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- })
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
-
- ASSERT_TRUE(mWrapper->off().isOk());
- ASSERT_TRUE(mWrapper->off().isUnsupported());
- ASSERT_TRUE(mWrapper->off().isFailed());
- ASSERT_TRUE(mWrapper->off().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1))))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
- });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- auto maxAmplitude = std::numeric_limits<uint8_t>::max();
- ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk());
- ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported());
- ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed());
- ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
- ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
- ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
- ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(2))
- .WillOnce([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- })
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- vibrator::Info info = mWrapper->getInfo();
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
- ASSERT_TRUE(info.supportedEffects.isUnsupported());
- ASSERT_TRUE(info.supportedBraking.isUnsupported());
- ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
- ASSERT_TRUE(info.primitiveDurations.isUnsupported());
- ASSERT_TRUE(info.primitiveDelayMax.isUnsupported());
- ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported());
- ASSERT_TRUE(info.compositionSizeMax.isUnsupported());
- ASSERT_TRUE(info.pwleSizeMax.isUnsupported());
- ASSERT_TRUE(info.minFrequency.isUnsupported());
- ASSERT_TRUE(info.resonantFrequency.isUnsupported());
- ASSERT_TRUE(info.frequencyResolution.isUnsupported());
- ASSERT_TRUE(info.qFactor.isUnsupported());
- ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
- ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
- return hardware::Return<bool>(false);
- });
-
- ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
- return hardware::Return<bool>(true);
- });
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(
- std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- vibrator::Info info = mWrapper->getInfo();
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
- ASSERT_TRUE(info.supportedEffects.isUnsupported());
- ASSERT_TRUE(info.supportedBraking.isUnsupported());
- ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
- ASSERT_TRUE(info.primitiveDurations.isUnsupported());
- ASSERT_TRUE(info.minFrequency.isUnsupported());
- ASSERT_TRUE(info.resonantFrequency.isUnsupported());
- ASSERT_TRUE(info.frequencyResolution.isUnsupported());
- ASSERT_TRUE(info.qFactor.isUnsupported());
- ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
- ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 10);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using TICK that is only available in v1.1
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) {
- PersistableBundle vendorData; // empty
- VendorEffect vendorEffect;
- vendorEffect.vendorData = vendorData;
- vendorEffect.strength = EffectStrength::LIGHT;
- vendorEffect.scale = 1.0f;
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
- std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
- singleEffect.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
- multipleEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
- multipleEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
- std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
- multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
- multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) {
- CompositePwleV2 composite;
- composite.pwlePrimitives = {
- PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
- PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
- PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
- };
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->composePwleV2(composite, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
deleted file mode 100644
index b0a6537..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_1Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_1 : public V1_1::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_1Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_1>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 0);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using THUD that is only available in v1.2
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
deleted file mode 100644
index dfe3fa0..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_2Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_2 : public V1_2::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_2,
- (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_2Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_2>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 10);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using TEXTURE_TICK that is only available in v1.3
- auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
deleted file mode 100644
index 8624332..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_3Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_3 : public V1_3::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_2,
- (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_3,
- (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_3Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_3>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
- ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
- vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
- mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(false); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(
- std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(false); });
- }
-
- // Call to supportsAmplitudeControl failed.
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- // Call to supportsExternalControl failed.
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- // Returns successful result from third call.
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-
- // Returns cached successful result.
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
- _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
- _))
- .Times(Exactly(2))
- .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 0);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 879d2d0..be8fb3e 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -22,6 +22,13 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+// Expose internal header files to test testing binary
+cc_library_headers {
+ name: "libvulkanprivate_headers-testing",
+ export_include_dirs: ["."],
+ visibility: ["//frameworks/native/vulkan/tests"],
+}
+
ndk_library {
name: "libvulkan",
symbol_file: "libvulkan.map.txt",
diff --git a/vulkan/libvulkan/TEST_MAPPING b/vulkan/libvulkan/TEST_MAPPING
new file mode 100644
index 0000000..16e342b7
--- /dev/null
+++ b/vulkan/libvulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libvulkan_test"
+ }
+ ]
+}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 09b0a14..5e2b55e 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1413,6 +1413,119 @@
allocator->pfnFree(allocator->pUserData, swapchain);
}
+static VkResult getProducerUsageGPDIFP2(
+ const VkPhysicalDevice& pdev,
+ const VkSwapchainCreateInfoKHR* create_info,
+ const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
+ bool create_protected_swapchain,
+ uint64_t* producer_usage) {
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to
+ // GetPhysicalDeviceImageFormatProperties2
+ void* compression_control_pNext = nullptr;
+ VkImageCompressionControlEXT image_compression = {};
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ compression_control_pNext = &image_compression;
+ } break;
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+
+ // call GetPhysicalDeviceImageFormatProperties2KHR
+ VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+ .pNext = compression_control_pNext,
+ .handleType =
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+ };
+
+ // AHB does not have an sRGB format so we can't pass it to GPDIFP
+ // We need to convert the format to unorm if it is srgb
+ VkFormat format = create_info->imageFormat;
+ if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+
+ VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+ .pNext = &external_image_format_info,
+ .format = format,
+ .type = VK_IMAGE_TYPE_2D,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = create_info->imageUsage,
+ .flags =
+ create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ };
+
+ // If supporting mutable format swapchain add the mutable format flag
+ if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
+ image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
+ }
+
+ VkAndroidHardwareBufferUsageANDROID ahb_usage;
+ ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+ ahb_usage.pNext = nullptr;
+
+ VkImageFormatProperties2 image_format_properties;
+ image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+ image_format_properties.pNext = &ahb_usage;
+
+ VkResult result = GetPhysicalDeviceImageFormatProperties2(
+ pdev, &image_format_info, &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE(
+ "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage "
+ "failed: %d",
+ result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ // Determine if USAGE_FRONT_BUFFER is needed.
+ // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when
+ // querying for producer_usage. So androidHardwareBufferUsage will not
+ // contain USAGE_FRONT_BUFFER. We need to manually check for usage here.
+ if (!(swapchain_image_usage &
+ VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) {
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ return VK_SUCCESS;
+ }
+
+ // Check if USAGE_FRONT_BUFFER is supported for this swapchain
+ AHardwareBuffer_Desc ahb_desc = {
+ .width = create_info->imageExtent.width,
+ .height = create_info->imageExtent.height,
+ .layers = create_info->imageArrayLayers,
+ .format = create_info->imageFormat,
+ .usage = ahb_usage.androidHardwareBufferUsage |
+ AHARDWAREBUFFER_USAGE_FRONT_BUFFER,
+ .stride = 0, // stride is always ignored when calling isSupported()
+ };
+
+ // If FRONT_BUFFER is not supported in the GPDIFP2 path
+ // then we need to fallback to GetSwapchainGrallocUsageXAndroid
+ if (AHardwareBuffer_isSupported(&ahb_desc)) {
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+ return VK_SUCCESS;
+ }
+
+ return VK_ERROR_FORMAT_NOT_SUPPORTED;
+}
+
static VkResult getProducerUsage(const VkDevice& device,
const VkSwapchainCreateInfoKHR* create_info,
const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
@@ -1422,106 +1535,16 @@
const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
const InstanceData& instance_data = GetData(pdev);
const InstanceDriverTable& instance_dispatch = instance_data.driver;
+
if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2 ||
instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
- // Look through the create_info pNext chain passed to createSwapchainKHR
- // for an image compression control struct.
- // if one is found AND the appropriate extensions are enabled, create a
- // VkImageCompressionControlEXT structure to pass on to
- // GetPhysicalDeviceImageFormatProperties2
- void* compression_control_pNext = nullptr;
- VkImageCompressionControlEXT image_compression = {};
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- compression_control_pNext = &image_compression;
- } break;
- default:
- // Ignore all other info structs
- break;
- }
- }
-
- // call GetPhysicalDeviceImageFormatProperties2KHR
- VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
- .pNext = compression_control_pNext,
- .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
- };
-
- // AHB does not have an sRGB format so we can't pass it to GPDIFP
- // We need to convert the format to unorm if it is srgb
- VkFormat format = create_info->imageFormat;
- if (format == VK_FORMAT_R8G8B8A8_SRGB) {
- format = VK_FORMAT_R8G8B8A8_UNORM;
- }
-
- VkPhysicalDeviceImageFormatInfo2 image_format_info = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
- .pNext = &external_image_format_info,
- .format = format,
- .type = VK_IMAGE_TYPE_2D,
- .tiling = VK_IMAGE_TILING_OPTIMAL,
- .usage = create_info->imageUsage,
- .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
- };
-
- // If supporting mutable format swapchain add the mutable format flag
- if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
- image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
- }
-
- VkAndroidHardwareBufferUsageANDROID ahb_usage;
- ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
- ahb_usage.pNext = nullptr;
-
- VkImageFormatProperties2 image_format_properties;
- image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
- image_format_properties.pNext = &ahb_usage;
-
- VkResult result = GetPhysicalDeviceImageFormatProperties2(
- pdev, &image_format_info, &image_format_properties);
- if (result != VK_SUCCESS) {
- ALOGE(
- "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage "
- "failed: %d",
- result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
-
- // Determine if USAGE_FRONT_BUFFER is needed.
- // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when
- // querying for producer_usage. So androidHardwareBufferUsage will not
- // contain USAGE_FRONT_BUFFER. We need to manually check for usage here.
- if (!(swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) {
- *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ VkResult result =
+ getProducerUsageGPDIFP2(pdev, create_info, swapchain_image_usage,
+ create_protected_swapchain, producer_usage);
+ if (result == VK_SUCCESS) {
return VK_SUCCESS;
}
-
- // Check if USAGE_FRONT_BUFFER is supported for this swapchain
- AHardwareBuffer_Desc ahb_desc = {
- .width = create_info->imageExtent.width,
- .height = create_info->imageExtent.height,
- .layers = create_info->imageArrayLayers,
- .format = create_info->imageFormat,
- .usage = ahb_usage.androidHardwareBufferUsage | AHARDWAREBUFFER_USAGE_FRONT_BUFFER,
- .stride = 0, // stride is always ignored when calling isSupported()
- };
-
- // If FRONT_BUFFER is not supported,
- // then we need to call GetSwapchainGrallocUsageXAndroid below
- if (AHardwareBuffer_isSupported(&ahb_desc)) {
- *producer_usage = ahb_usage.androidHardwareBufferUsage;
- *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
- return VK_SUCCESS;
- }
+ // Fall through to gralloc path on error
}
uint64_t native_usage = 0;
diff --git a/vulkan/tests/Android.bp b/vulkan/tests/Android.bp
new file mode 100644
index 0000000..db218c1
--- /dev/null
+++ b/vulkan/tests/Android.bp
@@ -0,0 +1,57 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "libvulkan_test",
+ test_suites: ["general-tests"],
+
+ srcs: [
+ "libvulkan_test.cpp",
+ ],
+
+ strip: {
+ none: true,
+ },
+
+ cflags: [
+ "-DVK_USE_PLATFORM_ANDROID_KHR",
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "hwvulkan_headers",
+ "libvulkanprivate_headers-testing",
+ "vulkan_headers",
+ ],
+
+ cppflags: [
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-c99-extensions",
+ "-Wno-exit-time-destructors",
+ "-Wno-float-equal",
+ "-Wno-global-constructors",
+ "-Wno-zero-length-array",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libgraphicsenv",
+ "liblog",
+ "libmediandk",
+ "libvulkan",
+ ],
+
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ "liblog",
+ ],
+
+}
diff --git a/vulkan/tests/README.md b/vulkan/tests/README.md
new file mode 100644
index 0000000..3c9b66c
--- /dev/null
+++ b/vulkan/tests/README.md
@@ -0,0 +1,24 @@
+#libvulkan_test
+
+This binary contains the unit tests for testing libvulkan (The Vulkan Loader).
+
+These tests rely on the underlying GPU driver to be able to successfully create a valid
+swapchain. These tests are design to run on an Android emulator to give us a consistent GPU
+driver to test against. YMMV when running this on a physical device with an arbitrary GPU
+driver.
+
+To run these tests run:
+```
+atest libvulkan_test
+```
+
+If using an acloud device the full command list for the root of a freshly cloned repo would be:
+```
+source build/envsetup.sh
+lunch aosp_cf_x86_64_phone-trunk_staging-eng
+m
+acloud create --local-image
+atest libvulkan_test
+```
+
+
diff --git a/vulkan/tests/libvulkan_test.cpp b/vulkan/tests/libvulkan_test.cpp
new file mode 100644
index 0000000..7e4bfd8
--- /dev/null
+++ b/vulkan/tests/libvulkan_test.cpp
@@ -0,0 +1,607 @@
+/*
+ * 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 <android/log.h>
+#include <driver.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <media/NdkImageReader.h>
+#include <vulkan/vulkan.h>
+
+#define LOGI(...) \
+ __android_log_print(ANDROID_LOG_INFO, "libvulkan_test", __VA_ARGS__)
+#define LOGE(...) \
+ __android_log_print(ANDROID_LOG_ERROR, "libvulkan_test", __VA_ARGS__)
+
+#define VK_CHECK(result) ASSERT_EQ(VK_SUCCESS, result)
+
+namespace android {
+
+namespace libvulkantest {
+
+class AImageReaderVulkanSwapchainTest : public ::testing::Test {
+ public:
+ AImageReaderVulkanSwapchainTest() {}
+
+ AImageReader* mReader = nullptr;
+ ANativeWindow* mWindow = nullptr;
+ VkInstance mVkInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDev = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+ VkSurfaceKHR mSurface = VK_NULL_HANDLE;
+ VkQueue mPresentQueue = VK_NULL_HANDLE;
+ uint32_t mPresentQueueFamily = UINT32_MAX;
+ VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ // ------------------------------------------------------
+ // Helper methods
+ // ------------------------------------------------------
+
+ void createVulkanInstance(std::vector<const char*>& layers) {
+ const char* extensions[] = {
+ VK_KHR_SURFACE_EXTENSION_NAME,
+ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
+ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+ };
+
+ VkApplicationInfo appInfo{};
+ appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ appInfo.pApplicationName = "AImageReader Vulkan Swapchain Test";
+ appInfo.applicationVersion = 1;
+ appInfo.pEngineName = "TestEngine";
+ appInfo.engineVersion = 1;
+ appInfo.apiVersion = VK_API_VERSION_1_0;
+
+ VkInstanceCreateInfo instInfo{};
+ instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instInfo.pApplicationInfo = &appInfo;
+ instInfo.enabledExtensionCount =
+ sizeof(extensions) / sizeof(extensions[0]);
+ instInfo.ppEnabledExtensionNames = extensions;
+ instInfo.enabledLayerCount = layers.size();
+ instInfo.ppEnabledLayerNames = layers.data();
+ VkResult res = vkCreateInstance(&instInfo, nullptr, &mVkInstance);
+ VK_CHECK(res);
+ LOGE("Vulkan instance created");
+ }
+
+ void createAImageReader(int width, int height, int format, int maxImages) {
+ media_status_t status =
+ AImageReader_new(width, height, format, maxImages, &mReader);
+ ASSERT_EQ(AMEDIA_OK, status) << "Failed to create AImageReader";
+ ASSERT_NE(nullptr, mReader) << "AImageReader is null";
+
+ // Optionally set a listener
+ AImageReader_ImageListener listener{};
+ listener.context = this;
+ listener.onImageAvailable =
+ &AImageReaderVulkanSwapchainTest::onImageAvailable;
+ AImageReader_setImageListener(mReader, &listener);
+
+ LOGI("AImageReader created with %dx%d, format=%d", width, height,
+ format);
+ }
+
+ void getANativeWindowFromReader() {
+ ASSERT_NE(nullptr, mReader);
+
+ media_status_t status = AImageReader_getWindow(mReader, &mWindow);
+ ASSERT_EQ(AMEDIA_OK, status)
+ << "Failed to get ANativeWindow from AImageReader";
+ ASSERT_NE(nullptr, mWindow) << "ANativeWindow is null";
+ LOGI("ANativeWindow obtained from AImageReader");
+ }
+
+ void createVulkanSurface() {
+ ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+ ASSERT_NE((ANativeWindow*)nullptr, mWindow);
+
+ VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo{};
+ surfaceCreateInfo.sType =
+ VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.window = mWindow;
+
+ VkResult res = vkCreateAndroidSurfaceKHR(
+ mVkInstance, &surfaceCreateInfo, nullptr, &mSurface);
+ VK_CHECK(res);
+ LOGI("Vulkan surface created from ANativeWindow");
+ }
+
+ void pickPhysicalDeviceAndQueueFamily() {
+ ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+
+ uint32_t deviceCount = 0;
+ vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, nullptr);
+ ASSERT_GT(deviceCount, 0U) << "No Vulkan physical devices found!";
+
+ std::vector<VkPhysicalDevice> devices(deviceCount);
+ vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, devices.data());
+
+ for (auto& dev : devices) {
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+ nullptr);
+ std::vector<VkQueueFamilyProperties> queueProps(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+ queueProps.data());
+
+ for (uint32_t i = 0; i < queueFamilyCount; i++) {
+ VkBool32 support = VK_FALSE;
+ vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, mSurface,
+ &support);
+ if (support == VK_TRUE) {
+ // Found a queue family that can present
+ mPhysicalDev = dev;
+ mPresentQueueFamily = i;
+
+ LOGI(
+ "Physical device found with queue family %u supporting "
+ "present",
+ i);
+ return;
+ }
+ }
+ }
+
+ FAIL()
+ << "No physical device found that supports present to the surface!";
+ }
+
+ void createDeviceAndGetQueue(std::vector<const char*>& layers,
+ std::vector<const char*> inExtensions = {}) {
+ ASSERT_NE((void*)VK_NULL_HANDLE, mPhysicalDev);
+ ASSERT_NE(UINT32_MAX, mPresentQueueFamily);
+
+ float queuePriority = 1.0f;
+ VkDeviceQueueCreateInfo queueInfo{};
+ queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo.queueFamilyIndex = mPresentQueueFamily;
+ queueInfo.queueCount = 1;
+ queueInfo.pQueuePriorities = &queuePriority;
+
+ VkDeviceCreateInfo deviceInfo{};
+ deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ deviceInfo.queueCreateInfoCount = 1;
+ deviceInfo.pQueueCreateInfos = &queueInfo;
+ deviceInfo.enabledLayerCount = layers.size();
+ deviceInfo.ppEnabledLayerNames = layers.data();
+
+ std::vector<const char*> extensions = {
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ };
+ for (auto extension : inExtensions) {
+ extensions.push_back(extension);
+ }
+ deviceInfo.enabledExtensionCount = extensions.size();
+ deviceInfo.ppEnabledExtensionNames = extensions.data();
+
+ VkResult res =
+ vkCreateDevice(mPhysicalDev, &deviceInfo, nullptr, &mDevice);
+ VK_CHECK(res);
+ LOGI("Logical device created");
+
+ vkGetDeviceQueue(mDevice, mPresentQueueFamily, 0, &mPresentQueue);
+ ASSERT_NE((VkQueue)VK_NULL_HANDLE, mPresentQueue);
+ LOGI("Acquired present-capable queue");
+ }
+
+ void createSwapchain() {
+ ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice);
+ ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface);
+
+ VkSurfaceCapabilitiesKHR surfaceCaps{};
+ VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ mPhysicalDev, mSurface, &surfaceCaps));
+
+ uint32_t formatCount = 0;
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+ &formatCount, nullptr);
+ ASSERT_GT(formatCount, 0U);
+ std::vector<VkSurfaceFormatKHR> formats(formatCount);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+ &formatCount, formats.data());
+
+ VkSurfaceFormatKHR chosenFormat = formats[0];
+ LOGI("Chosen surface format: %d", chosenFormat.format);
+
+ uint32_t presentModeCount = 0;
+ vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDev, mSurface,
+ &presentModeCount, nullptr);
+ ASSERT_GT(presentModeCount, 0U);
+ std::vector<VkPresentModeKHR> presentModes(presentModeCount);
+ vkGetPhysicalDeviceSurfacePresentModesKHR(
+ mPhysicalDev, mSurface, &presentModeCount, presentModes.data());
+
+ VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;
+ for (auto mode : presentModes) {
+ if (mode == VK_PRESENT_MODE_FIFO_KHR) {
+ chosenPresentMode = mode;
+ break;
+ }
+ }
+ LOGI("Chosen present mode: %d", chosenPresentMode);
+
+ VkExtent2D swapchainExtent{};
+ if (surfaceCaps.currentExtent.width == 0xFFFFFFFF) {
+ swapchainExtent.width = 640; // fallback
+ swapchainExtent.height = 480; // fallback
+ } else {
+ swapchainExtent = surfaceCaps.currentExtent;
+ }
+ LOGI("Swapchain extent: %d x %d", swapchainExtent.width,
+ swapchainExtent.height);
+
+ uint32_t desiredImageCount = surfaceCaps.minImageCount + 1;
+ if (surfaceCaps.maxImageCount > 0 &&
+ desiredImageCount > surfaceCaps.maxImageCount) {
+ desiredImageCount = surfaceCaps.maxImageCount;
+ }
+
+ VkSwapchainCreateInfoKHR swapchainInfo{};
+ swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainInfo.surface = mSurface;
+ swapchainInfo.minImageCount = desiredImageCount;
+ swapchainInfo.imageFormat = chosenFormat.format;
+ swapchainInfo.imageColorSpace = chosenFormat.colorSpace;
+ swapchainInfo.imageExtent = swapchainExtent;
+ swapchainInfo.imageArrayLayers = 1;
+ swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ swapchainInfo.preTransform = surfaceCaps.currentTransform;
+ swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
+ swapchainInfo.presentMode = chosenPresentMode;
+ swapchainInfo.clipped = VK_TRUE;
+ swapchainInfo.oldSwapchain = VK_NULL_HANDLE;
+
+ uint32_t queueFamilyIndices[] = {mPresentQueueFamily};
+ swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainInfo.queueFamilyIndexCount = 1;
+ swapchainInfo.pQueueFamilyIndices = queueFamilyIndices;
+
+ VkResult res =
+ vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain);
+ if (res == VK_SUCCESS) {
+ LOGI("Swapchain created successfully");
+
+ uint32_t swapchainImageCount = 0;
+ vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+ nullptr);
+ std::vector<VkImage> swapchainImages(swapchainImageCount);
+ vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+ swapchainImages.data());
+
+ LOGI("Swapchain has %u images", swapchainImageCount);
+ } else {
+ LOGI("Swapchain creation failed");
+ }
+ }
+
+ // Image available callback (AImageReader)
+ static void onImageAvailable(void*, AImageReader* reader) {
+ LOGI("onImageAvailable callback triggered");
+ AImage* image = nullptr;
+ media_status_t status = AImageReader_acquireLatestImage(reader, &image);
+ if (status != AMEDIA_OK || !image) {
+ LOGE("Failed to acquire latest image");
+ return;
+ }
+ AImage_delete(image);
+ LOGI("Released acquired image");
+ }
+
+ void cleanUpSwapchainForTest() {
+ if (mSwapchain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr);
+ mSwapchain = VK_NULL_HANDLE;
+ }
+ if (mDevice != VK_NULL_HANDLE) {
+ vkDestroyDevice(mDevice, nullptr);
+ mDevice = VK_NULL_HANDLE;
+ }
+ if (mSurface != VK_NULL_HANDLE) {
+ vkDestroySurfaceKHR(mVkInstance, mSurface, nullptr);
+ mSurface = VK_NULL_HANDLE;
+ }
+ if (mVkInstance != VK_NULL_HANDLE) {
+ vkDestroyInstance(mVkInstance, nullptr);
+ mVkInstance = VK_NULL_HANDLE;
+ }
+ if (mReader) {
+ // AImageReader_delete(mReader);
+ mReader = nullptr;
+ }
+ // Note: The ANativeWindow from AImageReader is implicitly
+ // managed by the reader, so we don't explicitly delete it.
+ mWindow = nullptr;
+ }
+
+ void buildSwapchianForTest(std::vector<const char*>& instanceLayers,
+ std::vector<const char*>& deviceLayers) {
+ createVulkanInstance(instanceLayers);
+
+ // the "atest libvulkan_test" command will execute this test as a binary
+ // (not apk) on the device. Consequently we can't render to the screen
+ // and need to work around this by using AImageReader*
+ createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+ getANativeWindowFromReader();
+ createVulkanSurface();
+ pickPhysicalDeviceAndQueueFamily();
+
+ createDeviceAndGetQueue(deviceLayers);
+ createSwapchain();
+ }
+};
+
+TEST_F(AImageReaderVulkanSwapchainTest, TestHelperMethods) {
+ // Verify that the basic plumbing/helper functions of these tests is
+ // working. This doesn't directly test any of the layer code. It only
+ // verifies that we can successfully create a swapchain with an AImageReader
+
+ std::vector<const char*> instanceLayers;
+ std::vector<const char*> deviceLayers;
+ buildSwapchianForTest(deviceLayers, instanceLayers);
+
+ ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE);
+ ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE);
+ ASSERT_NE(mSwapchain, (VkSwapchainKHR)VK_NULL_HANDLE);
+ cleanUpSwapchainForTest();
+}
+
+// Passing state in these tests requires global state. Wrap each test in an
+// anonymous namespace to prevent conflicting names.
+namespace {
+
+VKAPI_ATTR VkResult VKAPI_CALL hookedGetPhysicalDeviceImageFormatProperties2KHR(
+ VkPhysicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2*,
+ VkImageFormatProperties2*) {
+ return VK_ERROR_SURFACE_LOST_KHR;
+}
+
+static PFN_vkGetSwapchainGrallocUsage2ANDROID
+ pfnNextGetSwapchainGrallocUsage2ANDROID = nullptr;
+
+static bool g_grallocCalled = false;
+
+VKAPI_ATTR VkResult VKAPI_CALL hookGetSwapchainGrallocUsage2ANDROID(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ uint64_t* grallocConsumerUsage,
+ uint64_t* grallocProducerUsage) {
+ g_grallocCalled = true;
+ if (pfnNextGetSwapchainGrallocUsage2ANDROID) {
+ return pfnNextGetSwapchainGrallocUsage2ANDROID(
+ device, format, imageUsage, swapchainImageUsage,
+ grallocConsumerUsage, grallocProducerUsage);
+ }
+
+ return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+TEST_F(AImageReaderVulkanSwapchainTest, getProducerUsageFallbackTest1) {
+ // BUG: 379230826
+ // Verify that getProducerUsage falls back to
+ // GetSwapchainGrallocUsage*ANDROID if GPDIFP2 fails
+ std::vector<const char*> instanceLayers = {};
+ std::vector<const char*> deviceLayers = {};
+ createVulkanInstance(instanceLayers);
+
+ createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+ getANativeWindowFromReader();
+ createVulkanSurface();
+ pickPhysicalDeviceAndQueueFamily();
+
+ createDeviceAndGetQueue(deviceLayers);
+ auto& pdev = vulkan::driver::GetData(mDevice).driver_physical_device;
+ auto& pdevDispatchTable = vulkan::driver::GetData(pdev).driver;
+ auto& deviceDispatchTable = vulkan::driver::GetData(mDevice).driver;
+
+ ASSERT_NE(deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID, nullptr);
+
+ pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 =
+ hookedGetPhysicalDeviceImageFormatProperties2KHR;
+ deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID =
+ hookGetSwapchainGrallocUsage2ANDROID;
+
+ ASSERT_FALSE(g_grallocCalled);
+
+ createSwapchain();
+
+ ASSERT_TRUE(g_grallocCalled);
+
+ ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE);
+ ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE);
+ cleanUpSwapchainForTest();
+}
+
+} // namespace
+
+// Passing state in these tests requires global state. Wrap each test in an
+// anonymous namespace to prevent conflicting names.
+namespace {
+
+static bool g_returnNotSupportedOnce = true;
+
+VKAPI_ATTR VkResult VKAPI_CALL
+Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce(
+ VkPhysicalDevice /*physicalDevice*/,
+ const VkPhysicalDeviceImageFormatInfo2* /*pImageFormatInfo*/,
+ VkImageFormatProperties2* /*pImageFormatProperties*/) {
+ if (g_returnNotSupportedOnce) {
+ g_returnNotSupportedOnce = false;
+ return VK_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+ return VK_SUCCESS;
+}
+
+TEST_F(AImageReaderVulkanSwapchainTest, SurfaceFormats2KHR_IgnoreNotSupported) {
+ // BUG: 357903074
+ // Verify that vkGetPhysicalDeviceSurfaceFormats2KHR properly
+ // ignores VK_ERROR_FORMAT_NOT_SUPPORTED and continues enumerating formats.
+ std::vector<const char*> instanceLayers;
+ createVulkanInstance(instanceLayers);
+ createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+ getANativeWindowFromReader();
+ createVulkanSurface();
+ pickPhysicalDeviceAndQueueFamily();
+
+ auto& pdevDispatchTable = vulkan::driver::GetData(mPhysicalDev).driver;
+ pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 =
+ Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce;
+
+ PFN_vkGetPhysicalDeviceSurfaceFormats2KHR
+ pfnGetPhysicalDeviceSurfaceFormats2KHR =
+ reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormats2KHR>(
+ vkGetInstanceProcAddr(mVkInstance,
+ "vkGetPhysicalDeviceSurfaceFormats2KHR"));
+ ASSERT_NE(nullptr, pfnGetPhysicalDeviceSurfaceFormats2KHR)
+ << "Could not get pointer to vkGetPhysicalDeviceSurfaceFormats2KHR";
+
+ VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2{};
+ surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
+ surfaceInfo2.pNext = nullptr;
+ surfaceInfo2.surface = mSurface;
+
+ uint32_t formatCount = 0;
+ VkResult res = pfnGetPhysicalDeviceSurfaceFormats2KHR(
+ mPhysicalDev, &surfaceInfo2, &formatCount, nullptr);
+
+ // If the loader never tries a second format, it might fail or 0-out the
+ // formatCount. The patch ensures it continues to the next format rather
+ // than bailing out on the first NOT_SUPPORTED.
+ ASSERT_EQ(VK_SUCCESS, res)
+ << "vkGetPhysicalDeviceSurfaceFormats2KHR failed unexpectedly";
+ ASSERT_GT(formatCount, 0U)
+ << "No surface formats found; the loader may have bailed early.";
+
+ std::vector<VkSurfaceFormat2KHR> formats(formatCount);
+ for (auto& f : formats) {
+ f.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR;
+ f.pNext = nullptr;
+ }
+ res = pfnGetPhysicalDeviceSurfaceFormats2KHR(mPhysicalDev, &surfaceInfo2,
+ &formatCount, formats.data());
+ ASSERT_EQ(VK_SUCCESS, res) << "Failed to retrieve surface formats";
+
+ LOGI(
+ "SurfaceFormats2KHR_IgnoreNotSupported test: found %u formats after "
+ "ignoring NOT_SUPPORTED",
+ formatCount);
+
+ cleanUpSwapchainForTest();
+}
+
+} // namespace
+
+namespace {
+
+TEST_F(AImageReaderVulkanSwapchainTest, MutableFormatSwapchainTest) {
+ // Test swapchain with mutable format extension
+ std::vector<const char*> instanceLayers;
+ std::vector<const char*> deviceLayers;
+ std::vector<const char*> deviceExtensions = {
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
+ VK_KHR_MAINTENANCE2_EXTENSION_NAME,
+ VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME};
+
+ createVulkanInstance(instanceLayers);
+ createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+ getANativeWindowFromReader();
+ createVulkanSurface();
+ pickPhysicalDeviceAndQueueFamily();
+ createDeviceAndGetQueue(deviceLayers, deviceExtensions);
+
+ ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice);
+ ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface);
+
+ VkSurfaceCapabilitiesKHR surfaceCaps{};
+ VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDev, mSurface,
+ &surfaceCaps));
+
+ uint32_t formatCount = 0;
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount,
+ nullptr);
+ ASSERT_GT(formatCount, 0U);
+ std::vector<VkSurfaceFormatKHR> formats(formatCount);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount,
+ formats.data());
+
+ VkFormat viewFormats[2] = {formats[0].format, formats[0].format};
+ if (formatCount > 1) {
+ viewFormats[1] = formats[1].format;
+ }
+
+ VkImageFormatListCreateInfoKHR formatList{};
+ formatList.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR;
+ formatList.viewFormatCount = 2;
+ formatList.pViewFormats = viewFormats;
+
+ VkSwapchainCreateInfoKHR swapchainInfo{};
+ swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainInfo.pNext = &formatList;
+ swapchainInfo.surface = mSurface;
+ swapchainInfo.minImageCount = surfaceCaps.minImageCount + 1;
+ swapchainInfo.imageFormat = formats[0].format;
+ swapchainInfo.imageColorSpace = formats[0].colorSpace;
+ swapchainInfo.imageExtent = surfaceCaps.currentExtent;
+ swapchainInfo.imageArrayLayers = 1;
+ swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapchainInfo.preTransform = surfaceCaps.currentTransform;
+ swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
+ swapchainInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ swapchainInfo.clipped = VK_TRUE;
+
+ swapchainInfo.flags = VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
+
+ uint32_t queueFamilyIndices[] = {mPresentQueueFamily};
+ swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainInfo.queueFamilyIndexCount = 1;
+ swapchainInfo.pQueueFamilyIndices = queueFamilyIndices;
+
+ VkResult res =
+ vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain);
+ if (res == VK_SUCCESS) {
+ LOGI("Mutable format swapchain created successfully");
+
+ uint32_t imageCount = 0;
+ vkGetSwapchainImagesKHR(mDevice, mSwapchain, &imageCount, nullptr);
+ ASSERT_GT(imageCount, 0U);
+ } else {
+ LOGI(
+ "Mutable format swapchain creation failed (extension may not be "
+ "supported)");
+ }
+
+ cleanUpSwapchainForTest();
+}
+
+} // namespace
+
+} // namespace libvulkantest
+
+} // namespace android
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 8c0cce2..18fef2b 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -986,6 +986,13 @@
template <typename Visitor>
inline bool Iterate(Visitor* visitor,
+ VkJsonExtImage2DViewOf3DFeatures* features) {
+ return visitor->Visit("image2DViewOf3DFeaturesEXT",
+ &features->image_2D_view_of_3D_features_EXT);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
VkJsonExtShaderFloat16Int8Features* features) {
return visitor->Visit("shaderFloat16Int8FeaturesKHR",
&features->shader_float16_int8_features_khr);
@@ -1093,6 +1100,13 @@
template <typename Visitor>
inline bool Iterate(Visitor* visitor,
+ VkPhysicalDeviceImage2DViewOf3DFeaturesEXT* features) {
+ return visitor->Visit("image2DViewOf3D", &features->image2DViewOf3D) &&
+ visitor->Visit("sampler2DViewOf3D", &features->sampler2DViewOf3D);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR* features) {
return visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
visitor->Visit("shaderInt8", &features->shaderInt8);
@@ -1241,6 +1255,10 @@
ret &= visitor->Visit("VK_KHR_variable_pointers",
&device->ext_variable_pointer_features);
}
+ if (device->ext_image_2d_view_of_3d_features.reported) {
+ ret &= visitor->Visit("VK_EXT_image_2d_view_of_3d",
+ &device->ext_image_2d_view_of_3d_features);
+ }
if (device->ext_shader_float16_int8_features.reported) {
ret &= visitor->Visit("VK_KHR_shader_float16_int8",
&device->ext_shader_float16_int8_features);
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 5818c73..87a76c1 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -58,6 +58,16 @@
VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr;
};
+struct VkJsonExtImage2DViewOf3DFeatures {
+ VkJsonExtImage2DViewOf3DFeatures() {
+ reported = false;
+ memset(&image_2D_view_of_3D_features_EXT, 0,
+ sizeof(VkPhysicalDeviceImage2DViewOf3DFeaturesEXT));
+ }
+ bool reported;
+ VkPhysicalDeviceImage2DViewOf3DFeaturesEXT image_2D_view_of_3D_features_EXT;
+};
+
struct VkJsonExtShaderFloat16Int8Features {
VkJsonExtShaderFloat16Int8Features() {
reported = false;
@@ -115,6 +125,7 @@
VkPhysicalDeviceFeatures features;
VkJsonExtDriverProperties ext_driver_properties;
VkJsonExtVariablePointerFeatures ext_variable_pointer_features;
+ VkJsonExtImage2DViewOf3DFeatures ext_image_2d_view_of_3d_features;
VkJsonExtShaderFloat16Int8Features ext_shader_float16_int8_features;
VkPhysicalDeviceMemoryProperties memory;
std::vector<VkQueueFamilyProperties> queues;
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 32bc50b..04bb446 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -103,6 +103,16 @@
features.pNext =
&device.ext_variable_pointer_features.variable_pointer_features_khr;
}
+ if (HasExtension("VK_EXT_image_2d_view_of_3d", device.extensions)) {
+ device.ext_image_2d_view_of_3d_features.reported = true;
+ device.ext_image_2d_view_of_3d_features.image_2D_view_of_3D_features_EXT
+ .sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT;
+ device.ext_image_2d_view_of_3d_features.image_2D_view_of_3D_features_EXT
+ .pNext = features.pNext;
+ features.pNext = &device.ext_image_2d_view_of_3d_features
+ .image_2D_view_of_3D_features_EXT;
+ }
if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
device.ext_shader_float16_int8_features.reported = true;
device.ext_shader_float16_int8_features.shader_float16_int8_features_khr