Merge "surfaceflinger_fuzzer : Resolved memory leak"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a62bd01..860a2d8 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -155,7 +155,10 @@
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
- static_libs: ["libgmock"],
+ static_libs: [
+ "libc++fs",
+ "libgmock",
+ ],
test_config: "dumpstate_test.xml",
data: [
":dumpstate_test_fixture",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index e42ee05..42e9e0f 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -51,7 +51,7 @@
// Creates a bugreport and exits, thus preserving the oneshot nature of the service.
// Note: takes ownership of data.
-[[noreturn]] static void* dumpstate_thread_main(void* data) {
+[[noreturn]] static void* dumpstate_thread_bugreport(void* data) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
MYLOGD("Finished taking a bugreport. Exiting.\n");
@@ -84,11 +84,28 @@
return android::OK;
}
+binder::Status DumpstateService::preDumpUiData(const std::string&) {
+ std::lock_guard<std::mutex> lock(lock_);
+ MYLOGI("preDumpUiData()");
+
+ if (ds_ != nullptr) {
+ MYLOGE("Error! DumpstateService is currently already being used. Returning.");
+ return exception(binder::Status::EX_SERVICE_SPECIFIC,
+ "DumpstateService is already being used");
+ }
+
+ ds_ = &(Dumpstate::GetInstance());
+ ds_->PreDumpUiData();
+
+ return binder::Status::ok();
+}
+
binder::Status DumpstateService::startBugreport(int32_t calling_uid,
const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd,
int bugreport_mode,
+ int bugreport_flags,
const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
@@ -96,12 +113,12 @@
// Ensure there is only one bugreport in progress at a time.
std::lock_guard<std::mutex> lock(lock_);
if (ds_ != nullptr) {
- MYLOGE("Error! There is already a bugreport in progress. Returning.");
+ MYLOGE("Error! DumpstateService is currently already being used. Returning.");
if (listener != nullptr) {
listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
}
return exception(binder::Status::EX_SERVICE_SPECIFIC,
- "There is already a bugreport in progress");
+ "DumpstateService is already being used");
}
// From here on, all conditions that indicate we are done with this incoming request should
@@ -123,8 +140,8 @@
}
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
- options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
- screenshot_fd, is_screenshot_requested);
+ options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags,
+ bugreport_fd, screenshot_fd, is_screenshot_requested);
if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
MYLOGE("Invalid filedescriptor");
@@ -148,10 +165,9 @@
pthread_t thread;
// Initialize dumpstate
ds_->Initialize();
- status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
+ status_t err = pthread_create(&thread, nullptr, dumpstate_thread_bugreport, ds_info);
if (err != 0) {
delete ds_info;
- ds_info = nullptr;
MYLOGE("Could not create a thread");
signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 3ec8471..997999c 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,13 +38,16 @@
status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status preDumpUiData(const std::string& callingPackage) override;
+
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd, int bugreport_mode,
- const sp<IDumpstateListener>& listener,
+ int bugreport_flags, const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) override;
- binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
+ binder::Status cancelBugreport(int32_t calling_uid,
+ const std::string& calling_package) override;
private:
// Dumpstate object which contains all the bugreporting logic.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 0793f0b..d4323af 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -49,6 +49,23 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
+ // Use pre-dumped data.
+ const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;
+
+ /**
+ * Speculatively pre-dumps UI data for a bugreport request that might come later.
+ *
+ * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short
+ * ring buffers that might get lost by the time the actual bugreport is requested.
+ *
+ * <p>{@code startBugreport} will then pick the pre-dumped data if:
+ * - {@link BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified.
+ * - {@code preDumpUiData} and {@code startBugreport} were called by the same UID.
+ *
+ * @param callingPackage package of the original application that requested the report.
+ */
+ void preDumpUiData(@utf8InCpp String callingPackage);
+
/**
* Starts a bugreport in the background.
*
@@ -63,13 +80,14 @@
* @param bugreportFd the file to which the zipped bugreport should be written
* @param screenshotFd the file to which screenshot should be written
* @param bugreportMode the mode that specifies other run time options; must be one of above
+ * @param bugreportFlags flags to customize the bugreport generation
* @param listener callback for updates; optional
* @param isScreenshotRequested indicates screenshot is requested or not
*/
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener,
- boolean isScreenshotRequested);
+ int bugreportMode, int bugreportFlags,
+ IDumpstateListener listener, boolean isScreenshotRequested);
/**
* Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b076c15..33dceff 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -170,6 +170,7 @@
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log"
+#define UPDATE_ENGINE_PREF_DIR "/data/misc/update_engine/prefs"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
#define PREREBOOT_DATA_DIR "/data/misc/prereboot"
#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
@@ -238,6 +239,7 @@
static const std::string DUMP_HALS_TASK = "DUMP HALS";
static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES";
namespace android {
namespace os {
@@ -1593,16 +1595,16 @@
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
// be distributed fairly evenly throughout the function.
-static Dumpstate::RunStatus dumpstate() {
+Dumpstate::RunStatus Dumpstate::dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
// Enqueue slow functions into the thread pool, if the parallel run is enabled.
std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins,
- dump_netstats_report;
+ dump_netstats_report, post_process_ui_traces;
if (ds.dump_pool_) {
// Pool was shutdown in DumpstateDefaultAfterCritical method in order to
- // drop root user. Restarts it with two threads for the parallel run.
- ds.dump_pool_->start(/* thread_counts = */2);
+ // drop root user. Restarts it.
+ ds.dump_pool_->start(/* thread_counts = */3);
dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
dump_incident_report = ds.dump_pool_->enqueueTask(
@@ -1612,6 +1614,8 @@
dump_board = ds.dump_pool_->enqueueTaskWithFd(
DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+ post_process_ui_traces = ds.dump_pool_->enqueueTask(
+ POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds);
}
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1732,11 +1736,6 @@
DumpFile("BINDER STATS", binder_logs_dir + "/stats");
DumpFile("BINDER STATE", binder_logs_dir + "/state");
- /* Add window and surface trace files. */
- if (!PropertiesHelper::IsUserBuild()) {
- ds.AddDir(WMTRACE_DATA_DIR, false);
- }
-
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (ds.dump_pool_) {
@@ -1816,6 +1815,14 @@
DumpIncidentReport);
}
+ if (ds.dump_pool_) {
+ WaitForTask(std::move(post_process_ui_traces));
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces);
+ }
+
+ MaybeAddUiTracesToZip();
+
return Dumpstate::RunStatus::OK;
}
@@ -1859,6 +1866,7 @@
ds.AddDir(RECOVERY_DIR, true);
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
+ ds.AddDir(UPDATE_ENGINE_PREF_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
@@ -2787,9 +2795,11 @@
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
+ int bugreport_flags,
const android::base::unique_fd& bugreport_fd_in,
const android::base::unique_fd& screenshot_fd_in,
bool is_screenshot_requested) {
+ this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
// Duplicate the fds because the passed in fds don't outlive the binder transaction.
bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2916,6 +2926,10 @@
}
}
+void Dumpstate::PreDumpUiData() {
+ MaybeSnapshotUiTraces();
+}
+
/*
* Dumps relevant information to a bugreport based on the given options.
*
@@ -3107,9 +3121,9 @@
// The trace file is added to the zip by MaybeAddSystemTraceToZip().
MaybeSnapshotSystemTrace();
- // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
- // from WMTRACE_DATA_DIR.
- MaybeSnapshotWinTrace();
+ // Snapshot the UI traces now (if running).
+ // The trace files will be added to bugreport later.
+ MaybeSnapshotUiTraces();
}
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3223,15 +3237,53 @@
// file in the later stages.
}
-void Dumpstate::MaybeSnapshotWinTrace() {
+void Dumpstate::MaybeSnapshotUiTraces() {
+ if (PropertiesHelper::IsUserBuild() || options_->use_predumped_ui_data) {
+ return;
+ }
+
// Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
- for (const auto& service : {"window", "input_method"}) {
+ for (const auto& service : {"input_method", "window"}) {
RunCommand(
// Empty name because it's not intended to be classified as a bugreport section.
// Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
"", {"cmd", service, "tracing", "save-for-bugreport"},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
+
+ static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
+ "service", "call", "SurfaceFlinger", "1042"
+ };
+ // Empty name because it's not intended to be classified as a bugreport section.
+ // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+ RunCommand(
+ "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES,
+ CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build());
+}
+
+void Dumpstate::MaybePostProcessUiTraces() {
+ if (PropertiesHelper::IsUserBuild()) {
+ return;
+ }
+
+ RunCommand(
+ // Empty name because it's not intended to be classified as a bugreport section.
+ // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+ "", {
+ "/system/xbin/su", "system",
+ "/system/bin/layertracegenerator",
+ "/data/misc/wmtrace/transactions_trace.winscope",
+ "/data/misc/wmtrace/layers_trace_from_transactions.winscope"
+ },
+ CommandOptions::WithTimeout(120).Always().RedirectStderr().Build());
+}
+
+void Dumpstate::MaybeAddUiTracesToZip() {
+ if (PropertiesHelper::IsUserBuild()) {
+ return;
+ }
+
+ ds.AddDir(WMTRACE_DATA_DIR, false);
}
void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 66f84cb..5f3acfd 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -204,6 +204,12 @@
BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT
};
+ // The flags used to customize bugreport requests.
+ enum BugreportFlag {
+ BUGREPORT_USE_PREDUMPED_UI_DATA =
+ android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
+ };
+
static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
static Dumpstate& GetInstance();
@@ -333,6 +339,12 @@
struct DumpOptions;
+ /**
+ * Pre-dump critical UI data, e.g. data stored in short ring buffers that might get lost
+ * by the time the actual bugreport is requested.
+ */
+ void PreDumpUiData();
+
/*
* Main entry point for running a complete bugreport.
*
@@ -396,6 +408,8 @@
// TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
+ // Will use data collected through a previous call to PreDumpUiData().
+ bool use_predumped_ui_data;
// File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
@@ -414,7 +428,8 @@
RunStatus Initialize(int argc, char* argv[]);
/* Initializes options from the requested mode. */
- void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
+ void Initialize(BugreportMode bugreport_mode, int bugreport_flags,
+ const android::base::unique_fd& bugreport_fd,
const android::base::unique_fd& screenshot_fd,
bool is_screenshot_requested);
@@ -532,10 +547,13 @@
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
RunStatus DumpstateDefaultAfterCritical();
+ RunStatus dumpstate();
void MaybeTakeEarlyScreenshot();
void MaybeSnapshotSystemTrace();
- void MaybeSnapshotWinTrace();
+ void MaybeSnapshotUiTraces();
+ void MaybePostProcessUiTraces();
+ void MaybeAddUiTracesToZip();
void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index ec89c0d..a634f93 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -56,7 +56,7 @@
MYLOGE("Unable to start 'dumpstate' service: %d", ret);
exit(1);
}
- MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+ MYLOGI("'dumpstate' service started and will wait for a call");
// Waits forever for an incoming connection.
// TODO(b/111441001): should this time out?
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 28e5ee2..b091c8e 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -497,14 +497,17 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
+ true);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
EXPECT_TRUE(status.isOk());
@@ -532,6 +535,7 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
@@ -539,9 +543,9 @@
// Call startBugreport with bad arguments.
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- 2000, // invalid bugreport mode
- listener, false);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd), 2000, // invalid bugreport mode
+ flags, listener, false);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
// The service should have died, freeing itself up for a new invocation.
@@ -563,6 +567,7 @@
unique_fd bugreport_fd2(dup(bugreport_fd.get()));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
unique_fd screenshot_fd2(dup(screenshot_fd.get()));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(bugreport_fd2.get(), -1);
@@ -571,14 +576,18 @@
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
+ true);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
- status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
+ status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
+ std::move(screenshot_fd2),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
+ listener2, true);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 70b4e5c..1ffcafa 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -31,6 +31,7 @@
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
+#include <filesystem>
#include <thread>
#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
@@ -237,7 +238,7 @@
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
// Other options retain default values
@@ -251,7 +252,7 @@
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_screenshot);
@@ -265,7 +266,7 @@
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false);
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
@@ -279,7 +280,7 @@
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_progress_updates);
@@ -294,7 +295,7 @@
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
@@ -309,7 +310,7 @@
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.wifi_only);
@@ -982,6 +983,19 @@
EXPECT_FALSE(ds.dump_pool_);
}
+TEST_F(DumpstateBaseTest, PreDumpUiData) {
+ // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped
+ static const auto kTransactionsTrace =
+ std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"};
+
+ std::system(("rm " + kTransactionsTrace.string()).c_str());
+ EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace));
+
+ Dumpstate& ds_ = Dumpstate::GetInstance();
+ ds_.PreDumpUiData();
+ EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace));
+}
+
class ZippedBugReportStreamTest : public DumpstateBaseTest {
public:
void SetUp() {
@@ -1045,11 +1059,6 @@
VerifyEntry(handle_, bugreport_txt_name, &entry);
}
-class DumpstateServiceTest : public DumpstateBaseTest {
- public:
- DumpstateService dss;
-};
-
class ProgressTest : public DumpstateBaseTest {
public:
Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index f0c19b9..b8e5ce1 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -60,6 +60,7 @@
MOCK_METHOD1(isDeclared, bool(const String16&));
MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
+ MOCK_METHOD1(getUpdatableNames, Vector<String16>(const String16&));
MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&));
MOCK_METHOD2(registerForNotifications, status_t(const String16&,
const sp<LocalRegistrationCallback>&));
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 6ef41e3..3b589dc 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
@@ -52,22 +53,7 @@
constexpr int kTimeoutMs = 60000;
-// TODO(calin): try to dedup this code.
-#if defined(__arm__)
-static const std::string kRuntimeIsa = "arm";
-#elif defined(__aarch64__)
-static const std::string kRuntimeIsa = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const std::string kRuntimeIsa = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const std::string kRuntimeIsa = "mips64";
-#elif defined(__i386__)
-static const std::string kRuntimeIsa = "x86";
-#elif defined(__x86_64__)
-static const std::string kRuntimeIsa = "x86_64";
-#else
-static const std::string kRuntimeIsa = "none";
-#endif
+static const std::string kRuntimeIsa = ABI_STRING;
int get_property(const char *key, char *value, const char *default_value) {
return property_get(key, value, default_value);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 3681d5b..2684f04 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -142,6 +142,26 @@
return updatableViaApex;
}
+static std::vector<std::string> getVintfUpdatableInstances(const std::string& apexName) {
+ std::vector<std::string> instances;
+
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ if (manifestInstance.format() == vintf::HalFormat::AIDL &&
+ manifestInstance.updatableViaApex().has_value() &&
+ manifestInstance.updatableViaApex().value() == apexName) {
+ std::string aname = manifestInstance.package() + "." +
+ manifestInstance.interface() + "/" + manifestInstance.instance();
+ instances.push_back(aname);
+ }
+ return false; // continue
+ });
+ return false; // continue
+ });
+
+ return instances;
+}
+
static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) {
AidlName aname;
if (!AidlName::fill(name, &aname)) return std::nullopt;
@@ -512,6 +532,30 @@
return Status::ok();
}
+Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& apexName,
+ std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> apexUpdatableInstances;
+#ifndef VENDORSERVICEMANAGER
+ apexUpdatableInstances = getVintfUpdatableInstances(apexName);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : apexUpdatableInstances) {
+ if (mAccess->canFind(ctx, instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
+ }
+
+ return Status::ok();
+}
+
Status ServiceManager::getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) {
auto ctx = mAccess->getCallingContext();
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 07b79f8..b24c11c 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -49,6 +49,8 @@
binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status updatableViaApex(const std::string& name,
std::optional<std::string>* outReturn) override;
+ binder::Status getUpdatableNames(const std::string& apexName,
+ std::vector<std::string>* outReturn) override;
binder::Status getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index a831d1b..c1a04dd 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -111,9 +111,7 @@
};
int main(int argc, char** argv) {
-#ifdef __ANDROID_RECOVERY__
android::base::InitLogging(argv, android::base::KernelLogger);
-#endif
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
index c516043..8819e1e 100644
--- a/cmds/servicemanager/servicemanager.microdroid.rc
+++ b/cmds/servicemanager/servicemanager.microdroid.rc
@@ -5,5 +5,4 @@
critical
onrestart setprop servicemanager.ready false
onrestart restart apexd
- task_profiles ServiceCapacityLow
shutdown critical
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 6b35265..3bd6db5 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -3,6 +3,7 @@
user system
group system readproc
critical
+ file /dev/kmsg w
onrestart setprop servicemanager.ready false
onrestart restart apexd
onrestart restart audioserver
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index c9305a1..80af1d1 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -2,6 +2,7 @@
class core
user system
group system readproc
+ file /dev/kmsg w
task_profiles ServiceCapacityLow
onrestart class_restart main
onrestart class_restart hal
diff --git a/include/android/font.h b/include/android/font.h
index 8a3a474..45eb81a 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -31,6 +31,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <sys/cdefs.h>
/******************************************************************
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 4417422..63b0328 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -75,6 +75,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <android/font.h>
diff --git a/include/android/input.h b/include/android/input.h
index d906af6..5d19c5c 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -771,6 +771,21 @@
* The interpretation of a generic axis is device-specific.
*/
AMOTION_EVENT_AXIS_GENERIC_16 = 47,
+ /**
+ * Axis constant: X gesture offset axis of a motion event.
+ *
+ * - For a touch pad, reports the distance that a swipe gesture has moved in the X axis, as a
+ * proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a
+ * swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of
+ * -0.1.
+ */
+ AMOTION_EVENT_AXIS_GESTURE_X_OFFSET = 48,
+ /**
+ * Axis constant: Y gesture offset axis of a motion event.
+ *
+ * The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis.
+ */
+ AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET = 49,
/**
* Note: This is not an "Axis constant". It does not represent any axis, nor should it be used
@@ -778,7 +793,7 @@
* to make some computations (like iterating through all possible axes) cleaner.
* Please update the value accordingly if you add a new axis.
*/
- AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GENERIC_16,
+ AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET,
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/include/ftl/concat.h b/include/ftl/concat.h
index ded48f7..e0774d3 100644
--- a/include/ftl/concat.h
+++ b/include/ftl/concat.h
@@ -20,7 +20,9 @@
namespace android::ftl {
-// Lightweight (not allocating nor sprintf-based) concatenation.
+// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be
+// values of integral type (including bool and char), string literals, or strings whose length
+// is constrained:
//
// std::string_view name = "Volume";
// ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h
index 8ce949e..726ba02 100644
--- a/include/ftl/details/concat.h
+++ b/include/ftl/details/concat.h
@@ -19,6 +19,7 @@
#include <functional>
#include <string_view>
+#include <ftl/details/type_traits.h>
#include <ftl/string.h>
namespace android::ftl::details {
@@ -26,16 +27,42 @@
template <typename T, typename = void>
struct StaticString;
+// Booleans.
template <typename T>
-struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> {
- static constexpr std::size_t N = to_chars_length_v<T>;
+struct StaticString<T, std::enable_if_t<is_bool_v<T>>> {
+ static constexpr std::size_t N = 5; // Length of "false".
- explicit StaticString(T v) : view(to_chars(buffer, v)) {}
+ explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {}
- to_chars_buffer_t<T> buffer;
const std::string_view view;
};
+// Characters.
+template <typename T>
+struct StaticString<T, std::enable_if_t<is_char_v<T>>> {
+ static constexpr std::size_t N = 1;
+
+ explicit constexpr StaticString(char c) : character(c) {}
+
+ const char character;
+ const std::string_view view{&character, 1u};
+};
+
+// Integers, including the integer value of other character types like char32_t.
+template <typename T>
+struct StaticString<
+ T, std::enable_if_t<std::is_integral_v<remove_cvref_t<T>> && !is_bool_v<T> && !is_char_v<T>>> {
+ using U = remove_cvref_t<T>;
+ static constexpr std::size_t N = to_chars_length_v<U>;
+
+ // TODO: Mark this and to_chars as `constexpr` in C++23.
+ explicit StaticString(U v) : view(to_chars(buffer, v)) {}
+
+ to_chars_buffer_t<U> buffer;
+ const std::string_view view;
+};
+
+// Character arrays.
template <std::size_t M>
struct StaticString<const char (&)[M], void> {
static constexpr std::size_t N = M - 1;
@@ -50,6 +77,7 @@
std::string_view view;
};
+// Strings with constrained length.
template <std::size_t M>
struct StaticString<Truncated<M>, void> {
static constexpr std::size_t N = M;
diff --git a/include/ftl/details/type_traits.h b/include/ftl/details/type_traits.h
index 7092ec5..47bebc5 100644
--- a/include/ftl/details/type_traits.h
+++ b/include/ftl/details/type_traits.h
@@ -24,4 +24,10 @@
template <typename U>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+template <typename T>
+constexpr bool is_bool_v = std::is_same_v<remove_cvref_t<T>, bool>;
+
+template <typename T>
+constexpr bool is_char_v = std::is_same_v<remove_cvref_t<T>, char>;
+
} // namespace android::ftl::details
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h
new file mode 100644
index 0000000..35d09d7
--- /dev/null
+++ b/include/ftl/non_null.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdlib>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Enforces and documents non-null pre/post-condition for (raw or smart) pointers.
+//
+// void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr,
+// ftl::NonNull<std::size_t*> length_ptr) {
+// // No need for `nullptr` checks.
+// *length_ptr = string_ptr->length();
+// }
+//
+// const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
+// std::size_t size;
+// get_length(string_ptr, ftl::as_non_null(&size));
+// assert(size == 7u);
+//
+// For compatibility with std::unique_ptr<T> and performance with std::shared_ptr<T>, move
+// operations are allowed despite breaking the invariant:
+//
+// using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>;
+//
+// Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
+// // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point.
+// auto unique_ptr = std::move(non_null_ptr).take();
+//
+// auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr)));
+// auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr;
+//
+// return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)};
+// }
+//
+// auto ptr = ftl::as_non_null(std::make_unique<int>(42));
+// const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true);
+// assert(ptr1.get() == ptr2);
+//
+template <typename Pointer>
+class NonNull final {
+ struct Passkey {};
+
+ public:
+ // Disallow `nullptr` explicitly for clear compilation errors.
+ NonNull() = delete;
+ NonNull(std::nullptr_t) = delete;
+
+ // Copy operations.
+
+ constexpr NonNull(const NonNull&) = default;
+ constexpr NonNull& operator=(const NonNull&) = default;
+
+ constexpr const Pointer& get() const { return pointer_; }
+ constexpr explicit operator const Pointer&() const { return get(); }
+
+ // Move operations. These break the invariant, so care must be taken to avoid subsequent access.
+
+ constexpr NonNull(NonNull&&) = default;
+ constexpr NonNull& operator=(NonNull&&) = default;
+
+ constexpr Pointer take() && { return std::move(pointer_); }
+ constexpr explicit operator Pointer() && { return take(); }
+
+ // Dereferencing.
+ constexpr decltype(auto) operator*() const { return *get(); }
+ constexpr decltype(auto) operator->() const { return get(); }
+
+ // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
+ // through the passkey idiom, for clear compilation errors.
+ template <typename P>
+ constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
+ if (!pointer_) std::abort();
+ }
+
+ private:
+ template <typename P>
+ friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>;
+
+ Pointer pointer_;
+};
+
+template <typename P>
+constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
+ using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
+ return {Passkey{}, std::forward<P>(pointer)};
+}
+
+template <typename P, typename Q>
+constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() == rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index f4a1523..e911734 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -23,6 +23,7 @@
#include <unordered_map>
#include <vector>
+#include <android/os/IInputConstants.h>
#include "android/hardware/input/InputDeviceCountryCode.h"
namespace android {
@@ -57,6 +58,9 @@
// reuse values that are not associated with an input anymore.
uint16_t nonce;
+ // The bluetooth address of the device, if known.
+ std::optional<std::string> bluetoothAddress;
+
/**
* Return InputDeviceIdentifier.name that has been adjusted as follows:
* - all characters besides alphanumerics, dash,
@@ -346,6 +350,8 @@
const std::string& name, InputDeviceConfigurationFileType type);
enum ReservedInputDeviceId : int32_t {
+ // Device id representing an invalid device
+ INVALID_INPUT_DEVICE_ID = android::os::IInputConstants::INVALID_INPUT_DEVICE_ID,
// Device id of a special "virtual" keyboard that is always present.
VIRTUAL_KEYBOARD_ID = -1,
// Device id of the "built-in" keyboard if there is one.
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 55f730b..e24344b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -24,16 +24,20 @@
namespace android {
template <typename T>
-std::string constToString(const T& v) {
+inline std::string constToString(const T& v) {
return std::to_string(v);
}
+inline std::string constToString(const std::string& s) {
+ return s;
+}
+
/**
* Convert an optional type to string.
*/
template <typename T>
-std::string toString(const std::optional<T>& optional,
- std::string (*toString)(const T&) = constToString) {
+inline std::string toString(const std::optional<T>& optional,
+ std::string (*toString)(const T&) = constToString) {
return optional ? toString(*optional) : "<not set>";
}
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 294879e..62c3ae1 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -106,6 +106,9 @@
~VelocityTracker();
+ /** Return true if the axis is supported for velocity tracking, false otherwise. */
+ static bool isAxisSupported(int32_t axis);
+
// Resets the velocity tracker state.
void clear();
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index ed6f6f3..e0384f3 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
namespace android {
@@ -32,12 +34,16 @@
static sp<hardware::power::IPower> loadAidl();
static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+ static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
+ static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
private:
static std::mutex gHalMutex;
static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_3::IPower> gHalHidlV1_3 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index dfb0ff5..8028aa8 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/IPowerHintSession.h>
@@ -142,8 +144,8 @@
// Wrapper for the HIDL Power HAL v1.0.
class HidlHalWrapperV1_0 : public HalWrapper {
public:
- explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
- : mHandleV1_0(std::move(Hal)) {}
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
+ : mHandleV1_0(std::move(handleV1_0)) {}
virtual ~HidlHalWrapperV1_0() = default;
virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
@@ -154,10 +156,10 @@
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+ const sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data);
private:
- sp<hardware::power::V1_0::IPower> mHandleV1_0;
HalResult<void> setInteractive(bool enabled);
HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled);
};
@@ -165,17 +167,40 @@
// Wrapper for the HIDL Power HAL v1.1.
class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
public:
- HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
- sp<hardware::power::V1_1::IPower> handleV1_1)
- : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_1)) {}
virtual ~HidlHalWrapperV1_1() = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
uint32_t data) override;
+};
-private:
- sp<hardware::power::V1_1::IPower> mHandleV1_1;
+// Wrapper for the HIDL Power HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
+public:
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
+ : HidlHalWrapperV1_1(std::move(handleV1_2)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
+ uint32_t data) override;
+};
+
+// Wrapper for the HIDL Power HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
+public:
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
+ : HidlHalWrapperV1_2(std::move(handleV1_3)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
+ uint32_t data) override;
};
// Wrapper for the AIDL Power HAL.
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 76e3e66..5e539f2 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -49,6 +49,9 @@
"com.android.media",
"com.android.media.swcodec",
],
+ llndk: {
+ llndk_headers: true,
+ },
}
cc_library_static {
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index 2bf15d4..fddecc0 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -22,6 +22,7 @@
cc_library_static {
name: "libattestation",
+ host_supported: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index c4bb6d0..fdf4167 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -161,7 +161,7 @@
],
header_libs: [
- "libandroid_runtime_vm_headers",
+ "jni_headers",
],
export_header_lib_headers: [
@@ -315,7 +315,7 @@
},
recovery: {
exclude_header_libs: [
- "libandroid_runtime_vm_headers",
+ "jni_headers",
],
},
},
@@ -484,9 +484,6 @@
java: {
enabled: false,
},
- cpp: {
- gen_trace: false,
- },
},
}
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 05db774..a0c4334 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -81,6 +81,7 @@
bool isDeclared(const String16& name) override;
Vector<String16> getDeclaredInstances(const String16& interface) override;
std::optional<String16> updatableViaApex(const String16& name) override;
+ Vector<String16> getUpdatableNames(const String16& apexName) override;
std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
class RegistrationWaiter : public android::os::BnServiceCallback {
public:
@@ -479,6 +480,23 @@
return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
}
+Vector<String16> ServiceManagerShim::getUpdatableNames(const String16& apexName) {
+ std::vector<std::string> out;
+ if (Status status = mTheRealServiceManager->getUpdatableNames(String8(apexName).c_str(), &out);
+ !status.isOk()) {
+ ALOGW("Failed to getUpdatableNames for %s: %s", String8(apexName).c_str(),
+ status.toString8().c_str());
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnectionInfo(
const String16& name) {
std::optional<os::ConnectionInfo> connectionInfo;
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
index 77e401f..ce60e33 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS.cpp
@@ -67,7 +67,7 @@
return RpcTransportCtxFactoryRaw::make();
}
-int sendMessageOnSocket(
+ssize_t sendMessageOnSocket(
const RpcTransportFd& socket, iovec* iovs, int niovs,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
@@ -112,7 +112,7 @@
return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
}
-int receiveMessageFromSocket(
+ssize_t receiveMessageFromSocket(
const RpcTransportFd& socket, iovec* iovs, int niovs,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
if (ancillaryFds != nullptr) {
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 0d38968..fecae31 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -33,11 +33,11 @@
std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory();
-int sendMessageOnSocket(
+ssize_t sendMessageOnSocket(
const RpcTransportFd& socket, iovec* iovs, int niovs,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
-int receiveMessageFromSocket(
+ssize_t receiveMessageFromSocket(
const RpcTransportFd& socket, iovec* iovs, int niovs,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 7d6bcfc..ce6ef2b 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -46,8 +46,8 @@
#include "Utils.h"
#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
-#include <android_runtime/vm.h>
#include <jni.h>
+extern "C" JavaVM* AndroidRuntimeGetJavaVM();
#endif
namespace android {
@@ -408,10 +408,11 @@
"Unable to detach thread. No JavaVM, but it was present before!");
LOG_RPC_DETAIL("Detaching current thread from JVM");
- if (vm->DetachCurrentThread() != JNI_OK) {
+ int ret = vm->DetachCurrentThread();
+ if (ret == JNI_OK) {
mAttached = false;
} else {
- ALOGW("Unable to detach current thread from JVM");
+ ALOGW("Unable to detach current thread from JVM (%d)", ret);
}
}
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 1912d14..cd067bf 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -61,7 +61,8 @@
override {
bool sentFds = false;
auto send = [&](iovec* iovs, int niovs) -> ssize_t {
- int ret = sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
+ ssize_t ret =
+ sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
sentFds |= ret > 0;
return ret;
};
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 5880c0a..0fb1615 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -114,6 +114,12 @@
@nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name);
/**
+ * Returns all instances which are updatable via the APEX. Instance names are fully qualified
+ * like `pack.age.IFoo/default`.
+ */
+ @utf8InCpp String[] getUpdatableNames(@utf8InCpp String apexName);
+
+ /**
* If connection info is available for the given instance, returns the ConnectionInfo
*/
@nullable ConnectionInfo getConnectionInfo(@utf8InCpp String name);
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 413c97f..79e771f 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -115,6 +115,12 @@
virtual std::optional<String16> updatableViaApex(const String16& name) = 0;
/**
+ * Returns all instances which are updatable via the APEX. Instance names are fully qualified
+ * like `pack.age.IFoo/default`.
+ */
+ virtual Vector<String16> getUpdatableNames(const String16& apexName) = 0;
+
+ /**
* If this instance has declared remote connection information, returns
* the ConnectionInfo.
*/
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 5baa4d7..e4a9f99 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -24,23 +24,23 @@
// Starts an RPC server on a given port and a given root IBinder object.
// This function sets up the server and joins before returning.
-bool RunRpcServer(AIBinder* service, unsigned int port);
+bool RunVsockRpcServer(AIBinder* service, unsigned int port);
// Starts an RPC server on a given port and a given root IBinder object.
// This function sets up the server, calls readyCallback with a given param, and
// then joins before returning.
-bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
- void* param);
+bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
+ void (*readyCallback)(void* param), void* param);
// Starts an RPC server on a given port and a given root IBinder factory.
-// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of
+// RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
// assigning single root IBinder object to all connections, factory is called
// whenever a client connects, making it possible to assign unique IBinder
// object to each client.
-bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port);
+bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
+AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
// Connect to an RPC server with preconnected file descriptors.
//
@@ -50,5 +50,4 @@
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
-
}
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index a3d42b7..1f38bb9 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -30,8 +30,8 @@
extern "C" {
-bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port) {
+bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port) {
auto server = RpcServer::make();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
@@ -52,8 +52,8 @@
return true;
}
-bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
- void* param) {
+bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
+ void (*readyCallback)(void* param), void* param) {
auto server = RpcServer::make();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
@@ -70,11 +70,11 @@
return true;
}
-bool RunRpcServer(AIBinder* service, unsigned int port) {
- return RunRpcServerCallback(service, port, nullptr, nullptr);
+bool RunVsockRpcServer(AIBinder* service, unsigned int port) {
+ return RunVsockRpcServerCallback(service, port, nullptr, nullptr);
}
-AIBinder* RpcClient(unsigned int cid, unsigned int port) {
+AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
auto session = RpcSession::make();
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index e856569..347831a 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -1,8 +1,8 @@
LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
global:
- RunRpcServer;
- RunRpcServerCallback;
- RpcClient;
+ RunVsockRpcServer;
+ RunVsockRpcServerCallback;
+ VsockRpcClient;
RpcPreconnectedClient;
local:
*;
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 78bcb43..81975e7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -291,7 +291,10 @@
binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
- return interface->dump(fd, args, numArgs);
+ if (interface != nullptr) {
+ return interface->dump(fd, args, numArgs);
+ }
+ return STATUS_DEAD_OBJECT;
}
#ifdef HAS_BINDER_SHELL_COMMAND
@@ -299,7 +302,10 @@
int err, const char** argv,
uint32_t argc) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
- return interface->handleShellCommand(in, out, err, argv, argc);
+ if (interface != nullptr) {
+ return interface->handleShellCommand(in, out, err, argv, argc);
+ }
+ return STATUS_DEAD_OBJECT;
}
#endif
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index dfa8ea2..c234270 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -143,6 +143,17 @@
bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31);
/**
+ * Returns the APEX name if a service is declared as updatable via an APEX module.
+ *
+ * \param instance identifier of the service
+ * \param context to pass to callback
+ * \param callback taking the APEX name (e.g. 'com.android.foo') and context
+ */
+void AServiceManager_getUpdatableApexName(const char* instance, void* context,
+ void (*callback)(const char*, void*))
+ __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
* Prevent lazy services without client from shutting down their process
*
* This should only be used if it is every eventually set to false. If a
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 259a736..32ca564 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -152,6 +152,11 @@
AParcel_unmarshal;
};
+LIBBINDER_NDK34 { # introduced=UpsideDownCake
+ global:
+ AServiceManager_getUpdatableApexName; # systemapi
+};
+
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 7649a26..a12d0e9 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -113,6 +113,18 @@
sp<IServiceManager> sm = defaultServiceManager();
return sm->updatableViaApex(String16(instance)) != std::nullopt;
}
+void AServiceManager_getUpdatableApexName(const char* instance, void* context,
+ void (*callback)(const char*, void*)) {
+ CHECK_NE(instance, nullptr);
+ // context may be nullptr
+ CHECK_NE(callback, nullptr);
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance));
+ if (updatableViaApex.has_value()) {
+ callback(String8(updatableViaApex.value()).c_str(), context);
+ }
+}
void AServiceManager_forceLazyServicesPersist(bool persist) {
auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
serviceRegistrar.forcePersist(persist);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 01b9472..e221e4c 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -33,13 +33,15 @@
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
#include <binder/IShellCallback.h>
-
#include <sys/prctl.h>
+
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
+#include <optional>
#include <thread>
+
#include "android/binder_ibinder.h"
using namespace android;
@@ -337,6 +339,16 @@
EXPECT_EQ(isUpdatable, false);
}
+TEST(NdkBinder, GetUpdatableViaApex) {
+ std::optional<std::string> updatableViaApex;
+ AServiceManager_getUpdatableApexName(
+ "android.hardware.light.ILights/default", &updatableViaApex,
+ [](const char* apexName, void* context) {
+ *static_cast<std::optional<std::string>*>(context) = apexName;
+ });
+ EXPECT_EQ(updatableViaApex, std::nullopt) << *updatableViaApex;
+}
+
// This is too slow
TEST(NdkBinder, CheckLazyServiceShutDown) {
ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index a135796..738d16a 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -88,6 +88,7 @@
min_sdk_version: "Tiramisu",
lints: "none",
clippy_lints: "none",
+ visibility: [":__subpackages__"],
}
rust_bindgen {
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
index 743800b..4343ff4 100644
--- a/libs/binder/rust/rpcbinder/src/client.rs
+++ b/libs/binder/rust/rpcbinder/src/client.rs
@@ -22,9 +22,9 @@
/// Connects to an RPC Binder server over vsock.
pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
- // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can
- // safely be taken by new_spibinder.
- unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port)) }
+ // SAFETY: AIBinder returned by VsockRpcClient has correct reference count,
+ // and the ownership can safely be taken by new_spibinder.
+ unsafe { new_spibinder(binder_rpc_unstable_bindgen::VsockRpcClient(cid, port)) }
}
/// Connects to an RPC Binder server for a particular interface over vsock.
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index a5eea61..fb6b90c 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -23,4 +23,4 @@
get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface,
get_vsock_rpc_service,
};
-pub use server::{run_rpc_server, run_rpc_server_with_factory};
+pub use server::{run_vsock_rpc_server, run_vsock_rpc_server_with_factory};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index aeb23c6..8009297 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -30,7 +30,7 @@
/// The current thread is joined to the binder thread pool to handle incoming messages.
///
/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
+pub fn run_vsock_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
where
F: FnOnce(),
{
@@ -52,10 +52,10 @@
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- // RunRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
+ // RunVsockRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
// uses them before it returns, which is during the lifetime of `self`.
unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerCallback(
+ binder_rpc_unstable_bindgen::RunVsockRpcServerCallback(
service,
port,
Some(Self::ready_callback),
@@ -69,7 +69,7 @@
}
unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
- // SAFETY: This is only ever called by `RunRpcServerCallback`, within the lifetime of the
+ // SAFETY: This is only ever called by `RunVsockRpcServerCallback`, within the lifetime of the
// `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly
// aligned non-null pointer to an initialized instance).
let ready_notifier = param as *mut Self;
@@ -91,7 +91,7 @@
/// The current thread is joined to the binder thread pool to handle incoming messages.
///
/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_rpc_server_with_factory(
+pub fn run_vsock_rpc_server_with_factory(
port: u32,
mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync,
) -> bool {
@@ -100,18 +100,22 @@
let mut factory_ref: RpcServerFactoryRef = &mut factory;
let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void;
- // SAFETY: `factory_wrapper` is only ever called by `RunRpcServerWithFactory`, with context
+ // SAFETY: `factory_wrapper` is only ever called by `RunVsockRpcServerWithFactory`, with context
// taking the pointer value above (so a properly aligned non-null pointer to an initialized
// `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made
- // after `RunRpcServerWithFactory` returns).
+ // after `RunVsockRpcServerWithFactory` returns).
unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerWithFactory(Some(factory_wrapper), context, port)
+ binder_rpc_unstable_bindgen::RunVsockRpcServerWithFactory(
+ Some(factory_wrapper),
+ context,
+ port,
+ )
}
}
unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
// SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
- // `run_rpc_server_with_factory`, and we are still within the lifetime of the value it is
+ // `run_vsock_rpc_server_with_factory`, and we are still within the lifetime of the value it is
// pointing to.
let factory_ptr = context as *mut RpcServerFactoryRef;
let factory = factory_ptr.as_mut().unwrap();
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 777f3c9..692cc95 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -156,6 +156,10 @@
std::optional<std::string>* _aidl_return) override {
return mImpl->updatableViaApex(name, _aidl_return);
}
+ android::binder::Status getUpdatableNames(const std::string& apexName,
+ std::vector<std::string>* _aidl_return) override {
+ return mImpl->getUpdatableNames(apexName, _aidl_return);
+ }
android::binder::Status getConnectionInfo(
const std::string& name,
std::optional<android::os::ConnectionInfo>* _aidl_return) override {
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 397ff41..8ec9823 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -59,14 +59,14 @@
return RpcTransportCtxFactoryTipcTrusty::make();
}
-int sendMessageOnSocket(
+ssize_t sendMessageOnSocket(
const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
errno = ENOTSUP;
return -1;
}
-int receiveMessageFromSocket(
+ssize_t receiveMessageFromSocket(
const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
errno = ENOTSUP;
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index a6585c5..067ce17 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -208,5 +208,5 @@
cmdline = std::string(cmdline.c_str());
return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" ||
- cmdline == "usap64";
+ cmdline == "usap64" || cmdline == "webview_zygote";
}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6c6d7f3..480ec79 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -78,6 +78,11 @@
return std::nullopt;
}
+Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+ (void)apexName;
+ return {};
+}
+
std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
const String16& name) {
(void)name;
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
index e0af5d4..ee0637e 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
@@ -52,6 +52,8 @@
std::optional<String16> updatableViaApex(const String16& name) override;
+ Vector<String16> getUpdatableNames(const String16& apexName) override;
+
std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
status_t registerForNotifications(const String16& name,
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index c1945fd..81113bc 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -22,6 +22,7 @@
"flags_test.cpp",
"future_test.cpp",
"match_test.cpp",
+ "non_null_test.cpp",
"optional_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp
index 8ecb1b2..771f054 100644
--- a/libs/ftl/concat_test.cpp
+++ b/libs/ftl/concat_test.cpp
@@ -28,8 +28,25 @@
EXPECT_EQ(string.c_str()[string.size()], '\0');
}
+TEST(Concat, Characters) {
+ EXPECT_EQ(ftl::Concat(u'a', ' ', U'b').str(), "97 98");
+}
+
+TEST(Concat, References) {
+ int i[] = {-1, 2};
+ unsigned u = 3;
+ EXPECT_EQ(ftl::Concat(i[0], std::as_const(i[1]), u).str(), "-123");
+
+ const bool b = false;
+ const char c = 'o';
+ EXPECT_EQ(ftl::Concat(b, "tt", c).str(), "falsetto");
+}
+
namespace {
+static_assert(ftl::Concat{true, false, true}.str() == "truefalsetrue");
+static_assert(ftl::Concat{':', '-', ')'}.str() == ":-)");
+
static_assert(ftl::Concat{"foo"}.str() == "foo");
static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo");
diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp
new file mode 100644
index 0000000..bd0462b
--- /dev/null
+++ b/libs/ftl/non_null_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/non_null.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+namespace android::test {
+namespace {
+
+void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr,
+ ftl::NonNull<std::size_t*> length_ptr) {
+ // No need for `nullptr` checks.
+ *length_ptr = string_ptr->length();
+}
+
+using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>;
+
+Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
+ // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point.
+ auto unique_ptr = std::move(non_null_ptr).take();
+
+ auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr)));
+ auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr;
+
+ return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)};
+}
+
+} // namespace
+
+// Keep in sync with example usage in header file.
+TEST(NonNull, Example) {
+ const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
+ std::size_t size;
+ get_length(string_ptr, ftl::as_non_null(&size));
+ EXPECT_EQ(size, 7u);
+
+ auto ptr = ftl::as_non_null(std::make_unique<int>(42));
+ const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true);
+ EXPECT_EQ(ptr1.get(), ptr2);
+}
+
+namespace {
+
+constexpr std::string_view kApple = "apple";
+constexpr std::string_view kOrange = "orange";
+
+using StringViewPtr = ftl::NonNull<const std::string_view*>;
+constexpr StringViewPtr kApplePtr = ftl::as_non_null(&kApple);
+constexpr StringViewPtr kOrangePtr = ftl::as_non_null(&kOrange);
+
+constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) {
+ return ptr1->length() > ptr2->length() ? ptr1 : ptr2;
+}
+
+static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr);
+
+} // namespace
+} // namespace android::test
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 23a2181..a988e39 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -191,8 +191,6 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
"BufferItemConsumer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
@@ -228,9 +226,6 @@
shared_libs: [
"libbinder",
- "libbufferhub",
- "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
- "libpdx_default_transport",
],
export_shared_lib_headers: [
@@ -241,24 +236,6 @@
"libgui_aidl_headers",
],
- // bufferhub is not used when building libgui for vendors
- target: {
- vendor: {
- cflags: [
- "-DNO_BUFFERHUB",
- ],
- exclude_srcs: [
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
- ],
- exclude_shared_libs: [
- "libbufferhub",
- "libbufferhubqueue",
- "libpdx_default_transport",
- ],
- },
- },
-
aidl: {
export_aidl_headers: true,
},
@@ -288,7 +265,6 @@
min_sdk_version: "29",
cflags: [
- "-DNO_BUFFERHUB",
"-DNO_BINDER",
],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 8b9a878..0021bd6 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -167,14 +167,15 @@
mNumFrameAvailable = 0;
TransactionCompletedListener::getInstance()->addQueueStallListener(
- [&]() {
- std::function<void(bool)> callbackCopy;
- {
- std::unique_lock _lock{mMutex};
- callbackCopy = mTransactionHangCallback;
- }
- if (callbackCopy) callbackCopy(true);
- }, this);
+ [&](const std::string& reason) {
+ std::function<void(const std::string&)> callbackCopy;
+ {
+ std::unique_lock _lock{mMutex};
+ callbackCopy = mTransactionHangCallback;
+ }
+ if (callbackCopy) callbackCopy(reason);
+ },
+ this);
BQA_LOGV("BLASTBufferQueue created");
}
@@ -334,9 +335,11 @@
std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC);
if (statsOptional) {
SurfaceControlStats stat = *statsOptional;
- mTransformHint = stat.transformHint;
- mBufferItemConsumer->setTransformHint(mTransformHint);
- BQA_LOGV("updated mTransformHint=%d", mTransformHint);
+ if (stat.transformHint) {
+ mTransformHint = *stat.transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ BQA_LOGV("updated mTransformHint=%d", mTransformHint);
+ }
// Update frametime stamps if the frame was latched and presented, indicated by a
// valid latch time.
if (stat.latchTime > 0) {
@@ -1112,7 +1115,8 @@
return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
}
-void BLASTBufferQueue::setTransactionHangCallback(std::function<void(bool)> callback) {
+void BLASTBufferQueue::setTransactionHangCallback(
+ std::function<void(const std::string&)> callback) {
std::unique_lock _lock{mMutex};
mTransactionHangCallback = callback;
}
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
deleted file mode 100644
index b5cdeb2..0000000
--- a/libs/gui/BufferHubConsumer.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/BufferHubConsumer.h>
-
-namespace android {
-
-using namespace dvr;
-
-/* static */
-sp<BufferHubConsumer> BufferHubConsumer::Create(const std::shared_ptr<ConsumerQueue>& queue) {
- sp<BufferHubConsumer> consumer = new BufferHubConsumer;
- consumer->mQueue = queue;
- return consumer;
-}
-
-/* static */ sp<BufferHubConsumer> BufferHubConsumer::Create(ConsumerQueueParcelable parcelable) {
- if (!parcelable.IsValid()) {
- ALOGE("BufferHubConsumer::Create: Invalid consumer parcelable.");
- return nullptr;
- }
-
- sp<BufferHubConsumer> consumer = new BufferHubConsumer;
- consumer->mQueue = ConsumerQueue::Import(parcelable.TakeChannelHandle());
- return consumer;
-}
-
-status_t BufferHubConsumer::acquireBuffer(BufferItem* /*buffer*/, nsecs_t /*presentWhen*/,
- uint64_t /*maxFrameNumber*/) {
- ALOGE("BufferHubConsumer::acquireBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::detachBuffer(int /*slot*/) {
- ALOGE("BufferHubConsumer::detachBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::attachBuffer(int* /*outSlot*/, const sp<GraphicBuffer>& /*buffer*/) {
- ALOGE("BufferHubConsumer::attachBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::releaseBuffer(int /*buf*/, uint64_t /*frameNumber*/,
- EGLDisplay /*display*/, EGLSyncKHR /*fence*/,
- const sp<Fence>& /*releaseFence*/) {
- ALOGE("BufferHubConsumer::releaseBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::consumerConnect(const sp<IConsumerListener>& /*consumer*/,
- bool /*controlledByApp*/) {
- ALOGE("BufferHubConsumer::consumerConnect: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::consumerDisconnect() {
- ALOGE("BufferHubConsumer::consumerDisconnect: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::getReleasedBuffers(uint64_t* /*slotMask*/) {
- ALOGE("BufferHubConsumer::getReleasedBuffers: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferSize: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setMaxBufferCount(int /*bufferCount*/) {
- ALOGE("BufferHubConsumer::setMaxBufferCount: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setMaxAcquiredBufferCount(int /*maxAcquiredBuffers*/) {
- ALOGE("BufferHubConsumer::setMaxAcquiredBufferCount: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::setConsumerName(const String8& /*name*/) {
- ALOGE("BufferHubConsumer::setConsumerName: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferFormat(PixelFormat /*defaultFormat*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferFormat: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferDataSpace(android_dataspace /*defaultDataSpace*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferDataSpace: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setConsumerUsageBits(uint64_t /*usage*/) {
- ALOGE("BufferHubConsumer::setConsumerUsageBits: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setConsumerIsProtected(bool /*isProtected*/) {
- ALOGE("BufferHubConsumer::setConsumerIsProtected: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setTransformHint(uint32_t /*hint*/) {
- ALOGE("BufferHubConsumer::setTransformHint: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getSidebandStream(sp<NativeHandle>* /*outStream*/) const {
- ALOGE("BufferHubConsumer::getSidebandStream: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getOccupancyHistory(
- bool /*forceFlush*/, std::vector<OccupancyTracker::Segment>* /*outHistory*/) {
- ALOGE("BufferHubConsumer::getOccupancyHistory: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::discardFreeBuffers() {
- ALOGE("BufferHubConsumer::discardFreeBuffers: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const {
- ALOGE("BufferHubConsumer::dumpState: not implemented.");
- return INVALID_OPERATION;
-}
-
-IBinder* BufferHubConsumer::onAsBinder() {
- ALOGE("BufferHubConsumer::onAsBinder: BufferHubConsumer should never be used as an Binder "
- "object.");
- return nullptr;
-}
-
-} // namespace android
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
deleted file mode 100644
index 1f71e23..0000000
--- a/libs/gui/BufferHubProducer.cpp
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Copyright 2018 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 <dvr/dvr_api.h>
-#include <gui/BufferHubProducer.h>
-#include <inttypes.h>
-#include <log/log.h>
-#include <system/window.h>
-
-namespace android {
-
-using namespace dvr;
-
-/* static */
-sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<ProducerQueue>& queue) {
- sp<BufferHubProducer> producer = new BufferHubProducer;
- producer->queue_ = queue;
- return producer;
-}
-
-/* static */
-sp<BufferHubProducer> BufferHubProducer::Create(ProducerQueueParcelable parcelable) {
- if (!parcelable.IsValid()) {
- ALOGE("BufferHubProducer::Create: Invalid producer parcelable.");
- return nullptr;
- }
-
- sp<BufferHubProducer> producer = new BufferHubProducer;
- producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle());
- return producer;
-}
-
-status_t BufferHubProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- ALOGV("requestBuffer: slot=%d", slot);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("requestBuffer: BufferHubProducer has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (buffers_[slot].mGraphicBuffer != nullptr) {
- ALOGE("requestBuffer: slot %d is not empty.", slot);
- return BAD_VALUE;
- } else if (buffers_[slot].mProducerBuffer == nullptr) {
- ALOGE("requestBuffer: slot %d is not dequeued.", slot);
- return BAD_VALUE;
- }
-
- const auto& producer_buffer = buffers_[slot].mProducerBuffer;
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
-
- buffers_[slot].mGraphicBuffer = graphic_buffer;
- buffers_[slot].mRequestBufferCalled = true;
-
- *buf = graphic_buffer;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) {
- ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (max_dequeued_buffers <= 0 ||
- max_dequeued_buffers >
- int(BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
- ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers,
- BufferHubQueue::kMaxQueueCapacity);
- return BAD_VALUE;
- }
-
- // The new dequeued_buffers count should not be violated by the number
- // of currently dequeued buffers.
- int dequeued_count = 0;
- for (const auto& buf : buffers_) {
- if (buf.mBufferState.isDequeued()) {
- dequeued_count++;
- }
- }
- if (dequeued_count > max_dequeued_buffers) {
- ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers"
- "count (%d) exceeds the current dequeued buffer count (%d)",
- max_dequeued_buffers, dequeued_count);
- return BAD_VALUE;
- }
-
- max_dequeued_buffer_count_ = max_dequeued_buffers;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setAsyncMode(bool async) {
- if (async) {
- // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
- // automatically and behaves differently from IGraphicBufferConsumer. Thus,
- // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
- // to prevent dequeueBuffer from being blocking) technically does not apply
- // here.
- //
- // In Daydream, non-blocking producer side dequeue is guaranteed by careful
- // buffer consumer implementations. In another word, BufferHubQueue based
- // dequeueBuffer should never block whether setAsyncMode(true) is set or
- // not.
- //
- // See: IGraphicBufferProducer::setAsyncMode and
- // BufferQueueProducer::setAsyncMode for more about original implementation.
- ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be "
- "asynchronous. This call makes no effact.");
- return NO_ERROR;
- }
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
- uint32_t height, PixelFormat format, uint64_t usage,
- uint64_t* /*outBufferAge*/,
- FrameEventHistoryDelta* /* out_timestamps */) {
- ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);
-
- status_t ret;
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("dequeueBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- const uint32_t kLayerCount = 1;
- if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
- // Lazy allocation. When the capacity of |queue_| has not reached
- // |max_dequeued_buffer_count_|, allocate new buffer.
- // TODO(jwcai) To save memory, the really reasonable thing to do is to go
- // over existing slots and find first existing one to dequeue.
- ret = AllocateBuffer(width, height, kLayerCount, format, usage);
- if (ret < 0) return ret;
- }
-
- size_t slot = 0;
- std::shared_ptr<ProducerBuffer> producer_buffer;
-
- for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
- LocalHandle fence;
- auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
- if (!buffer_status) return NO_MEMORY;
-
- producer_buffer = buffer_status.take();
- if (!producer_buffer) return NO_MEMORY;
-
- if (width == producer_buffer->width() && height == producer_buffer->height() &&
- uint32_t(format) == producer_buffer->format()) {
- // The producer queue returns a producer buffer matches the request.
- break;
- }
-
- // Needs reallocation.
- // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
- ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
- "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
- "re-allocattion.",
- width, height, format, slot, producer_buffer->width(), producer_buffer->height(),
- producer_buffer->format());
- // Mark the slot as reallocating, so that later we can set
- // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
- buffers_[slot].mIsReallocating = true;
-
- // Remove the old buffer once the allocation before allocating its
- // replacement.
- RemoveBuffer(slot);
-
- // Allocate a new producer buffer with new buffer configs. Note that if
- // there are already multiple buffers in the queue, the next one returned
- // from |queue_->Dequeue| may not be the new buffer we just reallocated.
- // Retry up to BufferHubQueue::kMaxQueueCapacity times.
- ret = AllocateBuffer(width, height, kLayerCount, format, usage);
- if (ret < 0) return ret;
- }
-
- // With the BufferHub backed solution. Buffer slot returned from
- // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
- // It's either in free state (if the buffer has never been used before) or
- // in queued state (if the buffer has been dequeued and queued back to
- // BufferHubQueue).
- LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
- !buffers_[slot].mBufferState.isQueued()),
- "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
- buffers_[slot].mBufferState.string());
-
- buffers_[slot].mBufferState.freeQueued();
- buffers_[slot].mBufferState.dequeue();
- ALOGV("dequeueBuffer: slot=%zu", slot);
-
- // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
- // just need to exopose that through |BufferHubQueue| once we need fence.
- *out_fence = Fence::NO_FENCE;
- *out_slot = int(slot);
- ret = NO_ERROR;
-
- if (buffers_[slot].mIsReallocating) {
- ret |= BUFFER_NEEDS_REALLOCATION;
- buffers_[slot].mIsReallocating = false;
- }
-
- return ret;
-}
-
-status_t BufferHubProducer::detachBuffer(int slot) {
- ALOGV("detachBuffer: slot=%d", slot);
- std::unique_lock<std::mutex> lock(mutex_);
-
- return DetachBufferLocked(static_cast<size_t>(slot));
-}
-
-status_t BufferHubProducer::DetachBufferLocked(size_t slot) {
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("detachBuffer: BufferHubProducer is not connected.");
- return NO_INIT;
- }
-
- if (slot >= static_cast<size_t>(max_buffer_count_)) {
- ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (!buffers_[slot].mRequestBufferCalled) {
- ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot);
- return BAD_VALUE;
- }
- std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot);
- if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) {
- ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot);
- return BAD_VALUE;
- }
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
- if (graphic_buffer == nullptr) {
- ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot);
- return BAD_VALUE;
- }
-
- // Remove the ProducerBuffer from the ProducerQueue.
- status_t error = RemoveBuffer(slot);
- if (error != NO_ERROR) {
- ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error);
- return error;
- }
-
- // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject
- // the handle into the GraphicBuffer object at the requested slot.
- auto status_or_handle = producer_buffer->Detach();
- if (!status_or_handle.ok()) {
- ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot,
- status_or_handle.error());
- return BAD_VALUE;
- }
-
- // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
- // be directly backed by BufferHub.
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) {
- ALOGV("detachNextBuffer.");
-
- if (out_buffer == nullptr || out_fence == nullptr) {
- ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer,
- out_fence);
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("detachNextBuffer: BufferHubProducer is not connected.");
- return NO_INIT;
- }
-
- // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in
- // sequence, except for two things:
- //
- // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the
- // function just returns whatever ProducerBuffer is available from the ProducerQueue and no
- // buffer allocation or re-allocation will happen.
- // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return
- // an error instead.
- size_t slot = 0;
- LocalHandle fence;
-
- // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error
- // immediately if ProducerQueue::Dequeue() fails.
- auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence);
- if (!status_or_buffer.ok()) {
- ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error());
- return NO_MEMORY;
- }
-
- std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take();
- if (producer_buffer == nullptr) {
- ALOGE("detachNextBuffer: Dequeued buffer is null.");
- return NO_MEMORY;
- }
-
- // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to
- // be available for producer's use. It's either in free state (if the buffer has never been used
- // before) or in queued state (if the buffer has been dequeued and queued back to
- // BufferHubQueue).
- if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) {
- ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- }
- if (buffers_[slot].mProducerBuffer == nullptr) {
- ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot);
- return BAD_VALUE;
- }
- if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) {
- ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: "
- "%d, expected: %d.",
- slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id());
- return BAD_VALUE;
- }
-
- ALOGV("detachNextBuffer: slot=%zu", slot);
- buffers_[slot].mBufferState.freeQueued();
- buffers_[slot].mBufferState.dequeue();
-
- // Second, request the buffer.
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
- buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer();
-
- // Finally, detach the buffer and then return.
- status_t error = DetachBufferLocked(slot);
- if (error == NO_ERROR) {
- *out_fence = new Fence(fence.Release());
- *out_buffer = graphic_buffer;
- }
- return error;
-}
-
-status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) {
- // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only
- // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer.
- ALOGV("queueBuffer: buffer=%p", buffer.get());
-
- if (out_slot == nullptr) {
- ALOGE("attachBuffer: out_slot cannot be NULL.");
- return BAD_VALUE;
- }
- if (buffer == nullptr) {
- ALOGE("attachBuffer: invalid GraphicBuffer.");
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("attachBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- // Before attaching the buffer, caller is supposed to call
- // IGraphicBufferProducer::setGenerationNumber to inform the
- // BufferHubProducer the next generation number.
- if (buffer->getGenerationNumber() != generation_number_) {
- ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.",
- buffer->getGenerationNumber(), generation_number_);
- return BAD_VALUE;
- }
-
- // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
- // be directly backed by BufferHub.
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) {
- ALOGV("queueBuffer: slot %d", slot);
-
- if (output == nullptr) {
- return BAD_VALUE;
- }
-
- int64_t timestamp;
- bool is_auto_timestamp;
- android_dataspace dataspace;
- Rect crop(Rect::EMPTY_RECT);
- int scaling_mode;
- uint32_t transform;
- sp<Fence> fence;
-
- input.deflate(×tamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform,
- &fence);
-
- // Check input scaling mode is valid.
- switch (scaling_mode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
- break;
- default:
- ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
- return BAD_VALUE;
- }
-
- // Check input fence is valid.
- if (fence == nullptr) {
- ALOGE("queueBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("queueBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) {
- ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
- "mGraphicBuffer=%p)",
- slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get());
- return BAD_VALUE;
- }
-
- // Post the producer buffer with timestamp in the metadata.
- const auto& producer_buffer = buffers_[slot].mProducerBuffer;
-
- // Check input crop is not out of boundary of current buffer.
- Rect buffer_rect(producer_buffer->width(), producer_buffer->height());
- Rect cropped_rect(Rect::EMPTY_RECT);
- crop.intersect(buffer_rect, &cropped_rect);
- if (cropped_rect != crop) {
- ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
- return BAD_VALUE;
- }
-
- LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
-
- DvrNativeBufferMetadata meta_data;
- meta_data.timestamp = timestamp;
- meta_data.is_auto_timestamp = int32_t(is_auto_timestamp);
- meta_data.dataspace = int32_t(dataspace);
- meta_data.crop_left = crop.left;
- meta_data.crop_top = crop.top;
- meta_data.crop_right = crop.right;
- meta_data.crop_bottom = crop.bottom;
- meta_data.scaling_mode = int32_t(scaling_mode);
- meta_data.transform = int32_t(transform);
-
- producer_buffer->PostAsync(&meta_data, fence_fd);
- buffers_[slot].mBufferState.queue();
-
- output->width = producer_buffer->width();
- output->height = producer_buffer->height();
- output->transformHint = 0; // default value, we don't use it yet.
-
- // |numPendingBuffers| counts of the number of buffers that has been enqueued
- // by the producer but not yet acquired by the consumer. Due to the nature
- // of BufferHubQueue design, this is hard to trace from the producer's client
- // side, but it's safe to assume it's zero.
- output->numPendingBuffers = 0;
-
- // Note that we are not setting nextFrameNumber here as it seems to be only
- // used by surface flinger. See more at b/22802885, ag/791760.
- output->nextFrameNumber = 0;
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("cancelBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (fence == nullptr) {
- ALOGE("cancelBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- auto producer_buffer = buffers_[slot].mProducerBuffer;
- queue_->Enqueue(producer_buffer, size_t(slot), 0U);
- buffers_[slot].mBufferState.cancel();
- buffers_[slot].mFence = fence;
- ALOGV("cancelBuffer: slot %d", slot);
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::query(int what, int* out_value) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (out_value == nullptr) {
- ALOGE("query: out_value was NULL");
- return BAD_VALUE;
- }
-
- int value = 0;
- switch (what) {
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- // TODO(b/36187402) This should be the maximum number of buffers that this
- // producer queue's consumer can acquire. Set to be at least one. Need to
- // find a way to set from the consumer side.
- value = kDefaultUndequeuedBuffers;
- break;
- case NATIVE_WINDOW_BUFFER_AGE:
- value = 0;
- break;
- case NATIVE_WINDOW_WIDTH:
- value = int32_t(queue_->default_width());
- break;
- case NATIVE_WINDOW_HEIGHT:
- value = int32_t(queue_->default_height());
- break;
- case NATIVE_WINDOW_FORMAT:
- value = int32_t(queue_->default_format());
- break;
- case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
- // BufferHubQueue is always operating in async mode, thus semantically
- // consumer can never be running behind. See BufferQueueCore.cpp core
- // for more information about the original meaning of this flag.
- value = 0;
- break;
- case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
- // TODO(jwcai) This is currently not implement as we don't need
- // IGraphicBufferConsumer parity.
- value = 0;
- break;
- case NATIVE_WINDOW_DEFAULT_DATASPACE:
- // TODO(jwcai) Return the default value android::BufferQueue is using as
- // there is no way dvr::ConsumerQueue can set it.
- value = 0; // HAL_DATASPACE_UNKNOWN
- break;
- case NATIVE_WINDOW_STICKY_TRANSFORM:
- // TODO(jwcai) Return the default value android::BufferQueue is using as
- // there is no way dvr::ConsumerQueue can set it.
- value = 0;
- break;
- case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
- // In Daydream's implementation, the consumer end (i.e. VR Compostior)
- // knows how to handle protected buffers.
- value = 1;
- break;
- default:
- return BAD_VALUE;
- }
-
- ALOGV("query: key=%d, v=%d", what, value);
- *out_value = value;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::connect(const sp<IProducerListener>& /* listener */, int api,
- bool /* producer_controlled_by_app */,
- QueueBufferOutput* output) {
- // Consumer interaction are actually handled by buffer hub, and we need
- // to maintain consumer operations here. We only need to perform basic input
- // parameter checks here.
- ALOGV(__FUNCTION__);
-
- if (output == nullptr) {
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ != kNoConnectedApi) {
- return BAD_VALUE;
- }
-
- if (!queue_->is_connected()) {
- ALOGE("BufferHubProducer::connect: This BufferHubProducer is not "
- "connected to bufferhud. Has it been taken out as a parcelable?");
- return BAD_VALUE;
- }
-
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- case NATIVE_WINDOW_API_CPU:
- case NATIVE_WINDOW_API_MEDIA:
- case NATIVE_WINDOW_API_CAMERA:
- connected_api_ = api;
-
- output->width = queue_->default_width();
- output->height = queue_->default_height();
-
- // default values, we don't use them yet.
- output->transformHint = 0;
- output->numPendingBuffers = 0;
- output->nextFrameNumber = 0;
- output->bufferReplaced = false;
-
- break;
- default:
- ALOGE("BufferHubProducer::connect: unknow API %d", api);
- return BAD_VALUE;
- }
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) {
- // Consumer interaction are actually handled by buffer hub, and we need
- // to maintain consumer operations here. We only need to perform basic input
- // parameter checks here.
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (kNoConnectedApi == connected_api_) {
- return NO_INIT;
- } else if (api != connected_api_) {
- return BAD_VALUE;
- }
-
- FreeAllBuffers();
- connected_api_ = kNoConnectedApi;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setSidebandStream(const sp<NativeHandle>& stream) {
- if (stream != nullptr) {
- // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
- // metadata.
- ALOGE("SidebandStream is not currently supported.");
- return INVALID_OPERATION;
- }
- return NO_ERROR;
-}
-
-void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */,
- PixelFormat /* format */, uint64_t /* usage */) {
- // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
- // of buffers permitted by the current BufferQueue configuration (aka
- // |max_buffer_count_|).
- ALOGE("BufferHubProducer::allocateBuffers not implemented.");
-}
-
-status_t BufferHubProducer::allowAllocation(bool /* allow */) {
- ALOGE("BufferHubProducer::allowAllocation not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
- generation_number_ = generation_number;
- return NO_ERROR;
-}
-
-String8 BufferHubProducer::getConsumerName() const {
- // BufferHub based implementation could have one to many producer/consumer
- // relationship, thus |getConsumerName| from the producer side does not
- // make any sense.
- ALOGE("BufferHubProducer::getConsumerName not supported.");
- return String8("BufferHubQueue::StubConsumer");
-}
-
-status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
- if (shared_buffer_mode) {
- ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported.");
- // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
- return INVALID_OPERATION;
- }
- // Setting to default should just work as a no-op.
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) {
- if (auto_refresh) {
- ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported.");
- return INVALID_OPERATION;
- }
- // Setting to default should just work as a no-op.
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
- dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::getLastQueuedBuffer(sp<GraphicBuffer>* /* out_buffer */,
- sp<Fence>* /* out_fence */,
- float /*out_transform_matrix*/[16]) {
- ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented.");
- return INVALID_OPERATION;
-}
-
-void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {
- ALOGE("BufferHubProducer::getFrameTimestamps not implemented.");
-}
-
-status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const {
- ALOGV(__FUNCTION__);
-
- *out_id = unique_id_;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const {
- ALOGV(__FUNCTION__);
-
- // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
- *out_usage = 0;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::TakeAsParcelable(ProducerQueueParcelable* out_parcelable) {
- if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE;
-
- if (connected_api_ != kNoConnectedApi) {
- ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has "
- "connected client. Must disconnect first.");
- return BAD_VALUE;
- }
-
- if (!queue_->is_connected()) {
- ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer "
- "is not connected to bufferhud. Has it been taken out as a "
- "parcelable?");
- return BAD_VALUE;
- }
-
- auto status = queue_->TakeAsParcelable();
- if (!status) {
- ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out "
- "ProducuerQueueParcelable from the producer queue, error: %s.",
- status.GetErrorMessage().c_str());
- return BAD_VALUE;
- }
-
- *out_parcelable = status.take();
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
- PixelFormat format, uint64_t usage) {
- auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage);
- if (!status) {
- ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s",
- status.GetErrorMessage().c_str());
- return NO_MEMORY;
- }
-
- size_t slot = status.get();
- auto producer_buffer = queue_->GetBuffer(slot);
-
- LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr,
- "Failed to get the producer buffer at slot: %zu", slot);
-
- buffers_[slot].mProducerBuffer = producer_buffer;
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::RemoveBuffer(size_t slot) {
- auto status = queue_->RemoveBuffer(slot);
- if (!status) {
- ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.",
- slot, status.GetErrorMessage().c_str());
- return INVALID_OPERATION;
- }
-
- // Reset in memory objects related the the buffer.
- buffers_[slot].mProducerBuffer = nullptr;
- buffers_[slot].mBufferState.detachProducer();
- buffers_[slot].mFence = Fence::NO_FENCE;
- buffers_[slot].mGraphicBuffer = nullptr;
- buffers_[slot].mRequestBufferCalled = false;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::FreeAllBuffers() {
- for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
- // Reset in memory objects related the the buffer.
- buffers_[slot].mProducerBuffer = nullptr;
- buffers_[slot].mBufferState.reset();
- buffers_[slot].mFence = Fence::NO_FENCE;
- buffers_[slot].mGraphicBuffer = nullptr;
- buffers_[slot].mRequestBufferCalled = false;
- }
-
- auto status = queue_->FreeAllBuffers();
- if (!status) {
- ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on "
- "the queue: %s",
- status.GetErrorMessage().c_str());
- }
-
- if (queue_->capacity() != 0 || queue_->count() != 0) {
- LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed.");
- }
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::exportToParcel(Parcel* parcel) {
- status_t res = TakeAsParcelable(&pending_producer_parcelable_);
- if (res != NO_ERROR) return res;
-
- if (!pending_producer_parcelable_.IsValid()) {
- ALOGE("BufferHubProducer::exportToParcel: Invalid parcelable object.");
- return BAD_VALUE;
- }
-
- res = parcel->writeUint32(USE_BUFFER_HUB);
- if (res != NO_ERROR) {
- ALOGE("BufferHubProducer::exportToParcel: Cannot write magic, res=%d.", res);
- return res;
- }
-
- return pending_producer_parcelable_.writeToParcel(parcel);
-}
-
-IBinder* BufferHubProducer::onAsBinder() {
- ALOGE("BufferHubProducer::onAsBinder: BufferHubProducer should never be used as an Binder "
- "object.");
- return nullptr;
-}
-
-} // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index c1d92a2..66cad03 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -18,11 +18,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#ifndef NO_BUFFERHUB
-#include <gui/BufferHubConsumer.h>
-#include <gui/BufferHubProducer.h>
-#endif
-
#include <gui/BufferQueue.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
@@ -127,32 +122,4 @@
*outConsumer = consumer;
}
-#ifndef NO_BUFFERHUB
-void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer) {
- LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL");
- LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL");
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
-
- dvr::ProducerQueueConfigBuilder configBuilder;
- std::shared_ptr<dvr::ProducerQueue> producerQueue =
- dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{});
- LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue.");
-
- std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue();
- LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue.");
-
- producer = BufferHubProducer::Create(producerQueue);
- consumer = BufferHubConsumer::Create(consumerQueue);
-
- LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer");
- LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer");
-
- *outProducer = producer;
- *outConsumer = consumer;
-}
-#endif
-
}; // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 797069c..918ff2d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -27,10 +27,6 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#ifndef NO_BUFFERHUB
-#include <gui/BufferHubProducer.h>
-#endif
-
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
#include <gui/BufferQueueDefs.h>
@@ -1012,17 +1008,7 @@
}
case USE_BUFFER_HUB: {
ALOGE("createFromParcel: BufferHub not implemented.");
-#ifndef NO_BUFFERHUB
- dvr::ProducerQueueParcelable producerParcelable;
- res = producerParcelable.readFromParcel(parcel);
- if (res != NO_ERROR) {
- ALOGE("createFromParcel: Failed to read from parcel, error=%d", res);
- return nullptr;
- }
- return BufferHubProducer::Create(std::move(producerParcelable));
-#else
return nullptr;
-#endif
}
default: {
ALOGE("createFromParcel: Unexpected mgaic: 0x%x.", outMagic);
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index e4b8bad..2b25b61 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,6 +17,9 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
+#include <cstdint>
+#include <optional>
+
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
@@ -30,7 +33,7 @@
ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
ON_RELEASE_BUFFER,
ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_RELEASE_BUFFER,
+ LAST = ON_TRANSACTION_QUEUE_STALLED,
};
} // Anonymous namespace
@@ -126,7 +129,12 @@
} else {
SAFE_PARCEL(output->writeBool, false);
}
- SAFE_PARCEL(output->writeUint32, transformHint);
+
+ SAFE_PARCEL(output->writeBool, transformHint.has_value());
+ if (transformHint.has_value()) {
+ output->writeUint32(transformHint.value());
+ }
+
SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount);
SAFE_PARCEL(output->writeParcelable, eventStats);
SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
@@ -156,7 +164,16 @@
previousReleaseFence = new Fence();
SAFE_PARCEL(input->read, *previousReleaseFence);
}
- SAFE_PARCEL(input->readUint32, &transformHint);
+ bool hasTransformHint = false;
+ SAFE_PARCEL(input->readBool, &hasTransformHint);
+ if (hasTransformHint) {
+ uint32_t tempTransformHint;
+ SAFE_PARCEL(input->readUint32, &tempTransformHint);
+ transformHint = std::make_optional(tempTransformHint);
+ } else {
+ transformHint = std::nullopt;
+ }
+
SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount);
SAFE_PARCEL(input->readParcelable, &eventStats);
@@ -273,15 +290,17 @@
void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) override {
- callRemoteAsync<decltype(
- &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
- callbackId, releaseFence,
- currentMaxAcquiredBufferCount);
+ callRemoteAsync<decltype(&ITransactionCompletedListener::
+ onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
+ releaseFence,
+ currentMaxAcquiredBufferCount);
}
- void onTransactionQueueStalled() override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionQueueStalled)>(
- Tag::ON_TRANSACTION_QUEUE_STALLED);
+ void onTransactionQueueStalled(const String8& reason) override {
+ callRemoteAsync<
+ decltype(&ITransactionCompletedListener::
+ onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
+ reason);
}
};
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 04d4a07..9c2ce0f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -386,10 +386,11 @@
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
surfaceStats.eventStats,
surfaceStats.currentMaxAcquiredBufferCount);
- if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
+ if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl] &&
+ surfaceStats.transformHint.has_value()) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
- ->setTransformHint(surfaceStats.transformHint);
+ ->setTransformHint(*surfaceStats.transformHint);
}
// If there is buffer id set, we look up any pending client release buffer callbacks
// and call them. This is a performance optimization when we have a transaction
@@ -455,23 +456,24 @@
}
}
-void TransactionCompletedListener::onTransactionQueueStalled() {
- std::unordered_map<void*, std::function<void()>> callbackCopy;
- {
- std::scoped_lock<std::mutex> lock(mMutex);
- callbackCopy = mQueueStallListeners;
- }
- for (auto const& it : callbackCopy) {
- it.second();
- }
+void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
+ std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ callbackCopy = mQueueStallListeners;
+ }
+ for (auto const& it : callbackCopy) {
+ it.second(reason.c_str());
+ }
}
-void TransactionCompletedListener::addQueueStallListener(std::function<void()> stallListener,
- void* id) {
+void TransactionCompletedListener::addQueueStallListener(
+ std::function<void(const std::string&)> stallListener, void* id) {
std::scoped_lock<std::mutex> lock(mMutex);
mQueueStallListeners[id] = stallListener;
}
-void TransactionCompletedListener::removeQueueStallListener(void *id) {
+
+void TransactionCompletedListener::removeQueueStallListener(void* id) {
std::scoped_lock<std::mutex> lock(mMutex);
mQueueStallListeners.erase(id);
}
@@ -2451,6 +2453,12 @@
return statusTFromBinderStatus(status);
}
+status_t SurfaceComposerClient::getOverlaySupport(gui::OverlayProperties* outProperties) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getOverlaySupport(outProperties);
+ return statusTFromBinderStatus(status);
+}
+
status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display,
ui::DisplayModeId displayModeId) {
binder::Status status = ComposerServiceAIDL::getComposerService()
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index ca0b97f..92d9e77 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -40,6 +40,7 @@
import android.gui.IWindowInfosListener;
import android.gui.LayerCaptureArgs;
import android.gui.LayerDebugInfo;
+import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
import android.gui.StaticDisplayInfo;
@@ -472,4 +473,6 @@
void addWindowInfosListener(IWindowInfosListener windowInfosListener);
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
+
+ OverlayProperties getOverlaySupport();
}
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
new file mode 100644
index 0000000..80d5ced
--- /dev/null
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.SupportedBufferCombinations;
+
+/** @hide */
+parcelable OverlayProperties {
+ SupportedBufferCombinations[] combinations;
+}
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
new file mode 100644
index 0000000..a8bc994
--- /dev/null
+++ b/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable SupportedBufferCombinations {
+ int[] pixelFormats;
+ int[] dataspaces;
+}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 315b925..2025170 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -152,6 +152,7 @@
(override));
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
+ MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 827a6cc..957652e 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -113,12 +113,10 @@
uint64_t getLastAcquiredFrameNum();
/**
- * Set a callback to be invoked when we are hung. The boolean parameter
- * indicates whether the hang is due to an unfired fence.
- * TODO: The boolean is always true atm, unfired fence is
- * the only case we detect.
+ * Set a callback to be invoked when we are hung. The string parameter
+ * indicates the reason for the hang.
*/
- void setTransactionHangCallback(std::function<void(bool)> callback);
+ void setTransactionHangCallback(std::function<void(const std::string&)> callback);
virtual ~BLASTBufferQueue();
@@ -282,7 +280,7 @@
bool mAppliedLastTransaction = false;
uint64_t mLastAppliedFrameNumber = 0;
- std::function<void(bool)> mTransactionHangCallback;
+ std::function<void(const std::string&)> mTransactionHangCallback;
std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
};
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
deleted file mode 100644
index d380770..0000000
--- a/libs/gui/include/gui/BufferHubConsumer.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_BUFFERHUBCONSUMER_H_
-#define ANDROID_GUI_BUFFERHUBCONSUMER_H_
-
-#include <gui/IGraphicBufferConsumer.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-
-namespace android {
-
-class BufferHubConsumer : public IGraphicBufferConsumer {
-public:
- // Creates a BufferHubConsumer instance by importing an existing producer queue.
- static sp<BufferHubConsumer> Create(const std::shared_ptr<dvr::ConsumerQueue>& queue);
-
- // Creates a BufferHubConsumer instance by importing an existing producer
- // parcelable. Note that this call takes the ownership of the parcelable
- // object and is guaranteed to succeed if parcelable object is valid.
- static sp<BufferHubConsumer> Create(dvr::ConsumerQueueParcelable parcelable);
-
- // See |IGraphicBufferConsumer::acquireBuffer|
- status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override;
-
- // See |IGraphicBufferConsumer::detachBuffer|
- status_t detachBuffer(int slot) override;
-
- // See |IGraphicBufferConsumer::attachBuffer|
- status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override;
-
- // See |IGraphicBufferConsumer::releaseBuffer|
- status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence,
- const sp<Fence>& releaseFence) override;
-
- // See |IGraphicBufferConsumer::consumerConnect|
- status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override;
-
- // See |IGraphicBufferConsumer::consumerDisconnect|
- status_t consumerDisconnect() override;
-
- // See |IGraphicBufferConsumer::getReleasedBuffers|
- status_t getReleasedBuffers(uint64_t* slotMask) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferSize|
- status_t setDefaultBufferSize(uint32_t w, uint32_t h) override;
-
- // See |IGraphicBufferConsumer::setMaxBufferCount|
- status_t setMaxBufferCount(int bufferCount) override;
-
- // See |IGraphicBufferConsumer::setMaxAcquiredBufferCount|
- status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
-
- // See |IGraphicBufferConsumer::setConsumerName|
- status_t setConsumerName(const String8& name) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferFormat|
- status_t setDefaultBufferFormat(PixelFormat defaultFormat) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferDataSpace|
- status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override;
-
- // See |IGraphicBufferConsumer::setConsumerUsageBits|
- status_t setConsumerUsageBits(uint64_t usage) override;
-
- // See |IGraphicBufferConsumer::setConsumerIsProtected|
- status_t setConsumerIsProtected(bool isProtected) override;
-
- // See |IGraphicBufferConsumer::setTransformHint|
- status_t setTransformHint(uint32_t hint) override;
-
- // See |IGraphicBufferConsumer::getSidebandStream|
- status_t getSidebandStream(sp<NativeHandle>* outStream) const override;
-
- // See |IGraphicBufferConsumer::getOccupancyHistory|
- status_t getOccupancyHistory(bool forceFlush,
- std::vector<OccupancyTracker::Segment>* outHistory) override;
-
- // See |IGraphicBufferConsumer::discardFreeBuffers|
- status_t discardFreeBuffers() override;
-
- // See |IGraphicBufferConsumer::dumpState|
- status_t dumpState(const String8& prefix, String8* outResult) const override;
-
- // BufferHubConsumer provides its own logic to cast to a binder object.
- IBinder* onAsBinder() override;
-
-private:
- // Private constructor to force use of |Create|.
- BufferHubConsumer() = default;
-
- // Concrete implementation backed by BufferHubBuffer.
- std::shared_ptr<dvr::ConsumerQueue> mQueue;
-};
-
-} // namespace android
-
-#endif // ANDROID_GUI_BUFFERHUBCONSUMER_H_
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h
deleted file mode 100644
index 0e925ce..0000000
--- a/libs/gui/include/gui/BufferHubProducer.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_BUFFERHUBPRODUCER_H_
-#define ANDROID_GUI_BUFFERHUBPRODUCER_H_
-
-#include <gui/BufferSlot.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-
-namespace android {
-
-class BufferHubProducer : public IGraphicBufferProducer {
-public:
- static constexpr int kNoConnectedApi = -1;
-
- // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
- // side logic doesn't limit the number of buffer it can acquire
- // simultaneously. We need a way for consumer logic to configure and enforce
- // that.
- static constexpr int kDefaultUndequeuedBuffers = 1;
-
- // Creates a BufferHubProducer instance by importing an existing prodcuer
- // queue.
- static sp<BufferHubProducer> Create(const std::shared_ptr<dvr::ProducerQueue>& producer);
-
- // Creates a BufferHubProducer instance by importing an existing prodcuer
- // parcelable. Note that this call takes the ownership of the parcelable
- // object and is guaranteed to succeed if parcelable object is valid.
- static sp<BufferHubProducer> Create(dvr::ProducerQueueParcelable parcelable);
-
- // See |IGraphicBufferProducer::requestBuffer|
- status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
-
- // For the BufferHub based implementation. All buffers in the queue are
- // allowed to be dequeued from the consumer side. It call always returns
- // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
- // |max_dequeued_buffers| here can be considered the same as setting queue
- // capacity.
- //
- // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
- status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
-
- // See |IGraphicBufferProducer::setAsyncMode|
- status_t setAsyncMode(bool async) override;
-
- // See |IGraphicBufferProducer::dequeueBuffer|
- status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps) override;
-
- // See |IGraphicBufferProducer::detachBuffer|
- status_t detachBuffer(int slot) override;
-
- // See |IGraphicBufferProducer::detachNextBuffer|
- status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) override;
-
- // See |IGraphicBufferProducer::attachBuffer|
- status_t attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) override;
-
- // See |IGraphicBufferProducer::queueBuffer|
- status_t queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) override;
-
- // See |IGraphicBufferProducer::cancelBuffer|
- status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
-
- // See |IGraphicBufferProducer::query|
- status_t query(int what, int* out_value) override;
-
- // See |IGraphicBufferProducer::connect|
- status_t connect(const sp<IProducerListener>& listener, int api,
- bool producer_controlled_by_app, QueueBufferOutput* output) override;
-
- // See |IGraphicBufferProducer::disconnect|
- status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override;
-
- // See |IGraphicBufferProducer::setSidebandStream|
- status_t setSidebandStream(const sp<NativeHandle>& stream) override;
-
- // See |IGraphicBufferProducer::allocateBuffers|
- void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
- uint64_t usage) override;
-
- // See |IGraphicBufferProducer::allowAllocation|
- status_t allowAllocation(bool allow) override;
-
- // See |IGraphicBufferProducer::setGenerationNumber|
- status_t setGenerationNumber(uint32_t generation_number) override;
-
- // See |IGraphicBufferProducer::getConsumerName|
- String8 getConsumerName() const override;
-
- // See |IGraphicBufferProducer::setSharedBufferMode|
- status_t setSharedBufferMode(bool shared_buffer_mode) override;
-
- // See |IGraphicBufferProducer::setAutoRefresh|
- status_t setAutoRefresh(bool auto_refresh) override;
-
- // See |IGraphicBufferProducer::setDequeueTimeout|
- status_t setDequeueTimeout(nsecs_t timeout) override;
-
- // See |IGraphicBufferProducer::getLastQueuedBuffer|
- status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence,
- float out_transform_matrix[16]) override;
-
- // See |IGraphicBufferProducer::getFrameTimestamps|
- void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
-
- // See |IGraphicBufferProducer::getUniqueId|
- status_t getUniqueId(uint64_t* out_id) const override;
-
- // See |IGraphicBufferProducer::getConsumerUsage|
- status_t getConsumerUsage(uint64_t* out_usage) const override;
-
- // Takes out the current producer as a binder parcelable object. Note that the
- // producer must be disconnected to be exportable. After successful export,
- // the producer queue can no longer be connected again. Returns NO_ERROR when
- // takeout is successful and out_parcelable will hold the new parcelable
- // object. Also note that out_parcelable cannot be NULL and must points to an
- // invalid parcelable.
- status_t TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable);
-
- IBinder* onAsBinder() override;
-
-protected:
- // See |IGraphicBufferProducer::exportToParcel|
- status_t exportToParcel(Parcel* parcel) override;
-
-private:
- using LocalHandle = pdx::LocalHandle;
-
- // Private constructor to force use of |Create|.
- BufferHubProducer() {}
-
- static uint64_t genUniqueId() {
- static std::atomic<uint32_t> counter{0};
- static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
- return id | counter++;
- }
-
- // Allocate new buffer through BufferHub and add it into |queue_| for
- // bookkeeping.
- status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
- PixelFormat format, uint64_t usage);
-
- // Remove a buffer via BufferHubRPC.
- status_t RemoveBuffer(size_t slot);
-
- // Free all buffers which are owned by the prodcuer. Note that if graphic
- // buffers are acquired by the consumer, we can't .
- status_t FreeAllBuffers();
-
- // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been
- // locked already.
- status_t DetachBufferLocked(size_t slot);
-
- // Concreate implementation backed by BufferHubBuffer.
- std::shared_ptr<dvr::ProducerQueue> queue_;
-
- // Mutex for thread safety.
- std::mutex mutex_;
-
- // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
- int connected_api_{kNoConnectedApi};
-
- // |max_buffer_count_| sets the capacity of the underlying buffer queue.
- int32_t max_buffer_count_{dvr::BufferHubQueue::kMaxQueueCapacity};
-
- // |max_dequeued_buffer_count_| set the maximum number of buffers that can
- // be dequeued at the same momment.
- int32_t max_dequeued_buffer_count_{1};
-
- // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
- // slot is not yet available. The timeout is stored in milliseconds.
- int dequeue_timeout_ms_{dvr::BufferHubQueue::kNoTimeOut};
-
- // |generation_number_| stores the current generation number of the attached
- // producer. Any attempt to attach a buffer with a different generation
- // number will fail.
- // TOOD(b/38137191) Currently not used as we don't support
- // IGraphicBufferProducer::detachBuffer.
- uint32_t generation_number_{0};
-
- // |buffers_| stores the buffers that have been dequeued from
- // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
- // filled in with the result of |Dequeue|.
- // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
- // requested buffer usage or geometry differs from that of the buffer
- // allocated to a slot.
- struct BufferHubSlot : public BufferSlot {
- BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {}
- // BufferSlot comes from android framework, using m prefix to comply with
- // the name convention with the reset of data fields from BufferSlot.
- std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer;
- bool mIsReallocating;
- };
- BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
-
- // A uniqueId used by IGraphicBufferProducer interface.
- const uint64_t unique_id_{genUniqueId()};
-
- // A pending parcelable object which keeps the bufferhub channel alive.
- dvr::ProducerQueueParcelable pending_producer_parcelable_;
-};
-
-} // namespace android
-
-#endif // ANDROID_GUI_BUFFERHUBPRODUCER_H_
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 91f80d2..690587f 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -82,12 +82,6 @@
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger = false);
-#ifndef NO_BUFFERHUB
- // Creates an IGraphicBufferProducer and IGraphicBufferConsumer pair backed by BufferHub.
- static void createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer);
-#endif
-
BufferQueue() = delete; // Create through createBufferQueue
};
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index cc136bb..453e8f3 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -132,7 +132,7 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
- const sp<Fence>& prevReleaseFence, uint32_t hint,
+ const sp<Fence>& prevReleaseFence, std::optional<uint32_t> hint,
uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats,
std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId)
: surfaceControl(sc),
@@ -147,7 +147,7 @@
sp<IBinder> surfaceControl;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
sp<Fence> previousReleaseFence;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = 0;
uint32_t currentMaxAcquiredBufferCount = 0;
FrameEventHistoryStats eventStats;
std::vector<JankData> jankData;
@@ -194,7 +194,8 @@
virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) = 0;
- virtual void onTransactionQueueStalled() = 0;
+
+ virtual void onTransactionQueueStalled(const String8& name) = 0;
};
class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 5af5989..e16f89c 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -30,7 +30,8 @@
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
METADATA_DEQUEUE_TIME = 7,
- METADATA_GAME_MODE = 8
+ METADATA_GAME_MODE = 8,
+ METADATA_CALLING_UID = 9,
};
struct LayerMetadata : public Parcelable {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d138d68..c450e85 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -69,7 +69,7 @@
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats eventStats,
+ std::optional<uint32_t> hint, FrameEventHistoryStats eventStats,
uint32_t currentMaxAcquiredBufferCount)
: surfaceControl(sc),
latchTime(latchTime),
@@ -85,7 +85,7 @@
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
sp<Fence> presentFence;
sp<Fence> previousReleaseFence;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = 0;
FrameEventHistoryStats frameEventStats;
uint32_t currentMaxAcquiredBufferCount = 0;
};
@@ -182,6 +182,10 @@
// Gets if boot display mode operations are supported on a device
static status_t getBootDisplayModeSupport(bool* support);
+
+ // Gets the overlay properties of the device
+ static status_t getOverlaySupport(gui::OverlayProperties* outProperties);
+
// Sets the user-preferred display mode that a device should boot in
static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId);
// Clears the user-preferred display mode
@@ -782,7 +786,7 @@
// This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
// std::recursive_mutex
std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
- std::unordered_map<void*, std::function<void()>> mQueueStallListeners;
+ std::unordered_map<void*, std::function<void(const std::string&)>> mQueueStallListeners;
public:
static sp<TransactionCompletedListener> getInstance();
@@ -800,7 +804,7 @@
const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
- void addQueueStallListener(std::function<void()> stallListener, void* id);
+ void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
void removeQueueStallListener(void *id);
/*
@@ -831,7 +835,7 @@
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
- void onTransactionQueueStalled() override;
+ void onTransactionQueueStalled(const String8& reason) override;
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 7b37b25..183acc1 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -93,39 +93,6 @@
header_libs: ["libsurfaceflinger_headers"],
}
-// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
-// This test has a main method, and requires a separate binary to be built.
-// To add move tests like this, just add additional cc_test statements,
-// as opposed to adding more source files to this one.
-cc_test {
- name: "SurfaceParcelable_test",
- test_suites: ["device-tests"],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- srcs: [
- "SurfaceParcelable_test.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libbinder",
- "libcutils",
- "libgui",
- "libui",
- "libutils",
- "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
- "libpdx_default_transport",
- ],
-
- header_libs: [
- "libdvr_headers",
- ],
-}
-
cc_test {
name: "SamplingDemo",
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 2af2fe1..3427731 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -42,10 +42,6 @@
#define TEST_CONTROLLED_BY_APP false
#define TEST_PRODUCER_USAGE_BITS (0)
-#ifndef USE_BUFFER_HUB_AS_BUFFER_QUEUE
-#define USE_BUFFER_HUB_AS_BUFFER_QUEUE 0
-#endif
-
namespace android {
namespace {
@@ -77,7 +73,6 @@
// Enums to control which IGraphicBufferProducer backend to test.
enum IGraphicBufferProducerTestCode {
USE_BUFFER_QUEUE_PRODUCER = 0,
- USE_BUFFER_HUB_PRODUCER,
};
}; // namespace anonymous
@@ -99,10 +94,6 @@
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
break;
}
- case USE_BUFFER_HUB_PRODUCER: {
- BufferQueue::createBufferHubQueue(&mProducer, &mConsumer);
- break;
- }
default: {
// Should never reach here.
LOG_ALWAYS_FATAL("Invalid test params: %u", GetParam());
@@ -880,11 +871,6 @@
}
TEST_P(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) {
- if (GetParam() == USE_BUFFER_HUB_PRODUCER) {
- // TODO(b/36724099): Add support for BufferHubProducer::setAsyncMode(true)
- return;
- }
-
ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true;
@@ -1117,14 +1103,7 @@
}
}
-#if USE_BUFFER_HUB_AS_BUFFER_QUEUE
-INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest,
- ::testing::Values(USE_BUFFER_QUEUE_PRODUCER, USE_BUFFER_HUB_PRODUCER));
-#else
-// TODO(b/70046255): Remove the #ifdef here and always tests both backends once BufferHubQueue can
-// pass all existing libgui tests.
INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest,
::testing::Values(USE_BUFFER_QUEUE_PRODUCER));
-#endif
} // namespace android
diff --git a/libs/gui/tests/SurfaceParcelable_test.cpp b/libs/gui/tests/SurfaceParcelable_test.cpp
deleted file mode 100644
index 686dc82..0000000
--- a/libs/gui/tests/SurfaceParcelable_test.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2018 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 "SurfaceParcelable_test"
-
-#include <gtest/gtest.h>
-
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <gui/BufferHubProducer.h>
-#include <gui/BufferQueue.h>
-#include <gui/view/Surface.h>
-#include <utils/Log.h>
-
-namespace android {
-
-static const String16 kTestServiceName = String16("SurfaceParcelableTestService");
-static const String16 kSurfaceName = String16("TEST_SURFACE");
-static const uint32_t kBufferWidth = 100;
-static const uint32_t kBufferHeight = 1;
-static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-
-enum SurfaceParcelableTestServiceCode {
- CREATE_BUFFER_QUEUE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
- CREATE_BUFFER_HUB_SURFACE,
-};
-
-class SurfaceParcelableTestService : public BBinder {
-public:
- SurfaceParcelableTestService() {
- // BufferQueue
- BufferQueue::createBufferQueue(&mBufferQueueProducer, &mBufferQueueConsumer);
-
- // BufferHub
- dvr::ProducerQueueConfigBuilder configBuilder;
- mProducerQueue = dvr::ProducerQueue::Create(configBuilder.SetDefaultWidth(kBufferWidth)
- .SetDefaultHeight(kBufferHeight)
- .SetDefaultFormat(kBufferFormat)
- .Build(),
- dvr::UsagePolicy{});
- mBufferHubProducer = BufferHubProducer::Create(mProducerQueue);
- }
-
- ~SurfaceParcelableTestService() = default;
-
- virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply,
- uint32_t /*flags*/ = 0) {
- switch (code) {
- case CREATE_BUFFER_QUEUE_SURFACE: {
- view::Surface surfaceShim;
- surfaceShim.name = kSurfaceName;
- surfaceShim.graphicBufferProducer = mBufferQueueProducer;
- return surfaceShim.writeToParcel(reply);
- }
- case CREATE_BUFFER_HUB_SURFACE: {
- view::Surface surfaceShim;
- surfaceShim.name = kSurfaceName;
- surfaceShim.graphicBufferProducer = mBufferHubProducer;
- return surfaceShim.writeToParcel(reply);
- }
- default:
- return UNKNOWN_TRANSACTION;
- };
- }
-
-protected:
- sp<IGraphicBufferProducer> mBufferQueueProducer;
- sp<IGraphicBufferConsumer> mBufferQueueConsumer;
-
- std::shared_ptr<dvr::ProducerQueue> mProducerQueue;
- sp<IGraphicBufferProducer> mBufferHubProducer;
-};
-
-static int runBinderServer() {
- ProcessState::self()->startThreadPool();
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<SurfaceParcelableTestService> service = new SurfaceParcelableTestService;
- sm->addService(kTestServiceName, service, false);
-
- ALOGI("Binder server running...");
-
- while (true) {
- int stat, retval;
- retval = wait(&stat);
- if (retval == -1 && errno == ECHILD) {
- break;
- }
- }
-
- ALOGI("Binder server exiting...");
- return 0;
-}
-
-class SurfaceParcelableTest : public ::testing::TestWithParam<uint32_t> {
-protected:
- virtual void SetUp() {
- mService = defaultServiceManager()->getService(kTestServiceName);
- if (mService == nullptr) {
- ALOGE("Failed to connect to the test service.");
- return;
- }
-
- ALOGI("Binder service is ready for client.");
- }
-
- status_t GetSurface(view::Surface* surfaceShim) {
- ALOGI("...Test: %d", GetParam());
-
- uint32_t opCode = GetParam();
- Parcel data;
- Parcel reply;
- status_t error = mService->transact(opCode, data, &reply);
- if (error != NO_ERROR) {
- ALOGE("Failed to get surface over binder, error=%d.", error);
- return error;
- }
-
- error = surfaceShim->readFromParcel(&reply);
- if (error != NO_ERROR) {
- ALOGE("Failed to get surface from parcel, error=%d.", error);
- return error;
- }
-
- return NO_ERROR;
- }
-
-private:
- sp<IBinder> mService;
-};
-
-TEST_P(SurfaceParcelableTest, SendOverBinder) {
- view::Surface surfaceShim;
- EXPECT_EQ(GetSurface(&surfaceShim), NO_ERROR);
- EXPECT_EQ(surfaceShim.name, kSurfaceName);
- EXPECT_FALSE(surfaceShim.graphicBufferProducer == nullptr);
-}
-
-INSTANTIATE_TEST_CASE_P(SurfaceBackends, SurfaceParcelableTest,
- ::testing::Values(CREATE_BUFFER_QUEUE_SURFACE, CREATE_BUFFER_HUB_SURFACE));
-
-} // namespace android
-
-int main(int argc, char** argv) {
- pid_t pid = fork();
- if (pid == 0) {
- android::ProcessState::self()->startThreadPool();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-
- } else {
- ALOGI("Test process pid: %d.", pid);
- return android::runBinderServer();
- }
-}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 72d76ee..346b686 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -991,6 +991,10 @@
return binder::Status::ok();
}
+ binder::Status getOverlaySupport(gui::OverlayProperties* /*properties*/) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 579b28e..c1eb8e2 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -22,6 +22,7 @@
#include <inttypes.h>
#include <string.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
@@ -34,7 +35,7 @@
#ifdef __linux__
#include <binder/Parcel.h>
#endif
-#ifdef __ANDROID__
+#if defined(__ANDROID__)
#include <sys/random.h>
#endif
@@ -112,15 +113,31 @@
}
// --- IdGenerator ---
+#if defined(__ANDROID__)
+[[maybe_unused]]
+#endif
+static status_t
+getRandomBytes(uint8_t* data, size_t size) {
+ int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (ret == -1) {
+ return -errno;
+ }
+
+ base::unique_fd fd(ret);
+ if (!base::ReadFully(fd, data, size)) {
+ return -errno;
+ }
+ return OK;
+}
+
IdGenerator::IdGenerator(Source source) : mSource(source) {}
int32_t IdGenerator::nextId() const {
constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK;
int32_t id = 0;
-// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't
-// use sequence number so just always return mSource.
-#ifdef __ANDROID__
+#if defined(__ANDROID__)
+ // On device, prefer 'getrandom' to '/dev/urandom' because it's faster.
constexpr size_t BUF_LEN = sizeof(id);
size_t totalBytes = 0;
while (totalBytes < BUF_LEN) {
@@ -132,8 +149,17 @@
}
totalBytes += bytes;
}
+#else
+#if defined(__linux__)
+ // On host, <sys/random.h> / GRND_NONBLOCK is not available
+ while (true) {
+ status_t result = getRandomBytes(reinterpret_cast<uint8_t*>(&id), sizeof(id));
+ if (result == OK) {
+ break;
+ }
+ }
+#endif // __linux__
#endif // __ANDROID__
-
return (id & SEQUENCE_NUMBER_MASK) | static_cast<int32_t>(mSource);
}
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 163a2fe..b78fae3 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -391,7 +391,9 @@
DEFINE_AXIS(GENERIC_13), \
DEFINE_AXIS(GENERIC_14), \
DEFINE_AXIS(GENERIC_15), \
- DEFINE_AXIS(GENERIC_16)
+ DEFINE_AXIS(GENERIC_16), \
+ DEFINE_AXIS(GESTURE_X_OFFSET), \
+ DEFINE_AXIS(GESTURE_Y_OFFSET)
// NOTE: If you add new LEDs here, you must also add them to Input.h
#define LEDS_SEQUENCE \
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 4a4f734..19b4684 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -150,6 +150,10 @@
VelocityTracker::~VelocityTracker() {
}
+bool VelocityTracker::isAxisSupported(int32_t axis) {
+ return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end();
+}
+
void VelocityTracker::configureStrategy(int32_t axis) {
const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 5ce10a4..dab843b 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -35,6 +35,14 @@
const int INVALID_INPUT_EVENT_ID = 0;
/**
+ * Every input device has an id. This constant value is used when a valid input device id is not
+ * available.
+ * The virtual keyboard uses -1 as the input device id. Therefore, we use -2 as the value for
+ * an invalid input device.
+ */
+ const int INVALID_INPUT_DEVICE_ID = -2;
+
+ /**
* The input event was injected from accessibility. Used in policyFlags for input event
* injection.
*/
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bd12663..54feea2 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -299,6 +299,26 @@
}
/*
+ *================== VelocityTracker tests that do not require test motion data ====================
+ */
+TEST(SimpleVelocityTrackerTest, TestSupportedAxis) {
+ // Note that we are testing up to the max possible axis value, plus 3 more values. We are going
+ // beyond the max value to add a bit more protection. "3" is chosen arbitrarily to cover a few
+ // more values beyond the max.
+ for (int32_t axis = 0; axis <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE + 3; axis++) {
+ switch (axis) {
+ case AMOTION_EVENT_AXIS_X:
+ case AMOTION_EVENT_AXIS_Y:
+ case AMOTION_EVENT_AXIS_SCROLL:
+ EXPECT_TRUE(VelocityTracker::isAxisSupported(axis)) << axis << " is supported";
+ break;
+ default:
+ EXPECT_FALSE(VelocityTracker::isAxisSupported(axis)) << axis << " is NOT supported";
+ }
+ }
+}
+
+/*
* ================== VelocityTracker tests generated manually =====================================
*/
TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) {
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 285f8d5..3ab2ba8 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -31,4 +31,36 @@
srcs: [
"recoverymap.cpp",
],
+
+ shared_libs: [
+ "libutils",
+ ],
+}
+
+cc_library_static {
+ name: "libjpegencoder",
+
+ shared_libs: [
+ "libjpeg",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "jpegencoder.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libjpegdecoder",
+
+ shared_libs: [
+ "libjpeg",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "jpegdecoder.cpp",
+ ],
}
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
index 6ace354..133af5b 100644
--- a/libs/jpegrecoverymap/OWNERS
+++ b/libs/jpegrecoverymap/OWNERS
@@ -1,3 +1,4 @@
arifdikici@google.com
+deakin@google.com
dichenzhang@google.com
kyslov@google.com
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
new file mode 100644
index 0000000..2ab7550
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
+#include <cstdio>
+extern "C" {
+#include <jerror.h>
+#include <jpeglib.h>
+}
+#include <utils/Errors.h>
+#include <vector>
+namespace android::recoverymap {
+/*
+ * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
+ * This class is not thread-safe.
+ */
+class JpegDecoder {
+public:
+ JpegDecoder();
+ ~JpegDecoder();
+ /*
+ * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling
+ * this method, call getDecompressedImage() to get the image.
+ * Returns false if decompressing the image fails.
+ */
+ bool decompressImage(const void* image, int length);
+ /*
+ * Returns the decompressed raw image buffer pointer. This method must be called only after
+ * calling decompressImage().
+ */
+ const void* getDecompressedImagePtr();
+ /*
+ * Returns the decompressed raw image buffer size. This method must be called only after
+ * calling decompressImage().
+ */
+ size_t getDecompressedImageSize();
+private:
+ bool decode(const void* image, int length);
+ // Returns false if errors occur.
+ bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
+ bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+ // The buffer that holds the compressed result.
+ std::vector<JOCTET> mResultBuffer;
+};
+} /* namespace android */
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h
new file mode 100644
index 0000000..9641fda
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
+#include <cstdio>
+
+extern "C" {
+#include <jerror.h>
+#include <jpeglib.h>
+}
+
+#include <utils/Errors.h>
+#include <vector>
+
+namespace android::recoverymap {
+
+/*
+ * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
+ * This class is not thread-safe.
+ */
+class JpegEncoder {
+public:
+ JpegEncoder();
+ ~JpegEncoder();
+
+ /*
+ * Compresses YUV420Planer image to JPEG format. After calling this method, call
+ * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use.
+ * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of
+ * ICC segment which will be added to the compressed image.
+ * Returns false if errors occur during compression.
+ */
+ bool compressImage(const void* image, int width, int height, int quality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false);
+
+ /*
+ * Returns the compressed JPEG buffer pointer. This method must be called only after calling
+ * compressImage().
+ */
+ const void* getCompressedImagePtr();
+
+ /*
+ * Returns the compressed JPEG buffer size. This method must be called only after calling
+ * compressImage().
+ */
+ size_t getCompressedImageSize();
+
+private:
+ // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
+ // passed into jpeg library.
+ static void initDestination(j_compress_ptr cinfo);
+ static boolean emptyOutputBuffer(j_compress_ptr cinfo);
+ static void terminateDestination(j_compress_ptr cinfo);
+ static void outputErrorMessage(j_common_ptr cinfo);
+
+ // Returns false if errors occur.
+ bool encode(const void* inYuv, int width, int height, int jpegQuality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel);
+ void setJpegDestination(jpeg_compress_struct* cinfo);
+ void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
+ bool isSingleChannel);
+ // Returns false if errors occur.
+ bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel);
+ bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv);
+ bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image);
+
+ // The block size for encoded jpeg image buffer.
+ static const int kBlockSize = 16384;
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+
+ // The buffer that holds the compressed result.
+ std::vector<JOCTET> mResultBuffer;
+};
+
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index c5f8e9a..15eca1e 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -14,63 +14,160 @@
* limitations under the License.
*/
+ #include <utils/Errors.h>
+
namespace android::recoverymap {
+/*
+ * Holds information for uncompressed image or recovery map.
+ */
+struct jpegr_uncompressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Width of the recovery map or image in pixels.
+ int width;
+ // Height of the recovery map or image in pixels.
+ int height;
+};
+
+/*
+ * Holds information for compressed image or recovery map.
+ */
+struct jpegr_compressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Data length;
+ int length;
+};
+
+typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
+typedef struct jpegr_compressed_struct* jr_compressed_ptr;
+
class RecoveryMap {
public:
/*
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
+ * the recovery map to the end of the compressed JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param dest destination of the compressed JPEGR image
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ void* dest);
+
+ /*
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * Generate recovery map from the HDR and SDR inputs, append the recovery map to the end of the
+ * compressed JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param dest destination of the compressed JPEGR image
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ void* compressed_jpeg_image,
+ void* dest);
+
+ /*
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input
+ * and the decoded SDR result, append the recovery map to the end of the compressed JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param dest destination of the compressed JPEGR image
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ void* compressed_jpeg_image,
+ void* dest);
+
+ /*
+ * Decompress JPEGR image.
+ *
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param dest destination of the uncompressed JPEGR image
+ * @return NO_ERROR if decoding succeeds, error code if error occurs.
+ */
+ status_t decodeJPEGR(void* compressed_jpegr_image, jr_uncompressed_ptr dest);
+private:
+ /*
* This method is called in the decoding pipeline. It will decode the recovery map.
*
- * input: compressed recovery map
- * output: uncompressed recovery map
+ * @param compressed_recovery_map compressed recovery map
+ * @param dest decoded recover map
+ * @return NO_ERROR if decoding succeeds, error code if error occurs.
*/
- void* decodeRecoveryMap(void* compressed_recovery_map);
+ status_t decodeRecoveryMap(jr_compressed_ptr compressed_recovery_map,
+ jr_uncompressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will encode the recovery map.
*
- * input: uncompressed recovery map
- * output: compressed recovery map
+ * @param uncompressed_recovery_map uncompressed recovery map
+ * @param dest encoded recover map
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- void* encodeRecoveryMap(void* uncompressed_recovery_map);
+ status_t encodeRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_compressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
* 10-bit yuv images as input, and calculate the uncompressed recovery map.
*
- * input: uncompressed yuv_420 image, uncompressed p010 image
- * output: uncompressed recovery map
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param dest recover map
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- void* generateRecoveryMap(void* uncompressed_yuv_420_image, void* uncompressed_p010_image);
+ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest);
/*
* This method is called in the decoding pipeline. It will take the uncompressed (decoded)
- * 8-bit yuv image and the uncompressed(decoded) recovery map as input, and calculate the
+ * 8-bit yuv image and the uncompressed (decoded) recovery map as input, and calculate the
* 10-bit recovered image (in p010 color format).
*
- * input: uncompressed yuv_420 image, uncompressed recovery map
- * output: uncompress p010 image
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param uncompressed_recovery_map uncompressed recovery map
+ * @param dest reconstructed HDR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- void* applyRecoveryMap(void* uncompressed_yuv_420_image, void* uncompressed_recovery_map);
+ status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_uncompressed_ptr dest);
/*
* This method is called in the decoding pipeline. It will read XMP metadata to find the start
* position of the compressed recovery map, and will extract the compressed recovery map.
*
- * input: compressed JPEG-G image (8-bit JPEG + compressed recovery map)
- * output: compressed recovery map
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param dest destination of compressed recovery map
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- void* extractRecoveryMap(void* compressed_jpeg_g_image);
+ status_t extractRecoveryMap(void* compressed_jpegr_image, jr_compressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
* and the compressed recovery map as input, and update the XMP metadata with the end of JPEG
* marker, and append the compressed gian map after the JPEG.
*
- * input: compressed 8-bit JPEG image (standard JPEG), compressed recovery map
- * output: compressed JPEG-G image (8-bit JPEG + compressed recovery map)
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param compress_recovery_map compressed recover map
+ * @param dest compressed JPEGR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- void* appendRecoveryMap(void* compressed_jpeg_image, void* compressed_recovery_map);
+ status_t appendRecoveryMap(void* compressed_jpeg_image,
+ jr_compressed_ptr compressed_recovery_map,
+ void* dest);
};
-} // namespace android::recoverymap
\ No newline at end of file
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
new file mode 100644
index 0000000..22a5389
--- /dev/null
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/jpegdecoder.h>
+
+#include <cutils/log.h>
+
+#include <errno.h>
+#include <setjmp.h>
+
+namespace android::recoverymap {
+struct jpegr_source_mgr : jpeg_source_mgr {
+ jpegr_source_mgr(const uint8_t* ptr, int len);
+ ~jpegr_source_mgr();
+
+ const uint8_t* mBufferPtr;
+ size_t mBufferLength;
+};
+
+struct jpegrerror_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+static void jpegr_init_source(j_decompress_ptr cinfo) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+ src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
+ src->bytes_in_buffer = src->mBufferLength;
+}
+
+static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
+ ALOGE("%s : should not get here", __func__);
+ return FALSE;
+}
+
+static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+
+ if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
+ ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
+ } else {
+ src->next_input_byte += num_bytes;
+ src->bytes_in_buffer -= num_bytes;
+ }
+}
+
+static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
+
+jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) :
+ mBufferPtr(ptr), mBufferLength(len) {
+ init_source = jpegr_init_source;
+ fill_input_buffer = jpegr_fill_input_buffer;
+ skip_input_data = jpegr_skip_input_data;
+ resync_to_restart = jpeg_resync_to_restart;
+ term_source = jpegr_term_source;
+}
+
+jpegr_source_mgr::~jpegr_source_mgr() {}
+
+static void jpegrerror_exit(j_common_ptr cinfo) {
+ jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
+ longjmp(err->setjmp_buffer, 1);
+}
+
+JpegDecoder::JpegDecoder() {
+}
+
+JpegDecoder::~JpegDecoder() {
+}
+
+bool JpegDecoder::decompressImage(const void* image, int length) {
+ if (image == nullptr || length <= 0) {
+ ALOGE("Image size can not be handled: %d", length);
+ return false;
+ }
+
+ mResultBuffer.clear();
+ if (!decode(image, length)) {
+ return false;
+ }
+
+ return true;
+}
+
+const void* JpegDecoder::getDecompressedImagePtr() {
+ return mResultBuffer.data();
+}
+
+size_t JpegDecoder::getDecompressedImageSize() {
+ return mResultBuffer.size();
+}
+
+bool JpegDecoder::decode(const void* image, int length) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ cinfo.src = &mgr;
+ jpeg_read_header(&cinfo, TRUE);
+
+ if (cinfo.jpeg_color_space == JCS_YCbCr) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ }
+
+ cinfo.raw_data_out = TRUE;
+ cinfo.dct_method = JDCT_IFAST;
+ cinfo.out_color_space = cinfo.jpeg_color_space;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
+ cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
+ return false;
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return true;
+}
+
+bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
+ bool isSingleChannel) {
+ if (isSingleChannel) {
+ return decompressSingleChannel(cinfo, dest);
+ }
+ return decompressYUV(cinfo, dest);
+}
+
+bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3] {y, cb, cr};
+
+ size_t y_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t uv_plane_size = y_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->output_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool JpegDecoder::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1] {y};
+
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/jpegencoder.cpp b/libs/jpegrecoverymap/jpegencoder.cpp
new file mode 100644
index 0000000..d45d9b3
--- /dev/null
+++ b/libs/jpegrecoverymap/jpegencoder.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/jpegencoder.h>
+
+#include <cutils/log.h>
+
+#include <errno.h>
+
+namespace android::recoverymap {
+
+// The destination manager that can access |mResultBuffer| in JpegEncoder.
+struct destination_mgr {
+public:
+ struct jpeg_destination_mgr mgr;
+ JpegEncoder* encoder;
+};
+
+JpegEncoder::JpegEncoder() {
+}
+
+JpegEncoder::~JpegEncoder() {
+}
+
+bool JpegEncoder::compressImage(const void* image, int width, int height, int quality,
+ const void* iccBuffer, unsigned int iccSize,
+ bool isSingleChannel) {
+ if (width % 8 != 0 || height % 2 != 0) {
+ ALOGE("Image size can not be handled: %dx%d", width, height);
+ return false;
+ }
+
+ mResultBuffer.clear();
+ if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) {
+ return false;
+ }
+ ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes",
+ (width * height * 12) / 8, width, height, mResultBuffer.size());
+ return true;
+}
+
+const void* JpegEncoder::getCompressedImagePtr() {
+ return mResultBuffer.data();
+}
+
+size_t JpegEncoder::getCompressedImageSize() {
+ return mResultBuffer.size();
+}
+
+void JpegEncoder::initDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(kBlockSize);
+ dest->mgr.next_output_byte = &buffer[0];
+ dest->mgr.free_in_buffer = buffer.size();
+}
+
+boolean JpegEncoder::emptyOutputBuffer(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ size_t oldsize = buffer.size();
+ buffer.resize(oldsize + kBlockSize);
+ dest->mgr.next_output_byte = &buffer[oldsize];
+ dest->mgr.free_in_buffer = kBlockSize;
+ return true;
+}
+
+void JpegEncoder::terminateDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
+}
+
+void JpegEncoder::outputErrorMessage(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message) (cinfo, buffer);
+ ALOGE("%s\n", buffer);
+}
+
+bool JpegEncoder::encode(const void* image, int width, int height, int jpegQuality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) {
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ // Override output_message() to print error log with ALOGE().
+ cinfo.err->output_message = &outputErrorMessage;
+ jpeg_create_compress(&cinfo);
+ setJpegDestination(&cinfo);
+
+ setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ if (iccBuffer != nullptr && iccSize > 0) {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
+ }
+
+ if (!compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel)) {
+ return false;
+ }
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ return true;
+}
+
+void JpegEncoder::setJpegDestination(jpeg_compress_struct* cinfo) {
+ destination_mgr* dest = static_cast<struct destination_mgr *>((*cinfo->mem->alloc_small) (
+ (j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(destination_mgr)));
+ dest->encoder = this;
+ dest->mgr.init_destination = &initDestination;
+ dest->mgr.empty_output_buffer = &emptyOutputBuffer;
+ dest->mgr.term_destination = &terminateDestination;
+ cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
+}
+
+void JpegEncoder::setJpegCompressStruct(int width, int height, int quality,
+ jpeg_compress_struct* cinfo, bool isSingleChannel) {
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ if (isSingleChannel) {
+ cinfo->input_components = 1;
+ cinfo->in_color_space = JCS_GRAYSCALE;
+ } else {
+ cinfo->input_components = 3;
+ cinfo->in_color_space = JCS_YCbCr;
+ }
+ jpeg_set_defaults(cinfo);
+
+ jpeg_set_quality(cinfo, quality, TRUE);
+ jpeg_set_colorspace(cinfo, isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr);
+ cinfo->raw_data_in = TRUE;
+ cinfo->dct_method = JDCT_IFAST;
+
+ if (!isSingleChannel) {
+ // Configure sampling factors. The sampling factor is JPEG subsampling 420 because the
+ // source format is YUV420.
+ cinfo->comp_info[0].h_samp_factor = 2;
+ cinfo->comp_info[0].v_samp_factor = 2;
+ cinfo->comp_info[1].h_samp_factor = 1;
+ cinfo->comp_info[1].v_samp_factor = 1;
+ cinfo->comp_info[2].h_samp_factor = 1;
+ cinfo->comp_info[2].v_samp_factor = 1;
+ }
+}
+
+bool JpegEncoder::compress(
+ jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel) {
+ if (isSingleChannel) {
+ return compressSingleChannel(cinfo, image);
+ }
+ return compressYuv(cinfo, image);
+}
+
+bool JpegEncoder::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3] {y, cb, cr};
+
+ size_t y_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t uv_plane_size = y_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(yuv);
+ uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->next_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool JpegEncoder::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1] {y};
+
+ uint8_t* y_plane = const_cast<uint8_t*>(image);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 3e95a31..5d25722 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -18,60 +18,123 @@
namespace android::recoverymap {
-void* RecoveryMap::decodeRecoveryMap(void* compressed_recovery_map) {
- if (compressed_recovery_map == nullptr) {
- return nullptr;
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ void* dest) {
+ if (uncompressed_p010_image == nullptr
+ || uncompressed_yuv_420_image == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
}
-void* RecoveryMap::encodeRecoveryMap(void* uncompressed_recovery_map) {
- if (uncompressed_recovery_map == nullptr) {
- return nullptr;
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ void* compressed_jpeg_image,
+ void* dest) {
+
+ if (uncompressed_p010_image == nullptr
+ || uncompressed_yuv_420_image == nullptr
+ || compressed_jpeg_image == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
}
-void* RecoveryMap::generateRecoveryMap(
- void* uncompressed_yuv_420_image, void* uncompressed_p010_image) {
- if (uncompressed_yuv_420_image == nullptr || uncompressed_p010_image == nullptr) {
- return nullptr;
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ void* compressed_jpeg_image,
+ void* dest) {
+ if (uncompressed_p010_image == nullptr
+ || compressed_jpeg_image == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
}
-void* RecoveryMap::applyRecoveryMap(
- void* uncompressed_yuv_420_image, void* uncompressed_recovery_map) {
- if (uncompressed_yuv_420_image == nullptr || uncompressed_recovery_map == nullptr) {
- return nullptr;
+status_t RecoveryMap::decodeJPEGR(void* compressed_jpegr_image, jr_uncompressed_ptr dest) {
+ if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
}
-void* RecoveryMap::extractRecoveryMap(void* compressed_jpeg_g_image) {
- if (compressed_jpeg_g_image == nullptr) {
- return nullptr;
+status_t RecoveryMap::decodeRecoveryMap(jr_compressed_ptr compressed_recovery_map,
+ jr_uncompressed_ptr dest) {
+ if (compressed_recovery_map == nullptr || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
}
-void* RecoveryMap::appendRecoveryMap(void* compressed_jpeg_image, void* compressed_recovery_map) {
- if (compressed_jpeg_image == nullptr || compressed_recovery_map == nullptr) {
- return nullptr;
+status_t RecoveryMap::encodeRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_compressed_ptr dest) {
+ if (uncompressed_recovery_map == nullptr || dest == nullptr) {
+ return BAD_VALUE;
}
// TBD
- return nullptr;
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_yuv_420_image == nullptr
+ || uncompressed_p010_image == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
+ }
+
+ // TBD
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_yuv_420_image == nullptr
+ || uncompressed_recovery_map == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
+ }
+
+ // TBD
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::extractRecoveryMap(void* compressed_jpegr_image, jr_compressed_ptr dest) {
+ if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ return BAD_VALUE;
+ }
+
+ // TBD
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::appendRecoveryMap(void* compressed_jpeg_image,
+ jr_compressed_ptr compressed_recovery_map,
+ void* dest) {
+ if (compressed_jpeg_image == nullptr
+ || compressed_recovery_map == nullptr
+ || dest == nullptr) {
+ return BAD_VALUE;
+ }
+
+ // TBD
+ return NO_ERROR;
}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 79bf723..7f37f61 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -30,4 +30,36 @@
static_libs: [
"libjpegrecoverymap",
],
+}
+
+cc_test {
+ name: "libjpegencoder_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "jpegencoder_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libjpegencoder",
+ "libgtest",
+ ],
+}
+
+cc_test {
+ name: "libjpegdecoder_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "jpegdecoder_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libjpegdecoder",
+ "libgtest",
+ ],
}
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12
new file mode 100644
index 0000000..7b2fc71
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg
new file mode 100644
index 0000000..20b5a2c
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg
new file mode 100644
index 0000000..41300f4
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.y b/libs/jpegrecoverymap/tests/data/minnie-320x240.y
new file mode 100644
index 0000000..f9d8371
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.y
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12
new file mode 100644
index 0000000..0d66f53
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp
new file mode 100644
index 0000000..8e01351
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/jpegdecoder.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <fcntl.h>
+
+namespace android::recoverymap {
+
+#define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
+#define YUV_IMAGE_SIZE 20193
+#define GREY_IMAGE "/sdcard/Documents/minnie-320x240-y.jpg"
+#define GREY_IMAGE_SIZE 20193
+
+class JpegDecoderTest : public testing::Test {
+public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t size;
+ };
+ JpegDecoderTest();
+ ~JpegDecoderTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mYuvImage, mGreyImage;
+};
+
+JpegDecoderTest::JpegDecoderTest() {}
+
+JpegDecoderTest::~JpegDecoderTest() {}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], JpegDecoderTest::Image* result) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ result->buffer.reset(new uint8_t[length]);
+ if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+void JpegDecoderTest::SetUp() {
+ if (!loadFile(YUV_IMAGE, &mYuvImage)) {
+ FAIL() << "Load file " << YUV_IMAGE << " failed";
+ }
+ mYuvImage.size = YUV_IMAGE_SIZE;
+ if (!loadFile(GREY_IMAGE, &mGreyImage)) {
+ FAIL() << "Load file " << GREY_IMAGE << " failed";
+ }
+ mGreyImage.size = GREY_IMAGE_SIZE;
+}
+
+void JpegDecoderTest::TearDown() {}
+
+TEST_F(JpegDecoderTest, decodeYuvImage) {
+ JpegDecoder decoder;
+ EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+TEST_F(JpegDecoderTest, decodeGreyImage) {
+ JpegDecoder decoder;
+ EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+}
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/jpegencoder_test.cpp b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp
new file mode 100644
index 0000000..4cd2a5e
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/jpegencoder.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <fcntl.h>
+
+namespace android::recoverymap {
+
+#define VALID_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
+#define VALID_IMAGE_WIDTH 320
+#define VALID_IMAGE_HEIGHT 240
+#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y"
+#define SINGLE_CHANNEL_IMAGE_WIDTH VALID_IMAGE_WIDTH
+#define SINGLE_CHANNEL_IMAGE_HEIGHT VALID_IMAGE_HEIGHT
+#define INVALID_SIZE_IMAGE "/sdcard/Documents/minnie-318x240.yu12"
+#define INVALID_SIZE_IMAGE_WIDTH 318
+#define INVALID_SIZE_IMAGE_HEIGHT 240
+#define JPEG_QUALITY 90
+
+class JpegEncoderTest : public testing::Test {
+public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t width;
+ size_t height;
+ };
+ JpegEncoderTest();
+ ~JpegEncoderTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mValidImage, mInvalidSizeImage, mSingleChannelImage;
+};
+
+JpegEncoderTest::JpegEncoderTest() {}
+
+JpegEncoderTest::~JpegEncoderTest() {}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], JpegEncoderTest::Image* result) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ result->buffer.reset(new uint8_t[length]);
+ if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+void JpegEncoderTest::SetUp() {
+ if (!loadFile(VALID_IMAGE, &mValidImage)) {
+ FAIL() << "Load file " << VALID_IMAGE << " failed";
+ }
+ mValidImage.width = VALID_IMAGE_WIDTH;
+ mValidImage.height = VALID_IMAGE_HEIGHT;
+ if (!loadFile(INVALID_SIZE_IMAGE, &mInvalidSizeImage)) {
+ FAIL() << "Load file " << INVALID_SIZE_IMAGE << " failed";
+ }
+ mInvalidSizeImage.width = INVALID_SIZE_IMAGE_WIDTH;
+ mInvalidSizeImage.height = INVALID_SIZE_IMAGE_HEIGHT;
+ if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
+ FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
+ }
+ mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
+ mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
+}
+
+void JpegEncoderTest::TearDown() {}
+
+TEST_F(JpegEncoderTest, validImage) {
+ JpegEncoder encoder;
+ EXPECT_TRUE(encoder.compressImage(mValidImage.buffer.get(), mValidImage.width,
+ mValidImage.height, JPEG_QUALITY, NULL, 0));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+TEST_F(JpegEncoderTest, invalidSizeImage) {
+ JpegEncoder encoder;
+ EXPECT_FALSE(encoder.compressImage(mInvalidSizeImage.buffer.get(), mInvalidSizeImage.width,
+ mInvalidSizeImage.height, JPEG_QUALITY, NULL, 0));
+}
+
+TEST_F(JpegEncoderTest, singleChannelImage) {
+ JpegEncoder encoder;
+ EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width,
+ mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+}
+
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 3503a9e..3b58265 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -74,6 +74,9 @@
override_export_include_dirs: [
"include",
],
+ export_llndk_headers: [
+ "libarect_headers",
+ ],
},
export_include_dirs: [
"include",
@@ -110,16 +113,14 @@
],
header_libs: [
+ "libarect_headers",
"libnativebase_headers",
"libnativewindow_headers",
],
// headers we include in our public headers
- export_static_lib_headers: [
- "libarect",
- ],
-
export_header_lib_headers: [
+ "libarect_headers",
"libnativebase_headers",
],
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 5ff9240..f7f2d54 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -601,7 +601,7 @@
}
if (needs.hasTextureCoords()) {
- fs << "varying vec2 outTexCoords;";
+ fs << "varying highp vec2 outTexCoords;";
}
if (needs.hasRoundedCorners()) {
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index d0c03fe..3a31fa0 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
+#include <ostream>
#include <string>
#include <ftl/optional.h>
@@ -67,6 +68,11 @@
return std::to_string(displayId.value);
}
+// For tests.
+inline std::ostream& operator<<(std::ostream& stream, DisplayId displayId) {
+ return stream << "DisplayId{" << displayId.value << '}';
+}
+
// DisplayId of a physical display, such as the internal display or externally connected display.
struct PhysicalDisplayId : DisplayId {
static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 83c250a..2af51a7 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -21,31 +21,8 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library {
- name: "libvibrator",
- vendor_available: true,
- double_loadable: true,
-
- shared_libs: [
- "libbinder",
- "liblog",
- "libutils",
- ],
-
- header_libs: [
- "libaudio_system_headers",
- ],
-
- aidl: {
- include_dirs: ["frameworks/base/core/java"],
- local_include_dirs: ["include/"],
- export_aidl_headers: true,
- },
-
- srcs: [
- ":libvibrator_aidl",
- "*.cpp",
- ],
+cc_defaults {
+ name: "libvibrator_defaults",
cflags: [
"-Wall",
@@ -64,3 +41,54 @@
},
},
}
+
+cc_library {
+ name: "libvibrator",
+ defaults: ["libvibrator_defaults"],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ whole_static_libs: [
+ "libvibratorutils",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ local_include_dirs: ["include/"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libvibrator_aidl",
+ "ExternalVibration.cpp",
+ ],
+}
+
+cc_library {
+ name: "libvibratorutils",
+ defaults: ["libvibrator_defaults"],
+
+ vendor_available: true,
+ double_loadable: true,
+
+ shared_libs: [
+ "libutils",
+ ],
+
+ srcs: [
+ "ExternalVibrationUtils.cpp",
+ ],
+
+ visibility: [
+ "//frameworks/native/libs/vibrator",
+ "//frameworks/av/media/libeffects/hapticgenerator",
+ ],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index f6fc19e..80e911c 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -15,7 +15,9 @@
*/
#include <vibrator/ExternalVibration.h>
+#include <vibrator/ExternalVibrationUtils.h>
+#include <android/os/IExternalVibratorService.h>
#include <binder/Parcel.h>
#include <log/log.h>
#include <utils/Errors.h>
@@ -63,5 +65,25 @@
return mToken == rhs.mToken;
}
+os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) {
+ switch (externalVibrationScale) {
+ case IExternalVibratorService::SCALE_MUTE:
+ return os::HapticScale::MUTE;
+ case IExternalVibratorService::SCALE_VERY_LOW:
+ return os::HapticScale::VERY_LOW;
+ case IExternalVibratorService::SCALE_LOW:
+ return os::HapticScale::LOW;
+ case IExternalVibratorService::SCALE_NONE:
+ return os::HapticScale::NONE;
+ case IExternalVibratorService::SCALE_HIGH:
+ return os::HapticScale::HIGH;
+ case IExternalVibratorService::SCALE_VERY_HIGH:
+ return os::HapticScale::VERY_HIGH;
+ default:
+ ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale);
+ return os::HapticScale::NONE;
+ }
+}
+
} // namespace os
} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
index 760dbce..00cd3cd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -23,6 +23,7 @@
#include <binder/Parcelable.h>
#include <system/audio.h>
#include <utils/RefBase.h>
+#include <vibrator/ExternalVibrationUtils.h>
namespace android {
namespace os {
@@ -44,6 +45,10 @@
audio_attributes_t getAudioAttributes() const { return mAttrs; }
sp<IExternalVibrationController> getController() { return mController; }
+ /* Converts the scale from non-public ExternalVibrationService into the HapticScale
+ * used by the utils.
+ */
+ static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale);
private:
int32_t mUid;
@@ -53,7 +58,7 @@
sp<IBinder> mToken = new BBinder();
};
-} // namespace android
} // namespace os
+} // namespace android
#endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index 84357fc..ca219d3 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,17 +17,15 @@
#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
-#include <android/os/IExternalVibratorService.h>
-
namespace android::os {
enum class HapticScale {
- MUTE = IExternalVibratorService::SCALE_MUTE,
- VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
- LOW = IExternalVibratorService::SCALE_LOW,
- NONE = IExternalVibratorService::SCALE_NONE,
- HIGH = IExternalVibratorService::SCALE_HIGH,
- VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+ MUTE = -100,
+ VERY_LOW = -2,
+ LOW = -1,
+ NONE = 0,
+ HIGH = 1,
+ VERY_HIGH = 2,
};
bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
deleted file mode 100644
index e33e03b..0000000
--- a/libs/vr/libbufferhubqueue/benchmarks/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"],
-}
-
-cc_benchmark {
- srcs: ["buffer_transport_benchmark.cpp"],
- shared_libs: [
- "libbase",
- "libbinder",
- "libcutils",
- "libdvr.google",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libbufferhubqueue",
- "libpdx_default_transport",
- ],
- cflags: [
- "-DLOG_TAG=\"buffer_transport_benchmark\"",
- "-DTRACE=0",
- "-O2",
- "-Wall",
- "-Werror",
- ],
- name: "buffer_transport_benchmark",
-}
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
deleted file mode 100644
index b6813eb..0000000
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ /dev/null
@@ -1,589 +0,0 @@
-#include <android-base/logging.h>
-#include <android/native_window.h>
-#include <benchmark/benchmark.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <dvr/dvr_api.h>
-#include <gui/BufferItem.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/Surface.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <functional>
-#include <iostream>
-#include <thread>
-#include <vector>
-
-#include <dlfcn.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <sys/wait.h>
-
-// Use ALWAYS at the tag level. Control is performed manually during command
-// line processing.
-#ifdef ATRACE_TAG
-#undef ATRACE_TAG
-#endif
-#define ATRACE_TAG ATRACE_TAG_ALWAYS
-
-using namespace android;
-using ::benchmark::State;
-
-static const String16 kBinderService = String16("bufferTransport");
-static const uint32_t kBufferWidth = 100;
-static const uint32_t kBufferHeight = 1;
-static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-static const uint64_t kBufferUsage =
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-static const uint32_t kBufferLayer = 1;
-static const int kMaxAcquiredImages = 1;
-static const int kQueueDepth = 2; // We are double buffering for this test.
-static const size_t kMaxQueueCounts = 128;
-static const int kInvalidFence = -1;
-
-enum BufferTransportServiceCode {
- CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-// A binder services that minics a compositor that consumes buffers. It provides
-// one Binder interface to create a new Surface for buffer producer to write
-// into; while itself will carry out no-op buffer consuming by acquiring then
-// releasing the buffer immediately.
-class BufferTransportService : public BBinder {
- public:
- BufferTransportService() = default;
- ~BufferTransportService() = default;
-
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) {
- (void)flags;
- (void)data;
- switch (code) {
- case CREATE_BUFFER_QUEUE: {
- auto new_queue = std::make_shared<BufferQueueHolder>(this);
- reply->writeStrongBinder(
- IGraphicBufferProducer::asBinder(new_queue->producer));
- buffer_queues_.push_back(new_queue);
- return OK;
- }
- default:
- return UNKNOWN_TRANSACTION;
- };
- }
-
- private:
- struct FrameListener : public ConsumerBase::FrameAvailableListener {
- public:
- FrameListener(BufferTransportService* /*service*/,
- sp<BufferItemConsumer> buffer_item_consumer)
- : buffer_item_consumer_(buffer_item_consumer) {}
-
- void onFrameAvailable(const BufferItem& /*item*/) override {
- BufferItem buffer;
- status_t ret = 0;
- {
- ATRACE_NAME("AcquireBuffer");
- ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0,
- /*waitForFence=*/false);
- }
-
- if (ret != OK) {
- LOG(ERROR) << "Failed to acquire next buffer.";
- return;
- }
-
- {
- ATRACE_NAME("ReleaseBuffer");
- ret = buffer_item_consumer_->releaseBuffer(buffer);
- }
-
- if (ret != OK) {
- LOG(ERROR) << "Failed to release buffer.";
- return;
- }
- }
-
- private:
- sp<BufferItemConsumer> buffer_item_consumer_;
- };
-
- struct BufferQueueHolder {
- explicit BufferQueueHolder(BufferTransportService* service) {
- BufferQueue::createBufferQueue(&producer, &consumer);
-
- sp<BufferItemConsumer> buffer_item_consumer =
- new BufferItemConsumer(consumer, kBufferUsage, kMaxAcquiredImages,
- /*controlledByApp=*/true);
- buffer_item_consumer->setName(String8("BinderBufferTransport"));
- frame_listener_ = new FrameListener(service, buffer_item_consumer);
- buffer_item_consumer->setFrameAvailableListener(frame_listener_);
- }
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
-
- private:
- sp<FrameListener> frame_listener_;
- };
-
- std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
-};
-
-// A virtual interfaces that abstracts the common BufferQueue operations, so
-// that the test suite can use the same test case to drive different types of
-// transport backends.
-class BufferTransport {
- public:
- virtual ~BufferTransport() {}
-
- virtual int Start() = 0;
- virtual sp<Surface> CreateSurface() = 0;
-};
-
-// Binder-based buffer transport backend.
-//
-// On Start() a new process will be swapned to run a Binder server that
-// actually consumes the buffer.
-// On CreateSurface() a new Binder BufferQueue will be created, which the
-// service holds the concrete binder node of the IGraphicBufferProducer while
-// sending the binder proxy to the client. In another word, the producer side
-// operations are carried out process while the consumer side operations are
-// carried out within the BufferTransportService's own process.
-class BinderBufferTransport : public BufferTransport {
- public:
- BinderBufferTransport() {}
-
- int Start() override {
- sp<IServiceManager> sm = defaultServiceManager();
- service_ = sm->getService(kBinderService);
- if (service_ == nullptr) {
- LOG(ERROR) << "Failed to get the benchmark service.";
- return -EIO;
- }
-
- LOG(INFO) << "Binder server is ready for client.";
- return 0;
- }
-
- sp<Surface> CreateSurface() override {
- Parcel data;
- Parcel reply;
- int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
- if (error != OK) {
- LOG(ERROR) << "Failed to get buffer queue over binder.";
- return nullptr;
- }
-
- sp<IBinder> binder;
- error = reply.readNullableStrongBinder(&binder);
- if (error != OK) {
- LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
- return nullptr;
- }
-
- auto producer = interface_cast<IGraphicBufferProducer>(binder);
- if (producer == nullptr) {
- LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
- return nullptr;
- }
-
- sp<Surface> surface = new Surface(producer, /*controlledByApp=*/true);
-
- // Set buffer dimension.
- ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
- ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
- kBufferFormat);
-
- return surface;
- }
-
- private:
- sp<IBinder> service_;
-};
-
-class DvrApi {
- public:
- DvrApi() {
- handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL);
- CHECK(handle_);
-
- auto dvr_get_api =
- reinterpret_cast<decltype(&dvrGetApi)>(dlsym(handle_, "dvrGetApi"));
- int ret = dvr_get_api(&api_, sizeof(api_), /*version=*/1);
-
- CHECK(ret == 0);
- }
-
- ~DvrApi() { dlclose(handle_); }
-
- const DvrApi_v1& Api() { return api_; }
-
- private:
- void* handle_ = nullptr;
- DvrApi_v1 api_;
-};
-
-// BufferHub/PDX-based buffer transport.
-//
-// On Start() a new thread will be swapned to run an epoll polling thread which
-// minics the behavior of a compositor. Similar to Binder-based backend, the
-// buffer available handler is also a no-op: Buffer gets acquired and released
-// immediately.
-// On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will
-// be created. The epoll thread holds on the consumer queue and dequeues buffer
-// from it; while the producer queue will be wrapped in a Surface and returned
-// to test suite.
-class BufferHubTransport : public BufferTransport {
- public:
- virtual ~BufferHubTransport() {
- stopped_.store(true);
- if (reader_thread_.joinable()) {
- reader_thread_.join();
- }
- }
-
- int Start() override {
- int ret = epoll_fd_.Create();
- if (ret < 0) {
- LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret);
- return -1;
- }
-
- // Create the reader thread.
- reader_thread_ = std::thread([this]() {
- int ret = dvr_.Api().PerformanceSetSchedulerPolicy(0, "graphics");
- if (ret < 0) {
- LOG(ERROR) << "Failed to set scheduler policy, ret=" << ret;
- return;
- }
-
- stopped_.store(false);
- LOG(INFO) << "Reader Thread Running...";
-
- while (!stopped_.load()) {
- std::array<epoll_event, kMaxQueueCounts> events;
-
- // Don't sleep forever so that we will have a chance to wake up.
- const int ret = epoll_fd_.Wait(events.data(), events.size(),
- /*timeout=*/100);
- if (ret < 0) {
- LOG(ERROR) << "Error polling consumer queues.";
- continue;
- }
- if (ret == 0) {
- continue;
- }
-
- const int num_events = ret;
- for (int i = 0; i < num_events; i++) {
- uint32_t index = events[i].data.u32;
- dvr_.Api().ReadBufferQueueHandleEvents(
- buffer_queues_[index]->GetReadQueue());
- }
- }
-
- LOG(INFO) << "Reader Thread Exiting...";
- });
-
- return 0;
- }
-
- sp<Surface> CreateSurface() override {
- auto new_queue = std::make_shared<BufferQueueHolder>();
- if (!new_queue->IsReady()) {
- LOG(ERROR) << "Failed to create BufferHub-based BufferQueue.";
- return nullptr;
- }
-
- // Set buffer dimension.
- ANativeWindow_setBuffersGeometry(new_queue->GetSurface(), kBufferWidth,
- kBufferHeight, kBufferFormat);
-
- // Use the next position as buffer_queue index.
- uint32_t index = buffer_queues_.size();
- epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}};
- int queue_fd =
- dvr_.Api().ReadBufferQueueGetEventFd(new_queue->GetReadQueue());
- const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, queue_fd, &event);
- if (ret < 0) {
- LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret)
- << ", consumer queue fd: " << queue_fd;
- return nullptr;
- }
-
- buffer_queues_.push_back(new_queue);
- ANativeWindow_acquire(new_queue->GetSurface());
- return static_cast<Surface*>(new_queue->GetSurface());
- }
-
- private:
- struct BufferQueueHolder {
- BufferQueueHolder() {
- int ret = 0;
- ret = dvr_.Api().WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferLayer,
- kBufferUsage, 0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create write buffer queue, ret=" << ret;
- return;
- }
-
- ret = dvr_.Api().WriteBufferQueueCreateReadQueue(write_queue_,
- &read_queue_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create read buffer queue, ret=" << ret;
- return;
- }
-
- ret = dvr_.Api().ReadBufferQueueSetBufferAvailableCallback(
- read_queue_, BufferAvailableCallback, this);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create buffer available callback, ret=" << ret;
- return;
- }
-
- ret =
- dvr_.Api().WriteBufferQueueGetANativeWindow(write_queue_, &surface_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create surface, ret=" << ret;
- return;
- }
- }
-
- static void BufferAvailableCallback(void* context) {
- BufferQueueHolder* thiz = static_cast<BufferQueueHolder*>(context);
- thiz->HandleBufferAvailable();
- }
-
- DvrReadBufferQueue* GetReadQueue() { return read_queue_; }
-
- ANativeWindow* GetSurface() { return surface_; }
-
- bool IsReady() {
- return write_queue_ != nullptr && read_queue_ != nullptr &&
- surface_ != nullptr;
- }
-
- void HandleBufferAvailable() {
- int ret = 0;
- DvrNativeBufferMetadata meta;
- DvrReadBuffer* buffer = nullptr;
- DvrNativeBufferMetadata metadata;
- int acquire_fence = kInvalidFence;
-
- {
- ATRACE_NAME("AcquireBuffer");
- ret = dvr_.Api().ReadBufferQueueAcquireBuffer(
- read_queue_, 0, &buffer, &metadata, &acquire_fence);
- }
- if (ret < 0) {
- LOG(ERROR) << "Failed to acquire consumer buffer, error: " << ret;
- return;
- }
-
- if (buffer != nullptr) {
- ATRACE_NAME("ReleaseBuffer");
- ret = dvr_.Api().ReadBufferQueueReleaseBuffer(read_queue_, buffer,
- &meta, kInvalidFence);
- }
- if (ret < 0) {
- LOG(ERROR) << "Failed to release consumer buffer, error: " << ret;
- }
- }
-
- private:
- DvrWriteBufferQueue* write_queue_ = nullptr;
- DvrReadBufferQueue* read_queue_ = nullptr;
- ANativeWindow* surface_ = nullptr;
- };
-
- static DvrApi dvr_;
- std::atomic<bool> stopped_;
- std::thread reader_thread_;
-
- dvr::EpollFileDescriptor epoll_fd_;
- std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
-};
-
-DvrApi BufferHubTransport::dvr_ = {};
-
-enum TransportType {
- kBinderBufferTransport,
- kBufferHubTransport,
-};
-
-// Main test suite, which supports two transport backend: 1) BinderBufferQueue,
-// 2) BufferHubQueue. The test case drives the producer end of both transport
-// backend by queuing buffers into the buffer queue by using ANativeWindow API.
-class BufferTransportBenchmark : public ::benchmark::Fixture {
- public:
- void SetUp(State& state) override {
- if (state.thread_index == 0) {
- const int transport = state.range(0);
- switch (transport) {
- case kBinderBufferTransport:
- transport_.reset(new BinderBufferTransport);
- break;
- case kBufferHubTransport:
- transport_.reset(new BufferHubTransport);
- break;
- default:
- CHECK(false) << "Unknown test case.";
- break;
- }
-
- CHECK(transport_);
- const int ret = transport_->Start();
- CHECK_EQ(ret, 0);
-
- LOG(INFO) << "Transport backend running, transport=" << transport << ".";
-
- // Create surfaces for each thread.
- surfaces_.resize(state.threads);
- for (int i = 0; i < state.threads; i++) {
- // Common setup every thread needs.
- surfaces_[i] = transport_->CreateSurface();
- CHECK(surfaces_[i]);
-
- LOG(INFO) << "Surface initialized on thread " << i << ".";
- }
- }
- }
-
- void TearDown(State& state) override {
- if (state.thread_index == 0) {
- surfaces_.clear();
- transport_.reset();
- LOG(INFO) << "Tear down benchmark.";
- }
- }
-
- protected:
- std::unique_ptr<BufferTransport> transport_;
- std::vector<sp<Surface>> surfaces_;
-};
-
-BENCHMARK_DEFINE_F(BufferTransportBenchmark, Producers)(State& state) {
- ANativeWindow* window = nullptr;
- ANativeWindow_Buffer buffer;
- int32_t error = 0;
- double total_gain_buffer_us = 0;
- double total_post_buffer_us = 0;
- int iterations = 0;
-
- while (state.KeepRunning()) {
- if (window == nullptr) {
- CHECK(surfaces_[state.thread_index]);
- window = static_cast<ANativeWindow*>(surfaces_[state.thread_index].get());
-
- // Lock buffers a couple time from the queue, so that we have the buffer
- // allocated.
- for (int i = 0; i < kQueueDepth; i++) {
- error = ANativeWindow_lock(window, &buffer,
- /*inOutDirtyBounds=*/nullptr);
- CHECK_EQ(error, 0);
- error = ANativeWindow_unlockAndPost(window);
- CHECK_EQ(error, 0);
- }
- }
-
- {
- ATRACE_NAME("GainBuffer");
- auto t1 = std::chrono::high_resolution_clock::now();
- error = ANativeWindow_lock(window, &buffer,
- /*inOutDirtyBounds=*/nullptr);
- auto t2 = std::chrono::high_resolution_clock::now();
- std::chrono::duration<double, std::micro> delta_us = t2 - t1;
- total_gain_buffer_us += delta_us.count();
- }
- CHECK_EQ(error, 0);
-
- {
- ATRACE_NAME("PostBuffer");
- auto t1 = std::chrono::high_resolution_clock::now();
- error = ANativeWindow_unlockAndPost(window);
- auto t2 = std::chrono::high_resolution_clock::now();
- std::chrono::duration<double, std::micro> delta_us = t2 - t1;
- total_post_buffer_us += delta_us.count();
- }
- CHECK_EQ(error, 0);
-
- iterations++;
- }
-
- state.counters["gain_buffer_us"] = ::benchmark::Counter(
- total_gain_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
- state.counters["post_buffer_us"] = ::benchmark::Counter(
- total_post_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
- state.counters["producer_us"] = ::benchmark::Counter(
- (total_gain_buffer_us + total_post_buffer_us) / iterations,
- ::benchmark::Counter::kAvgThreads);
-}
-
-BENCHMARK_REGISTER_F(BufferTransportBenchmark, Producers)
- ->Unit(::benchmark::kMicrosecond)
- ->Ranges({{kBinderBufferTransport, kBufferHubTransport}})
- ->ThreadRange(1, 32);
-
-static void runBinderServer() {
- ProcessState::self()->setThreadPoolMaxThreadCount(0);
- ProcessState::self()->startThreadPool();
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<BufferTransportService> service = new BufferTransportService;
- sm->addService(kBinderService, service, false);
-
- LOG(INFO) << "Binder server running...";
-
- while (true) {
- int stat, retval;
- retval = wait(&stat);
- if (retval == -1 && errno == ECHILD) {
- break;
- }
- }
-
- LOG(INFO) << "Service Exiting...";
-}
-
-// To run binder-based benchmark, use:
-// adb shell buffer_transport_benchmark \
-// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/0/"
-//
-// To run bufferhub-based benchmark, use:
-// adb shell buffer_transport_benchmark \
-// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/1/"
-int main(int argc, char** argv) {
- bool tracing_enabled = false;
-
- // Parse arguments in addition to "--benchmark_filter" paramters.
- for (int i = 1; i < argc; i++) {
- if (std::string(argv[i]) == "--help") {
- std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl;
- std::cout << "\t--trace: Enable systrace logging." << std::endl;
- return 0;
- }
- if (std::string(argv[i]) == "--trace") {
- tracing_enabled = true;
- continue;
- }
- }
-
- // Setup ATRACE/systrace based on command line.
- atrace_setup();
- atrace_set_tracing_enabled(tracing_enabled);
-
- pid_t pid = fork();
- if (pid == 0) {
- // Child, i.e. the client side.
- ProcessState::self()->startThreadPool();
-
- ::benchmark::Initialize(&argc, argv);
- ::benchmark::RunSpecifiedBenchmarks();
- } else {
- LOG(INFO) << "Benchmark process pid: " << pid;
- runBinderServer();
- }
-}
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 33a0d75..e373376 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -48,19 +48,3 @@
],
name: "buffer_hub_queue-test",
}
-
-cc_test {
- srcs: ["buffer_hub_queue_producer-test.cpp"],
- header_libs: header_libraries,
- static_libs: static_libraries,
- shared_libs: shared_libraries,
- cflags: [
- "-DLOG_TAG=\"buffer_hub_queue_producer-test\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- ],
- name: "buffer_hub_queue_producer-test",
-}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
deleted file mode 100644
index fab1097..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-#include <base/logging.h>
-#include <gui/BufferHubProducer.h>
-#include <gui/IProducerListener.h>
-#include <gui/Surface.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalHandle;
-
-namespace {
-
-// Default dimensions before setDefaultBufferSize is called by the consumer.
-constexpr uint32_t kDefaultWidth = 1;
-constexpr uint32_t kDefaultHeight = 1;
-
-// Default format before setDefaultBufferFormat is called by the consumer.
-constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-constexpr int kDefaultConsumerUsageBits = 0;
-
-// Default transform hint before setTransformHint is called by the consumer.
-constexpr uint32_t kDefaultTransformHint = 0;
-
-constexpr int kTestApi = NATIVE_WINDOW_API_CPU;
-constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL;
-constexpr int kTestApiInvalid = 0xDEADBEEF;
-constexpr int kTestProducerUsageBits = 0;
-constexpr bool kTestControlledByApp = true;
-
-// Builder pattern to slightly vary *almost* correct input
-// -- avoids copying and pasting
-struct QueueBufferInputBuilder {
- IGraphicBufferProducer::QueueBufferInput build() {
- return IGraphicBufferProducer::QueueBufferInput(
- mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode,
- mTransform, mFence);
- }
-
- QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
- this->mTimestamp = timestamp;
- return *this;
- }
-
- QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
- this->mIsAutoTimestamp = isAutoTimestamp;
- return *this;
- }
-
- QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) {
- this->mDataSpace = dataSpace;
- return *this;
- }
-
- QueueBufferInputBuilder& setCrop(Rect crop) {
- this->mCrop = crop;
- return *this;
- }
-
- QueueBufferInputBuilder& setScalingMode(int scalingMode) {
- this->mScalingMode = scalingMode;
- return *this;
- }
-
- QueueBufferInputBuilder& setTransform(uint32_t transform) {
- this->mTransform = transform;
- return *this;
- }
-
- QueueBufferInputBuilder& setFence(sp<Fence> fence) {
- this->mFence = fence;
- return *this;
- }
-
- private:
- int64_t mTimestamp{1384888611};
- bool mIsAutoTimestamp{false};
- android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN};
- Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)};
- int mScalingMode{0};
- uint32_t mTransform{0};
- sp<Fence> mFence{Fence::NO_FENCE};
-};
-
-// This is a test that covers our implementation of bufferhubqueue-based
-// IGraphicBufferProducer.
-class BufferHubQueueProducerTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
- auto config = ProducerQueueConfigBuilder().Build();
- auto queue = ProducerQueue::Create(config, UsagePolicy{});
- ASSERT_TRUE(queue != nullptr);
-
- mProducer = BufferHubProducer::Create(std::move(queue));
- ASSERT_TRUE(mProducer != nullptr);
- mSurface = new Surface(mProducer, true);
- ASSERT_TRUE(mSurface != nullptr);
- }
-
- // Connect to a producer in a 'correct' fashion.
- void ConnectProducer() {
- IGraphicBufferProducer::QueueBufferOutput output;
- // Can connect the first time.
- ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, &output));
- }
-
- // Dequeue a buffer in a 'correct' fashion.
- // Precondition: Producer is connected.
- void DequeueBuffer(int* outSlot) {
- sp<Fence> fence;
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence));
- }
-
- void DequeueBuffer(int* outSlot, sp<Fence>* outFence) {
- ASSERT_NE(nullptr, outSlot);
- ASSERT_NE(nullptr, outFence);
-
- int ret = mProducer->dequeueBuffer(
- outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits, nullptr, nullptr);
- // BUFFER_NEEDS_REALLOCATION can be either on or off.
- ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
-
- // Slot number should be in boundary.
- ASSERT_LE(0, *outSlot);
- ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot);
- }
-
- // Create a generic "valid" input for queueBuffer
- // -- uses the default buffer format, width, etc.
- static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
- return QueueBufferInputBuilder().build();
- }
-
- const sp<IProducerListener> kStubListener{new StubProducerListener};
-
- sp<BufferHubProducer> mProducer;
- sp<Surface> mSurface;
-};
-
-TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) {
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // NULL output returns BAD_VALUE
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, nullptr));
-
- // Invalid API returns bad value
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid,
- kTestControlledByApp, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Can't connect when there is already a producer connected.
- IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
-}
-
-TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Must disconnect with same API number
- EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther));
- // API must not be out of range
- EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid));
-}
-
-TEST_F(BufferHubQueueProducerTest, Query_Succeeds) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int32_t value = -1;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
- EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
- EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
- EXPECT_EQ(kDefaultFormat, value);
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
- EXPECT_LE(0, value);
- EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
-
- EXPECT_EQ(OK,
- mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
- EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
- EXPECT_EQ(kDefaultConsumerUsageBits, value);
-}
-
-TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // One past the end of the last 'query' enum value. Update this if we add more
- // enums.
- const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1;
-
- int value;
- // What was out of range
- EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value));
- EXPECT_EQ(BAD_VALUE,
- mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value));
-
- // Some enums from window.h are 'invalid'
- EXPECT_EQ(BAD_VALUE,
- mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
-
- // Value was NULL
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL));
-}
-
-TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- // Request the buffer (pre-requisite for queueing)
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // Queue the buffer back into the BQ
- ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-
- EXPECT_EQ(kDefaultWidth, output.width);
- EXPECT_EQ(kDefaultHeight, output.height);
- EXPECT_EQ(kDefaultTransformHint, output.transformHint);
-
- // BufferHubQueue delivers buffers to consumer immediately.
- EXPECT_EQ(0u, output.numPendingBuffers);
-
- // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to
- // be a SurfaceFlinger specific optimization.
- EXPECT_EQ(0u, output.nextFrameNumber);
-
- // Buffer was not in the dequeued state
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test invalid slot number
-TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output));
- EXPECT_EQ(BAD_VALUE,
- mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output));
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS,
- input, &output));
-}
-
-// Slot was not in the dequeued state (all slots start out in Free state)
-TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output));
-}
-
-// Slot was enqueued without requesting a buffer
-TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test when fence was NULL
-TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- sp<Fence> nullFence = NULL;
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder().setFence(nullFence).build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test scaling mode was invalid
-TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder().setScalingMode(-1).build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-
- input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test crop rect is out of bounds of the buffer dimensions
-TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder()
- .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1))
- .build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) {
- int slot = -1;
- sp<Fence> fence;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
-
- // Should be able to cancel buffer after a dequeue.
- EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
-}
-
-TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
- return;
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int minUndequeuedBuffers;
- ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
-
- const int minBuffers = 1;
- const int maxBuffers =
- BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
-
- ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
- << "bufferCount: " << minBuffers;
-
- // Should now be able to dequeue up to minBuffers times
- // Should now be able to dequeue up to maxBuffers times
- int slot = -1;
- for (int i = 0; i < minBuffers; ++i) {
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- }
-
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
-
- // queue the first buffer to enable max dequeued buffer count checking
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-
- sp<Fence> fence;
- for (int i = 0; i < maxBuffers; ++i) {
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
- }
-
- // Cancel a buffer, so we can decrease the buffer count
- ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
-
- // Should now be able to decrease the max dequeued count by 1
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
-}
-
-TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int minUndequeuedBuffers;
- ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
-
- const int minBuffers = 1;
- const int maxBuffers =
- BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
-
- ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
- // Buffer count was out of range
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
- << "bufferCount: " << 0;
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
- << "bufferCount: " << maxBuffers + 1;
-
- // Set max dequeue count to 2
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
- // Dequeue 2 buffers
- int slot = -1;
- sp<Fence> fence;
- for (int i = 0; i < 2; i++) {
- ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
- (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
- kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits,
- nullptr, nullptr)))
- << "slot: " << slot;
- }
-
- // Client has too many buffers dequeued
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1))
- << "bufferCount: " << minBuffers;
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_dequeueBuffer) {
- int slot = -1;
- sp<Fence> fence;
-
- ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
- kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits,
- nullptr, nullptr));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_requestBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- // Shouldn't be able to request buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_queueBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // Shouldn't be able to queue buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_cancelBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // Shouldn't be able to cancel buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
-}
-
-TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
-
- constexpr int maxDequeuedBuffers = 1;
- int minUndequeuedBuffers;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
- EXPECT_EQ(OK, mProducer->setAsyncMode(false));
- EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
-
- int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
-
- // Dequeue, request, and queue all buffers.
- for (int i = 0; i < maxCapacity; i++) {
- EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- }
-
- // Disconnect then reconnect.
- EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Dequeue, request, and queue all buffers.
- for (int i = 0; i < maxCapacity; i++) {
- EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- }
-
- EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
-}
-
-TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
- // Connected producer cannot be taken out as a parcelable.
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
- ProducerQueueParcelable producer_parcelable;
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE);
-
- // Create a valid fake producer parcelable.
- auto fake_channel_parcelable =
- std::make_unique<pdx::default_transport::ChannelParcelable>(
- LocalHandle(0), LocalHandle(0), LocalHandle(0));
- EXPECT_TRUE(fake_channel_parcelable->IsValid());
- ProducerQueueParcelable fake_producer_parcelable(
- std::move(fake_channel_parcelable));
- EXPECT_TRUE(fake_producer_parcelable.IsValid());
-
- // Disconnect producer can be taken out, but only to an invalid parcelable.
- ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
- EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE);
- EXPECT_FALSE(producer_parcelable.IsValid());
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
- EXPECT_TRUE(producer_parcelable.IsValid());
-
- // Should still be able to query buffer dimension after disconnect.
- int32_t value = -1;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
- EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
-
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
- EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
-
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
- EXPECT_EQ(value, kDefaultFormat);
-
- // But connect to API will fail.
- IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp,
- &output),
- BAD_VALUE);
-
- // Create a new producer from the parcelable and connect to kTestApi should
- // succeed.
- sp<BufferHubProducer> new_producer =
- BufferHubProducer::Create(std::move(producer_parcelable));
- ASSERT_TRUE(new_producer != nullptr);
- EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp,
- &output),
- OK);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 96023dd..9dbeacb 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -33,87 +33,3 @@
],
min_sdk_version: "29",
}
-
-cc_library_headers {
- name: "libdvr_private_headers",
- export_include_dirs: ["."],
- vendor_available: false,
-}
-
-cflags = [
- "-DDVR_TRACKING_IMPLEMENTED=0",
- "-DLOG_TAG=\"libdvr\"",
- "-DTRACE=0",
- "-Wall",
- "-Werror",
-]
-
-srcs = [
- "dvr_api.cpp",
- "dvr_buffer.cpp",
- "dvr_buffer_queue.cpp",
- "dvr_configuration_data.cpp",
- "dvr_display_manager.cpp",
- "dvr_hardware_composer_client.cpp",
- "dvr_performance.cpp",
- "dvr_pose.cpp",
- "dvr_surface.cpp",
- "dvr_tracking.cpp",
-]
-
-static_libs = [
- "libbroadcastring",
- "libvrsensor",
- "libdisplay",
- "libvirtualtouchpadclient",
- "libvr_hwc-impl",
- "libvr_hwc-binder",
- "libgrallocusage",
- "libperformance",
-]
-
-shared_libs = [
- "android.hardware.graphics.bufferqueue@1.0",
- "android.hidl.token@1.0-utils",
- "libbase",
- "libbufferhubqueue",
- "libbinder",
- "liblog",
- "libcutils",
- "libutils",
- "libnativewindow",
- "libgui",
- "libui",
- "libpdx_default_transport",
-]
-
-cc_library_shared {
- name: "libdvr.google",
- system_ext_specific: true,
- owner: "google",
- cflags: cflags,
- header_libs: ["libdvr_headers"],
- export_header_lib_headers: ["libdvr_headers"],
- srcs: srcs,
- static_libs: static_libs,
- shared_libs: shared_libs,
- version_script: "exported_apis.lds",
-}
-
-// Also build a static libdvr for linking into tests. The linker script
-// restricting function access in the shared lib makes it inconvenient to use in
-// test code.
-cc_library_static {
- name: "libdvr_static.google",
- owner: "google",
- cflags: cflags,
- header_libs: ["libdvr_headers"],
- export_header_lib_headers: ["libdvr_headers"],
- srcs: srcs,
- static_libs: static_libs,
- shared_libs: shared_libs,
-}
-
-subdirs = [
- "tests",
-]
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
deleted file mode 100644
index e099f6a..0000000
--- a/libs/vr/libdvr/dvr_api.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "include/dvr/dvr_api.h"
-
-#include <errno.h>
-#include <utils/Log.h>
-
-#include <algorithm>
-
-// Headers from libdvr
-#include <dvr/dvr_buffer.h>
-#include <dvr/dvr_buffer_queue.h>
-#include <dvr/dvr_configuration_data.h>
-#include <dvr/dvr_display_manager.h>
-#include <dvr/dvr_performance.h>
-#include <dvr/dvr_surface.h>
-#include <dvr/dvr_tracking.h>
-#include <dvr/dvr_vsync.h>
-
-// Headers not yet moved into libdvr.
-// TODO(jwcai) Move these once their callers are moved into Google3.
-#include <dvr/dvr_hardware_composer_client.h>
-#include <dvr/pose_client.h>
-#include <dvr/virtual_touchpad_client.h>
-
-extern "C" {
-
-int dvrGetApi(void* api, size_t struct_size, int version) {
- ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
- version);
- if (version == 1) {
- // New entry points are added at the end. If the caller's struct and
- // this library have different sizes, we define the entry points in common.
- // The caller is expected to handle unset entry points if necessary.
- size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1));
- DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
-
-// Defines an API entry for V1 (no version suffix).
-#define DVR_V1_API_ENTRY(name) \
- do { \
- if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
- clamped_struct_size) { \
- dvr_api->name = dvr##name; \
- } \
- } while (0)
-
-#define DVR_V1_API_ENTRY_DEPRECATED(name) \
- do { \
- if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
- clamped_struct_size) { \
- dvr_api->name = nullptr; \
- } \
- } while (0)
-
-#include "include/dvr/dvr_api_entries.h"
-
-// Undefine macro definitions to play nice with Google3 style rules.
-#undef DVR_V1_API_ENTRY
-#undef DVR_V1_API_ENTRY_DEPRECATED
-
- return 0;
- }
-
- ALOGE("dvrGetApi: Unknown API version=%d", version);
- return -EINVAL;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
deleted file mode 100644
index c11706f..0000000
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "include/dvr/dvr_buffer.h"
-
-#include <android/hardware_buffer.h>
-#include <dvr/dvr_shared_buffers.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-#include <ui/GraphicBuffer.h>
-
-#include "dvr_internal.h"
-
-using namespace android;
-
-namespace android {
-namespace dvr {
-
-DvrBuffer* CreateDvrBufferFromIonBuffer(
- const std::shared_ptr<IonBuffer>& ion_buffer) {
- if (!ion_buffer)
- return nullptr;
- return new DvrBuffer{std::move(ion_buffer)};
-}
-
-} // namespace dvr
-} // namespace android
-
-namespace {
-
-int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!hardware_buffer || !graphic_buffer) {
- return -EINVAL;
- }
- *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer);
- AHardwareBuffer_acquire(*hardware_buffer);
- return 0;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
- if (write_buffer != nullptr) {
- ALOGW_IF(
- write_buffer->slot != -1,
- "dvrWriteBufferDestroy: Destroying a buffer associated with a valid "
- "buffer queue slot. This may indicate possible leaks, buffer_id=%d.",
- dvrWriteBufferGetId(write_buffer));
- delete write_buffer;
- }
-}
-
-int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
- return write_buffer && write_buffer->write_buffer;
-}
-
-int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
- if (!write_buffer || !write_buffer->write_buffer)
- return -EINVAL;
-
- return write_buffer->write_buffer->id();
-}
-
-int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!write_buffer || !write_buffer->write_buffer)
- return -EINVAL;
-
- return ConvertToAHardwareBuffer(
- write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer);
-}
-
-void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) {
- if (read_buffer != nullptr) {
- ALOGW_IF(
- read_buffer->slot != -1,
- "dvrReadBufferDestroy: Destroying a buffer associated with a valid "
- "buffer queue slot. This may indicate possible leaks, buffer_id=%d.",
- dvrReadBufferGetId(read_buffer));
- delete read_buffer;
- }
-}
-
-int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
- return read_buffer && read_buffer->read_buffer;
-}
-
-int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
- if (!read_buffer || !read_buffer->read_buffer)
- return -EINVAL;
-
- return read_buffer->read_buffer->id();
-}
-
-int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!read_buffer || !read_buffer->read_buffer)
- return -EINVAL;
-
- return ConvertToAHardwareBuffer(
- read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer);
-}
-
-void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }
-
-int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!buffer || !buffer->buffer || !hardware_buffer) {
- return -EINVAL;
- }
-
- return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
- hardware_buffer);
-}
-
-// Retrieve the shared buffer layout version defined in dvr_shared_buffers.h.
-int dvrBufferGlobalLayoutVersionGet() {
- return android::dvr::kSharedBufferLayoutVersion;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
deleted file mode 100644
index 1ca653c..0000000
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-#include "include/dvr/dvr_api.h"
-#include "include/dvr/dvr_buffer_queue.h"
-
-#include <android/native_window.h>
-#include <gui/BufferHubProducer.h>
-
-#include "dvr_internal.h"
-#include "dvr_buffer_queue_internal.h"
-
-using namespace android;
-using android::dvr::BufferHubBase;
-using android::dvr::ConsumerBuffer;
-using android::dvr::ConsumerQueue;
-using android::dvr::ProducerBuffer;
-using android::dvr::ProducerQueue;
-using android::dvr::ProducerQueueConfigBuilder;
-using android::dvr::UsagePolicy;
-
-extern "C" {
-
-DvrWriteBufferQueue::DvrWriteBufferQueue(
- const std::shared_ptr<ProducerQueue>& producer_queue)
- : producer_queue_(producer_queue),
- width_(producer_queue->default_width()),
- height_(producer_queue->default_height()),
- format_(producer_queue->default_format()) {}
-
-int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) {
- if (native_window_ == nullptr) {
- // Lazy creation of |native_window|, as not everyone is using
- // DvrWriteBufferQueue as an external surface.
- sp<IGraphicBufferProducer> gbp = BufferHubProducer::Create(producer_queue_);
- native_window_ = new Surface(gbp, true);
- }
-
- *out_window = static_cast<ANativeWindow*>(native_window_.get());
- return 0;
-}
-
-int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
- std::unique_ptr<ConsumerQueue> consumer_queue =
- producer_queue_->CreateConsumerQueue();
- if (consumer_queue == nullptr) {
- ALOGE(
- "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue "
- "from producer queue: queue_id=%d.", producer_queue_->id());
- return -ENOMEM;
- }
-
- *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
- return 0;
-}
-
-int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
- int* out_fence_fd) {
- DvrNativeBufferMetadata meta;
- DvrWriteBuffer* buffer = nullptr;
- int fence_fd = -1;
- if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd))
- return ret;
- if (!buffer)
- return -ENOMEM;
-
- write_buffers_[buffer->slot].reset(buffer);
- write_buffer->write_buffer = std::move(buffer->write_buffer);
- *out_fence_fd = fence_fd;
- return 0;
-}
-
-int DvrWriteBufferQueue::GainBuffer(int timeout,
- DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- size_t slot;
- pdx::LocalHandle release_fence;
-
- // Need to retry N+1 times, where N is total number of buffers in the queue.
- // As in the worst case, we will dequeue all N buffers and reallocate them, on
- // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension.
- size_t max_retries = 1 + producer_queue_->capacity();
- size_t retry = 0;
-
- for (; retry < max_retries; retry++) {
- auto buffer_status =
- producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence);
- if (!buffer_status) {
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- if (write_buffers_[slot] == nullptr) {
- // Lazy initialization of a write_buffers_ slot. Note that a slot will
- // only be dynamically allocated once during the entire cycle life of a
- // queue.
- write_buffers_[slot] = std::make_unique<DvrWriteBuffer>();
- write_buffers_[slot]->slot = slot;
- }
-
- LOG_ALWAYS_FATAL_IF(
- write_buffers_[slot]->write_buffer,
- "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
- write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
-
- const auto& producer_buffer = write_buffers_[slot]->write_buffer;
- if (!producer_buffer)
- return -ENOMEM;
-
- if (width_ == producer_buffer->width() &&
- height_ == producer_buffer->height() &&
- format_ == producer_buffer->format()) {
- // Producer queue returns a buffer matches the current request.
- break;
- }
-
- // Needs reallocation. Note that if there are already multiple available
- // buffers in the queue, the next one returned from |queue_->Dequeue| may
- // still have the old buffer dimension or format. Retry up to N+1 times or
- // until we dequeued a buffer with new configuration.
- ALOGD_IF(TRACE,
- "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
- "(w=%u, h=%u, fmt=%u) is different from the buffer returned "
- "(w=%u, h=%u, fmt=%u). Need re-allocation.",
- slot, width_, height_, format_, producer_buffer->width(),
- producer_buffer->height(), producer_buffer->format());
-
- // Currently, we are not storing |layer_count| and |usage| in queue
- // configuration. Copy those setup from the last buffer dequeued before we
- // remove it.
- uint32_t old_layer_count = producer_buffer->layer_count();
- uint64_t old_usage = producer_buffer->usage();
-
- // Allocate a new producer buffer with new buffer configs. Note that if
- // there are already multiple available buffers in the queue, the next one
- // returned from |queue_->Dequeue| may still have the old buffer dimension
- // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until
- // we dequeued a buffer with new configuration.
- auto remove_status = producer_queue_->RemoveBuffer(slot);
- if (!remove_status) {
- ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s",
- remove_status.GetErrorMessage().c_str());
- return -remove_status.error();
- }
- // Make sure that the previously allocated buffer is dereferenced from
- // write_buffers_ array.
- write_buffers_[slot]->write_buffer = nullptr;
-
- auto allocate_status = producer_queue_->AllocateBuffer(
- width_, height_, old_layer_count, format_, old_usage);
- if (!allocate_status) {
- ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s",
- allocate_status.GetErrorMessage().c_str());
- return -allocate_status.error();
- }
- }
-
- if (retry >= max_retries) {
- ALOGE(
- "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after "
- "resizing.");
- return -ENOMEM;
- }
-
- *out_write_buffer = write_buffers_[slot].release();
- *out_fence_fd = release_fence.Release();
-
- return 0;
-}
-
-int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta,
- int ready_fence_fd) {
- // Some basic sanity checks before we put the buffer back into a slot.
- size_t slot = static_cast<size_t>(write_buffer->slot);
- LOG_FATAL_IF(
- (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()),
- "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
-
- if (write_buffers_[slot] != nullptr) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot);
- return -EINVAL;
- }
- if (write_buffer->write_buffer == nullptr) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer.");
- return -EINVAL;
- }
- if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) {
- ALOGE(
- "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not "
- "belong to this buffer queue. Posting buffer: id=%d, buffer in "
- "queue: id=%d",
- write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot));
- return -EINVAL;
- }
-
- write_buffer->write_buffer->SetQueueIndex(next_post_index_++);
- pdx::LocalHandle fence(ready_fence_fd);
- const int ret = write_buffer->write_buffer->PostAsync(meta, fence);
- if (ret < 0) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d",
- ret);
- return ret;
- }
-
- // Put the DvrWriteBuffer pointer back into its slot for reuse.
- write_buffers_[slot].reset(write_buffer);
- // It's import to reset the write buffer client now. It should stay invalid
- // until next GainBuffer on the same slot.
- write_buffers_[slot]->write_buffer = nullptr;
- return 0;
-}
-
-int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) {
- if (width == 0 || height == 0) {
- ALOGE(
- "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, "
- "h=%u.",
- width, height);
- return -EINVAL;
- }
-
- width_ = width;
- height_ = height;
- return 0;
-}
-
-int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format,
- uint32_t layer_count, uint64_t usage,
- size_t capacity, size_t metadata_size,
- DvrWriteBufferQueue** out_write_queue) {
- if (!out_write_queue)
- return -EINVAL;
-
- auto config_builder = ProducerQueueConfigBuilder()
- .SetDefaultWidth(width)
- .SetDefaultHeight(height)
- .SetDefaultFormat(format)
- .SetMetadataSize(metadata_size);
- std::unique_ptr<ProducerQueue> producer_queue =
- ProducerQueue::Create(config_builder.Build(), UsagePolicy{});
- if (!producer_queue) {
- ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue.");
- return -ENOMEM;
- }
-
- auto status = producer_queue->AllocateBuffers(width, height, layer_count,
- format, usage, capacity);
- if (!status.ok()) {
- ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers.");
- return -ENOMEM;
- }
-
- *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue));
- return 0;
-}
-
-void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
- delete write_queue;
-}
-
-ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->capacity();
-}
-
-int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->id();
-}
-
-int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue,
- ANativeWindow** out_window) {
- if (!write_queue || !out_window)
- return -EINVAL;
-
- return write_queue->GetNativeWindow(out_window);
-}
-
-int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
- DvrReadBufferQueue** out_read_queue) {
- if (!write_queue || !out_read_queue)
- return -EINVAL;
-
- return write_queue->CreateReadQueue(out_read_queue);
-}
-
-int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
- DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd)
- return -EINVAL;
-
- return write_queue->GainBuffer(timeout, out_write_buffer, out_meta,
- out_fence_fd);
-}
-
-int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
- DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta,
- int ready_fence_fd) {
- if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta)
- return -EINVAL;
-
- return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd);
-}
-
-int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
- uint32_t width, uint32_t height) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->ResizeBuffer(width, height);
-}
-
-// ReadBufferQueue
-
-DvrReadBufferQueue::DvrReadBufferQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue)
- : consumer_queue_(consumer_queue) {}
-
-int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
- std::unique_ptr<ConsumerQueue> consumer_queue =
- consumer_queue_->CreateConsumerQueue();
- if (consumer_queue == nullptr) {
- ALOGE(
- "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue "
- "from producer queue: queue_id=%d.", consumer_queue_->id());
- return -ENOMEM;
- }
-
- *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
- return 0;
-}
-
-int DvrReadBufferQueue::AcquireBuffer(int timeout,
- DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- size_t slot;
- pdx::LocalHandle acquire_fence;
- auto buffer_status =
- consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence);
- if (!buffer_status) {
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- if (read_buffers_[slot] == nullptr) {
- // Lazy initialization of a read_buffers_ slot. Note that a slot will only
- // be dynamically allocated once during the entire cycle life of a queue.
- read_buffers_[slot] = std::make_unique<DvrReadBuffer>();
- read_buffers_[slot]->slot = slot;
- }
-
- LOG_FATAL_IF(
- read_buffers_[slot]->read_buffer,
- "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot);
- read_buffers_[slot]->read_buffer = std::move(buffer_status.take());
-
- *out_read_buffer = read_buffers_[slot].release();
- *out_fence_fd = acquire_fence.Release();
-
- return 0;
-}
-
-int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta,
- int release_fence_fd) {
- // Some basic sanity checks before we put the buffer back into a slot.
- size_t slot = static_cast<size_t>(read_buffer->slot);
- LOG_FATAL_IF(
- (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()),
- "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
-
- if (read_buffers_[slot] != nullptr) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot);
- return -EINVAL;
- }
- if (read_buffer->read_buffer == nullptr) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer.");
- return -EINVAL;
- }
- if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) {
- if (consumer_queue_->GetBufferId(slot) > 0) {
- ALOGE(
- "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not "
- "belong to this queue (queue_id=%d): attempting to release buffer "
- "(buffer_id=%d) at slot %d which holds a different buffer "
- "(buffer_id=%d).",
- consumer_queue_->id(), read_buffer->read_buffer->id(),
- static_cast<int>(slot), consumer_queue_->GetBufferId(slot));
- } else {
- ALOGI(
- "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not "
- "belong to this queue (queue_id=%d): attempting to release buffer "
- "(buffer_id=%d) at slot %d which is empty.",
- consumer_queue_->id(), read_buffer->read_buffer->id(),
- static_cast<int>(slot));
- }
- }
-
- pdx::LocalHandle fence(release_fence_fd);
- int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence);
- if (ret < 0) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d",
- ret);
- return ret;
- }
-
- // Put the DvrReadBuffer pointer back into its slot for reuse.
- read_buffers_[slot].reset(read_buffer);
- // It's import to reset the read buffer client now. It should stay invalid
- // until next AcquireBuffer on the same slot.
- read_buffers_[slot]->read_buffer = nullptr;
- return 0;
-}
-
-void DvrReadBufferQueue::SetBufferAvailableCallback(
- DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
- if (callback == nullptr) {
- consumer_queue_->SetBufferAvailableCallback(nullptr);
- } else {
- consumer_queue_->SetBufferAvailableCallback(
- [callback, context]() { callback(context); });
- }
-}
-
-void DvrReadBufferQueue::SetBufferRemovedCallback(
- DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
- if (callback == nullptr) {
- consumer_queue_->SetBufferRemovedCallback(nullptr);
- } else {
- consumer_queue_->SetBufferRemovedCallback(
- [callback, context](const std::shared_ptr<BufferHubBase>& buffer) {
- // When buffer is removed from the queue, the slot is already invalid.
- auto read_buffer = std::make_unique<DvrReadBuffer>();
- read_buffer->read_buffer =
- std::static_pointer_cast<ConsumerBuffer>(buffer);
- callback(read_buffer.release(), context);
- });
- }
-}
-
-int DvrReadBufferQueue::HandleEvents() {
- // TODO(jwcai) Probably should change HandleQueueEvents to return Status.
- consumer_queue_->HandleQueueEvents();
- return 0;
-}
-
-void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
- delete read_queue;
-}
-
-ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->capacity();
-}
-
-int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->id();
-}
-
-int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->event_fd();
-}
-
-int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
- DvrReadBufferQueue** out_read_queue) {
- if (!read_queue || !out_read_queue)
- return -EINVAL;
-
- return read_queue->CreateReadQueue(out_read_queue);
-}
-
-int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
- DvrReadBuffer* read_buffer, int* out_fence_fd,
- void* out_meta, size_t meta_size_bytes) {
- if (!read_queue || !read_buffer || !out_fence_fd)
- return -EINVAL;
-
- if (meta_size_bytes != 0 && !out_meta)
- return -EINVAL;
-
- return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
- meta_size_bytes);
-}
-
-int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
- DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd)
- return -EINVAL;
-
- return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta,
- out_fence_fd);
-}
-
-int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
- DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta,
- int release_fence_fd) {
- if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta)
- return -EINVAL;
-
- return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd);
-}
-
-int dvrReadBufferQueueSetBufferAvailableCallback(
- DvrReadBufferQueue* read_queue,
- DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
- if (!read_queue)
- return -EINVAL;
-
- read_queue->SetBufferAvailableCallback(callback, context);
- return 0;
-}
-
-int dvrReadBufferQueueSetBufferRemovedCallback(
- DvrReadBufferQueue* read_queue,
- DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
- if (!read_queue)
- return -EINVAL;
-
- read_queue->SetBufferRemovedCallback(callback, context);
- return 0;
-}
-
-int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->HandleEvents();
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h
deleted file mode 100644
index e53a686..0000000
--- a/libs/vr/libdvr/dvr_buffer_queue_internal.h
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
-#define ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
-
-#include <gui/Surface.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <sys/cdefs.h>
-
-#include <array>
-#include <memory>
-
-#include "dvr_internal.h"
-
-struct ANativeWindow;
-
-typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
-typedef struct DvrReadBuffer DvrReadBuffer;
-typedef struct DvrReadBufferQueue DvrReadBufferQueue;
-typedef struct DvrWriteBuffer DvrWriteBuffer;
-typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
-typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer,
- void* context);
-
-struct DvrWriteBufferQueue {
- using BufferHubQueue = android::dvr::BufferHubQueue;
- using ProducerQueue = android::dvr::ProducerQueue;
-
- // Create a concrete object for DvrWriteBufferQueue.
- //
- // @param producer_queue The BufferHub's ProducerQueue that is used to back
- // this DvrWriteBufferQueue, must not be NULL.
- explicit DvrWriteBufferQueue(
- const std::shared_ptr<ProducerQueue>& producer_queue);
-
- int id() const { return producer_queue_->id(); }
- uint32_t width() const { return width_; };
- uint32_t height() const { return height_; };
- uint32_t format() const { return format_; };
- size_t capacity() const { return producer_queue_->capacity(); }
- const std::shared_ptr<ProducerQueue>& producer_queue() const {
- return producer_queue_;
- }
-
- int GetNativeWindow(ANativeWindow** out_window);
- int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
- int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd);
- int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
- int PostBuffer(DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta, int ready_fence_fd);
- int ResizeBuffer(uint32_t width, uint32_t height);
-
- private:
- std::shared_ptr<ProducerQueue> producer_queue_;
- std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity>
- write_buffers_;
-
- int64_t next_post_index_ = 0;
- uint32_t width_;
- uint32_t height_;
- uint32_t format_;
-
- android::sp<android::Surface> native_window_;
-};
-
-struct DvrReadBufferQueue {
- using BufferHubQueue = android::dvr::BufferHubQueue;
- using ConsumerQueue = android::dvr::ConsumerQueue;
-
- explicit DvrReadBufferQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
-
- int id() const { return consumer_queue_->id(); }
- int event_fd() const { return consumer_queue_->queue_fd(); }
- size_t capacity() const { return consumer_queue_->capacity(); }
-
- int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
- int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd,
- void* out_meta, size_t user_metadata_size);
- int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
- int ReleaseBuffer(DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta, int release_fence_fd);
- void SetBufferAvailableCallback(
- DvrReadBufferQueueBufferAvailableCallback callback, void* context);
- void SetBufferRemovedCallback(
- DvrReadBufferQueueBufferRemovedCallback callback, void* context);
- int HandleEvents();
-
- private:
- std::shared_ptr<ConsumerQueue> consumer_queue_;
- std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity>
- read_buffers_;
-};
-
-#endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_configuration_data.cpp b/libs/vr/libdvr/dvr_configuration_data.cpp
deleted file mode 100644
index df0d54e..0000000
--- a/libs/vr/libdvr/dvr_configuration_data.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "include/dvr/dvr_configuration_data.h"
-
-#include <private/dvr/display_client.h>
-
-using android::dvr::display::ConfigFileType;
-using android::dvr::display::DisplayClient;
-
-extern "C" {
-
-int dvrConfigurationDataGet(int config_type, uint8_t** data,
- size_t* data_size) {
- if (!data || !data_size) {
- return -EINVAL;
- }
-
- auto client = DisplayClient::Create();
- if (!client) {
- ALOGE("dvrGetGlobalBuffer: Failed to create display client!");
- return -ECOMM;
- }
-
- ConfigFileType config_file_type = static_cast<ConfigFileType>(config_type);
- auto config_data_status =
- client->GetConfigurationData(config_file_type);
-
- if (!config_data_status) {
- return -config_data_status.error();
- }
-
- *data_size = config_data_status.get().size();
- *data = new uint8_t[*data_size];
- std::copy_n(config_data_status.get().begin(), *data_size, *data);
- return 0;
-}
-
-void dvrConfigurationDataDestroy(uint8_t* data) {
- delete[] data;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
deleted file mode 100644
index 7f631e3..0000000
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-#include "include/dvr/dvr_display_manager.h"
-
-#include <dvr/dvr_buffer.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/display_manager_client.h>
-
-#include "dvr_internal.h"
-#include "dvr_buffer_queue_internal.h"
-
-using android::dvr::ConsumerBuffer;
-using android::dvr::display::DisplayManagerClient;
-using android::dvr::display::SurfaceAttribute;
-using android::dvr::display::SurfaceAttributes;
-using android::dvr::display::SurfaceState;
-using android::pdx::rpc::EmptyVariant;
-
-namespace {
-
-// Extracts type and value from the attribute Variant and writes them into the
-// respective fields of DvrSurfaceAttribute.
-struct AttributeVisitor {
- DvrSurfaceAttribute* attribute;
-
- void operator()(int32_t value) {
- attribute->value.int32_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
- }
- void operator()(int64_t value) {
- attribute->value.int64_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
- }
- void operator()(bool value) {
- attribute->value.bool_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
- }
- void operator()(float value) {
- attribute->value.float_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
- }
- void operator()(const std::array<float, 2>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float2_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
- }
- void operator()(const std::array<float, 3>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float3_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
- }
- void operator()(const std::array<float, 4>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float4_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
- }
- void operator()(const std::array<float, 8>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float8_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
- }
- void operator()(const std::array<float, 16>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float16_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
- }
- void operator()(EmptyVariant) {
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- }
-};
-
-size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes,
- DvrSurfaceAttribute* attributes,
- size_t max_count) {
- size_t count = 0;
- for (const auto& attribute : surface_attributes) {
- if (count >= max_count)
- break;
-
- // Copy the key and extract the Variant value using a visitor.
- attributes[count].key = attribute.first;
- attribute.second.Visit(AttributeVisitor{&attributes[count]});
- count++;
- }
-
- return count;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-struct DvrDisplayManager {
- std::unique_ptr<DisplayManagerClient> client;
-};
-
-struct DvrSurfaceState {
- std::vector<SurfaceState> state;
-};
-
-int dvrDisplayManagerCreate(DvrDisplayManager** client_out) {
- if (!client_out)
- return -EINVAL;
-
- auto client = DisplayManagerClient::Create();
- if (!client) {
- ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!");
- return -EIO;
- }
-
- *client_out = new DvrDisplayManager{std::move(client)};
- return 0;
-}
-
-void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; }
-
-int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) {
- if (!client)
- return -EINVAL;
-
- return client->client->event_fd();
-}
-
-int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
- int in_events, int* out_events) {
- if (!client || !out_events)
- return -EINVAL;
-
- auto status = client->client->GetEventMask(in_events);
- if (!status)
- return -status.error();
-
- *out_events = status.get();
- return 0;
-}
-
-int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
- DvrSurfaceState* state) {
- if (!client || !state)
- return -EINVAL;
-
- auto status = client->client->GetSurfaceState();
- if (!status)
- return -status.error();
-
- state->state = status.take();
- return 0;
-}
-
-int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
- int surface_id, int queue_id,
- DvrReadBufferQueue** queue_out) {
- if (!client || !queue_out)
- return -EINVAL;
-
- auto status = client->client->GetSurfaceQueue(surface_id, queue_id);
- if (!status) {
- ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *queue_out = new DvrReadBufferQueue(status.take());
- return 0;
-}
-
-int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) {
- if (!surface_state_out)
- return -EINVAL;
-
- *surface_state_out = new DvrSurfaceState{};
- return 0;
-}
-
-void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) {
- delete surface_state;
-}
-
-int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
- size_t* count_out) {
- if (!surface_state)
- return -EINVAL;
-
- *count_out = surface_state->state.size();
- return 0;
-}
-
-int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
- size_t surface_index,
- DvrSurfaceUpdateFlags* flags_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *flags_out = surface_state->state[surface_index].update_flags;
- return 0;
-}
-
-int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
- size_t surface_index, int* surface_id_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *surface_id_out = surface_state->state[surface_index].surface_id;
- return 0;
-}
-
-int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
- size_t surface_index, int* process_id_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *process_id_out = surface_state->state[surface_index].process_id;
- return 0;
-}
-
-int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
- size_t surface_index, size_t* count_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *count_out = surface_state->state[surface_index].queue_ids.size();
- return 0;
-}
-
-ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
- size_t surface_index, int* queue_ids,
- size_t max_count) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- size_t i;
- const auto& state = surface_state->state[surface_index];
- for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) {
- queue_ids[i] = state.queue_ids[i];
- }
-
- return i;
-}
-
-int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
- size_t surface_index, int* z_order_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !z_order_out) {
- return -EINVAL;
- }
-
- *z_order_out = surface_state->state[surface_index].GetZOrder();
- return 0;
-}
-
-int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
- size_t surface_index, bool* visible_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !visible_out) {
- return -EINVAL;
- }
-
- *visible_out = surface_state->state[surface_index].GetVisible();
- return 0;
-}
-
-int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
- size_t surface_index, size_t* count_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !count_out) {
- return -EINVAL;
- }
-
- *count_out = surface_state->state[surface_index].surface_attributes.size();
- return 0;
-}
-
-ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
- size_t surface_index,
- DvrSurfaceAttribute* attributes,
- size_t max_count) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !attributes) {
- return -EINVAL;
- }
-
- return ConvertSurfaceAttributes(
- surface_state->state[surface_index].surface_attributes, attributes,
- max_count);
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
deleted file mode 100644
index 4e87cf6..0000000
--- a/libs/vr/libdvr/dvr_hardware_composer_client.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-#include "include/dvr/dvr_hardware_composer_client.h"
-
-#include <android/dvr/IVrComposer.h>
-#include <android/dvr/BnVrComposerCallback.h>
-#include <android/hardware_buffer.h>
-#include <binder/IServiceManager.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <functional>
-#include <memory>
-#include <mutex>
-
-struct DvrHwcFrame {
- android::dvr::ComposerView::Frame frame;
-};
-
-namespace {
-
-class HwcCallback : public android::dvr::BnVrComposerCallback {
- public:
- using CallbackFunction = std::function<int(DvrHwcFrame*)>;
-
- explicit HwcCallback(const CallbackFunction& callback);
- ~HwcCallback() override;
-
- // Reset the callback. This needs to be done early to avoid use after free
- // accesses from binder thread callbacks.
- void Shutdown();
-
- std::unique_ptr<DvrHwcFrame> DequeueFrame();
-
- private:
- // android::dvr::BnVrComposerCallback:
- android::binder::Status onNewFrame(
- const android::dvr::ParcelableComposerFrame& frame,
- android::dvr::ParcelableUniqueFd* fence) override;
-
- // Protects the |callback_| from uses from multiple threads. During shutdown
- // there may be in-flight frame update events. In those cases the callback
- // access needs to be protected otherwise binder threads may access an invalid
- // callback.
- std::mutex mutex_;
- CallbackFunction callback_;
-
- HwcCallback(const HwcCallback&) = delete;
- void operator=(const HwcCallback&) = delete;
-};
-
-HwcCallback::HwcCallback(const CallbackFunction& callback)
- : callback_(callback) {}
-
-HwcCallback::~HwcCallback() {}
-
-void HwcCallback::Shutdown() {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = nullptr;
-}
-
-android::binder::Status HwcCallback::onNewFrame(
- const android::dvr::ParcelableComposerFrame& frame,
- android::dvr::ParcelableUniqueFd* fence) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!callback_) {
- fence->set_fence(android::base::unique_fd());
- return android::binder::Status::ok();
- }
-
- std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame());
- dvr_frame->frame = frame.frame();
-
- fence->set_fence(android::base::unique_fd(callback_(dvr_frame.release())));
- return android::binder::Status::ok();
-}
-
-} // namespace
-
-struct DvrHwcClient {
- android::sp<android::dvr::IVrComposer> composer;
- android::sp<HwcCallback> callback;
-};
-
-DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) {
- std::unique_ptr<DvrHwcClient> client(new DvrHwcClient());
-
- android::sp<android::IServiceManager> sm(android::defaultServiceManager());
- client->composer = android::interface_cast<android::dvr::IVrComposer>(
- sm->getService(android::dvr::IVrComposer::SERVICE_NAME()));
- if (!client->composer.get())
- return nullptr;
-
- client->callback = new HwcCallback(std::bind(callback, data,
- std::placeholders::_1));
- android::binder::Status status = client->composer->registerObserver(
- client->callback);
- if (!status.isOk())
- return nullptr;
-
- return client.release();
-}
-
-void dvrHwcClientDestroy(DvrHwcClient* client) {
- client->composer->clearObserver();
-
- // NOTE: Deleting DvrHwcClient* isn't enough since DvrHwcClient::callback is a
- // shared pointer that could be referenced from a binder thread. But the
- // client callback isn't valid past this calls so that needs to be reset.
- client->callback->Shutdown();
-
- delete client;
-}
-
-void dvrHwcFrameDestroy(DvrHwcFrame* frame) {
- delete frame;
-}
-
-DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) {
- return frame->frame.display_id;
-}
-
-int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) {
- return frame->frame.display_width;
-}
-
-int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) {
- return frame->frame.display_height;
-}
-
-bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) {
- return frame->frame.removed;
-}
-
-size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) {
- return frame->frame.layers.size();
-}
-
-uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.active_config);
-}
-
-uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.color_mode);
-}
-
-void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
- int32_t* out_hint) {
- *out_hint = frame->frame.color_transform_hint;
- memcpy(out_matrix, frame->frame.color_transform,
- sizeof(frame->frame.color_transform));
-}
-
-uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.power_mode);
-}
-
-uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.vsync_enabled);
-}
-
-DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].id;
-}
-
-AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
- size_t layer_index) {
- AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer(
- frame->frame.layers[layer_index].buffer.get());
- AHardwareBuffer_acquire(buffer);
- return buffer;
-}
-
-int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].fence->dup();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
- size_t layer_index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].display_frame.left,
- frame->frame.layers[layer_index].display_frame.top,
- frame->frame.layers[layer_index].display_frame.right,
- frame->frame.layers[layer_index].display_frame.bottom,
- };
-}
-
-DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) {
- return DvrHwcRectf{
- frame->frame.layers[layer_index].crop.left,
- frame->frame.layers[layer_index].crop.top,
- frame->frame.layers[layer_index].crop.right,
- frame->frame.layers[layer_index].crop.bottom,
- };
-}
-
-DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
- size_t layer_index) {
- return static_cast<DvrHwcBlendMode>(
- frame->frame.layers[layer_index].blend_mode);
-}
-
-float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].alpha;
-}
-
-uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].type;
-}
-
-uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].app_id;
-}
-
-uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].z_order;
-}
-
-void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
- int32_t* out_x, int32_t* out_y) {
- *out_x = frame->frame.layers[layer_index].cursor_x;
- *out_y = frame->frame.layers[layer_index].cursor_y;
-}
-
-uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].transform;
-}
-
-uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].dataspace;
-}
-
-uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) {
- const auto& color = frame->frame.layers[layer_index].color;
- return color.r | (static_cast<uint32_t>(color.g) << 8) |
- (static_cast<uint32_t>(color.b) << 16) |
- (static_cast<uint32_t>(color.a) << 24);
-}
-
-uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].visible_regions.size();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
- size_t layer_index, size_t index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].visible_regions[index].left,
- frame->frame.layers[layer_index].visible_regions[index].top,
- frame->frame.layers[layer_index].visible_regions[index].right,
- frame->frame.layers[layer_index].visible_regions[index].bottom,
- };
-}
-
-uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].damaged_regions.size();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
- size_t layer_index, size_t index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].damaged_regions[index].left,
- frame->frame.layers[layer_index].damaged_regions[index].top,
- frame->frame.layers[layer_index].damaged_regions[index].right,
- frame->frame.layers[layer_index].damaged_regions[index].bottom,
- };
-}
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
deleted file mode 100644
index f845cd8..0000000
--- a/libs/vr/libdvr/dvr_internal.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef ANDROID_DVR_INTERNAL_H_
-#define ANDROID_DVR_INTERNAL_H_
-
-#include <sys/cdefs.h>
-
-#include <memory>
-
-extern "C" {
-
-typedef struct DvrBuffer DvrBuffer;
-typedef struct DvrReadBuffer DvrReadBuffer;
-typedef struct DvrWriteBuffer DvrWriteBuffer;
-
-} // extern "C"
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-
-DvrBuffer* CreateDvrBufferFromIonBuffer(
- const std::shared_ptr<IonBuffer>& ion_buffer);
-
-} // namespace dvr
-} // namespace android
-
-extern "C" {
-
-struct DvrWriteBuffer {
- // The slot nubmer of the buffer, a valid slot number must be in the range of
- // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
- // DvrWriteBuffer acquired from a DvrWriteBufferQueue.
- int32_t slot = -1;
-
- std::shared_ptr<android::dvr::ProducerBuffer> write_buffer;
-};
-
-struct DvrReadBuffer {
- // The slot nubmer of the buffer, a valid slot number must be in the range of
- // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
- // DvrReadBuffer acquired from a DvrReadBufferQueue.
- int32_t slot = -1;
-
- std::shared_ptr<android::dvr::ConsumerBuffer> read_buffer;
-};
-
-struct DvrBuffer {
- std::shared_ptr<android::dvr::IonBuffer> buffer;
-};
-
-} // extern "C"
-
-#endif // ANDROID_DVR_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_performance.cpp b/libs/vr/libdvr/dvr_performance.cpp
deleted file mode 100644
index 599101f..0000000
--- a/libs/vr/libdvr/dvr_performance.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "include/dvr/dvr_performance.h"
-
-#include <private/dvr/performance_client.h>
-
-using android::dvr::PerformanceClient;
-
-extern "C" {
-
-int dvrPerformanceSetSchedulerPolicy(pid_t task_id,
- const char* scheduler_policy) {
- int error;
- if (auto client = PerformanceClient::Create(&error))
- return client->SetSchedulerPolicy(task_id, scheduler_policy);
- else
- return error;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp
deleted file mode 100644
index c379ef5..0000000
--- a/libs/vr/libdvr/dvr_pose.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "include/dvr/dvr_pose.h"
-
-#include <memory>
-
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/pose_client_internal.h>
-
-#include "dvr_buffer_queue_internal.h"
-
-using android::dvr::ConsumerQueue;
-
-int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
- DvrReadBufferQueue** queue_out) {
- if (!client || !queue_out)
- return -EINVAL;
-
- ConsumerQueue* consumer_queue;
- int status = android::dvr::dvrPoseClientGetDataReaderHandle(client,
- data_type,
- &consumer_queue);
- if (status != 0) {
- ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status);
- return status;
- }
-
- std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue};
- *queue_out = new DvrReadBufferQueue(consumer_queue_ptr);
- return 0;
-}
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
deleted file mode 100644
index 0c7ec01..0000000
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-#include "include/dvr/dvr_surface.h"
-
-#include <inttypes.h>
-
-#include <pdx/rpc/variant.h>
-#include <private/android/AHardwareBufferHelpers.h>
-#include <private/dvr/display_client.h>
-
-#include "dvr_buffer_queue_internal.h"
-#include "dvr_internal.h"
-
-using android::AHardwareBuffer_convertToGrallocUsageBits;
-using android::dvr::display::DisplayClient;
-using android::dvr::display::Surface;
-using android::dvr::display::SurfaceAttributes;
-using android::dvr::display::SurfaceAttributeValue;
-using android::pdx::rpc::EmptyVariant;
-
-namespace {
-
-// Sets the Variant |destination| to the target std::array type and copies the C
-// array into it. Unsupported std::array configurations will fail to compile.
-template <typename T, std::size_t N>
-void ArrayCopy(SurfaceAttributeValue* destination, const T (&source)[N]) {
- using ArrayType = std::array<T, N>;
- *destination = ArrayType{};
- std::copy(std::begin(source), std::end(source),
- std::get<ArrayType>(*destination).begin());
-}
-
-bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes,
- size_t attribute_count,
- SurfaceAttributes* surface_attributes,
- size_t* error_index) {
- for (size_t i = 0; i < attribute_count; i++) {
- SurfaceAttributeValue value;
- switch (attributes[i].value.type) {
- case DVR_SURFACE_ATTRIBUTE_TYPE_INT32:
- value = attributes[i].value.int32_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_INT64:
- value = attributes[i].value.int64_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL:
- // bool_value is defined in an extern "C" block, which makes it look
- // like an int to C++. Use a cast to assign the correct type to the
- // Variant type SurfaceAttributeValue.
- value = static_cast<bool>(attributes[i].value.bool_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT:
- value = attributes[i].value.float_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2:
- ArrayCopy(&value, attributes[i].value.float2_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3:
- ArrayCopy(&value, attributes[i].value.float3_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4:
- ArrayCopy(&value, attributes[i].value.float4_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8:
- ArrayCopy(&value, attributes[i].value.float8_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16:
- ArrayCopy(&value, attributes[i].value.float16_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_NONE:
- value = EmptyVariant{};
- break;
- default:
- *error_index = i;
- return false;
- }
-
- surface_attributes->emplace(attributes[i].key, value);
- }
-
- return true;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-struct DvrSurface {
- std::unique_ptr<Surface> surface;
-};
-
-int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
- size_t attribute_count, DvrSurface** out_surface) {
- if (out_surface == nullptr) {
- ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface);
- return -EINVAL;
- }
-
- size_t error_index;
- SurfaceAttributes surface_attributes;
- if (!ConvertSurfaceAttributes(attributes, attribute_count,
- &surface_attributes, &error_index)) {
- ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64,
- attributes[error_index].value.type);
- return -EINVAL;
- }
-
- auto status = Surface::CreateSurface(surface_attributes);
- if (!status) {
- ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *out_surface = new DvrSurface{status.take()};
- return 0;
-}
-
-void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; }
-
-int dvrSurfaceGetId(DvrSurface* surface) {
- return surface->surface->surface_id();
-}
-
-int dvrSurfaceSetAttributes(DvrSurface* surface,
- const DvrSurfaceAttribute* attributes,
- size_t attribute_count) {
- if (surface == nullptr || attributes == nullptr) {
- ALOGE(
- "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p "
- "attribute_count=%zu",
- surface, attributes, attribute_count);
- return -EINVAL;
- }
-
- size_t error_index;
- SurfaceAttributes surface_attributes;
- if (!ConvertSurfaceAttributes(attributes, attribute_count,
- &surface_attributes, &error_index)) {
- ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64,
- attributes[error_index].value.type);
- return -EINVAL;
- }
-
- auto status = surface->surface->SetAttributes(surface_attributes);
- if (!status) {
- ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- return 0;
-}
-
-int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
- uint32_t height, uint32_t format,
- uint32_t layer_count, uint64_t usage,
- size_t capacity, size_t metadata_size,
- DvrWriteBufferQueue** out_writer) {
- if (surface == nullptr || out_writer == nullptr) {
- ALOGE(
- "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, "
- "out_writer=%p.",
- surface, out_writer);
- return -EINVAL;
- }
-
- auto status = surface->surface->CreateQueue(
- width, height, layer_count, format, usage, capacity, metadata_size);
- if (!status) {
- ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *out_writer = new DvrWriteBufferQueue(status.take());
- return 0;
-}
-
-int dvrSetupGlobalBuffer(DvrGlobalBufferKey key, size_t size, uint64_t usage,
- DvrBuffer** buffer_out) {
- if (!buffer_out)
- return -EINVAL;
-
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrSetupGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
-
- auto buffer_status = client->SetupGlobalBuffer(key, size, gralloc_usage);
- if (!buffer_status) {
- ALOGE("dvrSetupGlobalBuffer: Failed to setup global buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take());
- return 0;
-}
-
-int dvrDeleteGlobalBuffer(DvrGlobalBufferKey key) {
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrDeleteGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- auto buffer_status = client->DeleteGlobalBuffer(key);
- if (!buffer_status) {
- ALOGE("dvrDeleteGlobalBuffer: Failed to delete named buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- return 0;
-}
-
-int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer) {
- if (!out_buffer)
- return -EINVAL;
-
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrGetGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- auto status = client->GetGlobalBuffer(key);
- if (!status) {
- return -status.error();
- }
- *out_buffer = CreateDvrBufferFromIonBuffer(status.take());
- return 0;
-}
-
-int dvrGetNativeDisplayMetrics(size_t sizeof_metrics,
- DvrNativeDisplayMetrics* metrics) {
- ALOGE_IF(sizeof_metrics != sizeof(DvrNativeDisplayMetrics),
- "dvrGetNativeDisplayMetrics: metrics struct mismatch, your dvr api "
- "header is out of date.");
-
- auto client = DisplayClient::Create();
- if (!client) {
- ALOGE("dvrGetNativeDisplayMetrics: Failed to create display client!");
- return -ECOMM;
- }
-
- if (metrics == nullptr) {
- ALOGE("dvrGetNativeDisplayMetrics: output metrics buffer must be non-null");
- return -EINVAL;
- }
-
- auto status = client->GetDisplayMetrics();
-
- if (!status) {
- return -status.error();
- }
-
- if (sizeof_metrics >= 20) {
- metrics->display_width = status.get().display_width;
- metrics->display_height = status.get().display_height;
- metrics->display_x_dpi = status.get().display_x_dpi;
- metrics->display_y_dpi = status.get().display_y_dpi;
- metrics->vsync_period_ns = status.get().vsync_period_ns;
- }
-
- return 0;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp
deleted file mode 100644
index 73addc9..0000000
--- a/libs/vr/libdvr/dvr_tracking.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/dvr/dvr_tracking.h"
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#if !DVR_TRACKING_IMPLEMENTED
-
-extern "C" {
-
-// This file provides the stub implementation of dvrTrackingXXX APIs. On
-// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the
-// build file.
-int dvrTrackingCameraCreate(DvrTrackingCamera**) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingCameraDestroy(DvrTrackingCamera*) {
- ALOGE("dvrTrackingCameraDestroy is not implemented.");
-}
-
-int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingCameraStop(DvrTrackingCamera*) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) {
- ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented.");
-}
-
-int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*,
- DvrTrackingFeatureCallback, void*) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*,
- DvrReadBuffer*,
- const DvrTrackingBufferMetadata*,
- bool*) {
- ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) {
- ALOGE("dvrTrackingSensorsCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingSensorsDestroy(DvrTrackingSensors*) {
- ALOGE("dvrTrackingSensorsDestroy is not implemented.");
-}
-
-int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback,
- void*) {
- ALOGE("dvrTrackingStart is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingSensorsStop(DvrTrackingSensors*) {
- ALOGE("dvrTrackingStop is not implemented.");
- return -ENOSYS;
-}
-
-} // extern "C"
-
-#endif // DVR_TRACKING_IMPLEMENTED
diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds
deleted file mode 100644
index 5ecb498..0000000
--- a/libs/vr/libdvr/exported_apis.lds
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- global:
- # Whitelist the function to load the dvr api.
- dvrGetApi;
-
- local:
- # Hide everything else.
- *;
-};
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
deleted file mode 100644
index fe7feb8..0000000
--- a/libs/vr/libdvr/tests/Android.bp
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_test {
- srcs: [
- "dvr_display_manager-test.cpp",
- "dvr_named_buffer-test.cpp",
- "dvr_tracking-test.cpp",
- ],
-
- header_libs: ["libdvr_headers"],
- static_libs: [
- "libdvr_static.google",
- "libchrome",
- "libdvrcommon",
- "libdisplay",
- "libbroadcastring",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
- ],
- cflags: [
- "-DDVR_TRACKING_IMPLEMENTED=0",
- "-DLOG_TAG=\"dvr_api-test\"",
- "-DTRACE=0",
- "-Wno-missing-field-initializers",
- "-O0",
- "-g",
- ],
- name: "dvr_api-test",
-}
-
-cc_test {
- name: "dvr_buffer_queue-test",
-
- // Includes the dvr_api.h header. Tests should only include "dvr_api.h",
- // and shall only get access to |dvrGetApi|, as other symbols are hidden
- // from the library.
- include_dirs: ["frameworks/native/libs/vr/libdvr/include"],
-
- srcs: ["dvr_buffer_queue-test.cpp"],
-
- shared_libs: [
- "libandroid",
- "liblog",
- ],
-
- cflags: [
- "-DTRACE=0",
- "-O2",
- "-g",
- ],
-
- // DTS Should only link to NDK libraries.
- sdk_version: "26",
- stl: "c++_static",
-}
-
-cc_test {
- name: "dvr_display-test",
-
- include_dirs: [
- "frameworks/native/libs/vr/libdvr/include",
- "frameworks/native/libs/nativewindow/include",
- ],
-
- srcs: ["dvr_display-test.cpp"],
-
- shared_libs: [
- "libandroid",
- "liblog",
- ],
-
- cflags: [
- "-DTRACE=0",
- "-O2",
- "-g",
- ],
-
- // DTS Should only link to NDK libraries.
- sdk_version: "26",
- stl: "c++_static",
-}
diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h
deleted file mode 100644
index 5d2ec28..0000000
--- a/libs/vr/libdvr/tests/dvr_api_test.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <dlfcn.h>
-#include <dvr/dvr_api.h>
-
-#include <gtest/gtest.h>
-
-/** DvrTestBase loads the libdvr.so at runtime and get the Dvr API version 1. */
-class DvrApiTest : public ::testing::Test {
- protected:
- void SetUp() override {
- int flags = RTLD_NOW | RTLD_LOCAL;
-
- // Here we need to ensure that libdvr is loaded with RTLD_NODELETE flag set
- // (so that calls to `dlclose` don't actually unload the library). This is a
- // workaround for an Android NDK bug. See more detail:
- // https://github.com/android-ndk/ndk/issues/360
- flags |= RTLD_NODELETE;
- platform_handle_ = dlopen("libdvr.google.so", flags);
- ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing.";
-
- auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>(
- dlsym(platform_handle_, "dvrGetApi"));
- ASSERT_NE(nullptr, dvr_get_api) << "Platform library missing dvrGetApi.";
-
- ASSERT_EQ(dvr_get_api(&api_, sizeof(api_), /*version=*/1), 0)
- << "Unable to find compatible Dvr API.";
- }
-
- void TearDown() override {
- if (platform_handle_ != nullptr) {
- dlclose(platform_handle_);
- }
- }
-
- void* platform_handle_ = nullptr;
- DvrApi_v1 api_;
-};
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
deleted file mode 100644
index df06097..0000000
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ /dev/null
@@ -1,579 +0,0 @@
-#include <android/log.h>
-#include <android/native_window.h>
-#include <dvr/dvr_api.h>
-#include <dvr/dvr_buffer_queue.h>
-
-#include <gtest/gtest.h>
-
-#include <array>
-#include <unordered_map>
-
-#include "dvr_api_test.h"
-
-#define LOG_TAG "dvr_buffer_queue-test"
-
-#ifndef ALOGD
-#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#endif
-
-#ifndef ALOGD_IF
-#define ALOGD_IF(cond, ...) \
- ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0)
-#endif
-
-namespace {
-
-static constexpr uint32_t kBufferWidth = 100;
-static constexpr uint32_t kBufferHeight = 1;
-static constexpr uint32_t kLayerCount = 1;
-static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB;
-static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
-static constexpr size_t kQueueCapacity = 3;
-
-class DvrBufferQueueTest : public DvrApiTest {
- public:
- static void BufferAvailableCallback(void* context) {
- DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context);
- thiz->HandleBufferAvailable();
- }
-
- static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) {
- DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context);
- thiz->HandleBufferRemoved(buffer);
- }
-
- protected:
- void TearDown() override {
- if (write_queue_ != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
- }
- DvrApiTest::TearDown();
- }
-
- void HandleBufferAvailable() {
- buffer_available_count_ += 1;
- ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_);
- }
-
- void HandleBufferRemoved(DvrReadBuffer* buffer) {
- buffer_removed_count_ += 1;
- ALOGD_IF(TRACE, "Buffer removed, buffer=%p, count=%d", buffer,
- buffer_removed_count_);
- }
-
- DvrWriteBufferQueue* write_queue_ = nullptr;
- int buffer_available_count_{0};
- int buffer_removed_count_{0};
-};
-
-TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
-}
-
-TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- size_t capacity = api_.WriteBufferQueueGetCapacity(write_queue_);
-
- ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-}
-
-TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue1 = nullptr;
- DvrReadBufferQueue* read_queue2 = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue1);
-
- ret = api_.ReadBufferQueueCreateReadQueue(read_queue1, &read_queue2);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue2);
- ASSERT_NE(read_queue1, read_queue2);
-
- api_.ReadBufferQueueDestroy(read_queue1);
- api_.ReadBufferQueueDestroy(read_queue2);
-}
-
-TEST_F(DvrBufferQueueTest, GainBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrWriteBuffer* wb = nullptr;
- EXPECT_FALSE(api_.WriteBufferIsValid(wb));
-
- DvrNativeBufferMetadata meta;
- int fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta,
- &fence_fd);
- ASSERT_EQ(ret, 0);
- EXPECT_EQ(fence_fd, -1);
- EXPECT_NE(wb, nullptr);
- EXPECT_TRUE(api_.WriteBufferIsValid(wb));
-}
-
-TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrReadBufferQueue* read_queue = nullptr;
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- DvrNativeBufferMetadata meta1;
- DvrNativeBufferMetadata meta2;
- int fence_fd = -1;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(ret, 0);
- ASSERT_NE(read_queue, nullptr);
-
- api_.ReadBufferQueueSetBufferAvailableCallback(
- read_queue, &BufferAvailableCallback, this);
-
- // Gain buffer for writing.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb,
- &meta1, &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_NE(wb, nullptr);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb));
- ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
- wb, fence_fd);
- close(fence_fd);
-
- // Post buffer to the read_queue.
- meta1.timestamp = 42;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- ASSERT_FALSE(api_.WriteBufferIsValid(wb));
- wb = nullptr;
-
- // Acquire buffer for reading.
- ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb,
- &meta2, &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_NE(rb, nullptr);
-
- // Dequeue is successfully, BufferAvailableCallback should be fired once.
- ASSERT_EQ(buffer_available_count_, 1);
- ASSERT_TRUE(api_.ReadBufferIsValid(rb));
-
- // Metadata should be passed along from producer to consumer properly.
- ASSERT_EQ(meta1.timestamp, meta2.timestamp);
-
- ALOGD_IF(TRACE,
- "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
- fence_fd);
- close(fence_fd);
-
- // Release buffer to the write_queue.
- ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- ASSERT_FALSE(api_.ReadBufferIsValid(rb));
- rb = nullptr;
-
- // TODO(b/34387835) Currently buffer allocation has to happen after all queues
- // are initialized.
- size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue);
-
- ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, GetANativeWindow) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, /*user_metadata_size=*/0, &write_queue_);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, write_queue_);
-
- ANativeWindow* window = nullptr;
- ret = api_.WriteBufferQueueGetANativeWindow(write_queue_, &window);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, window);
-
- uint32_t width = ANativeWindow_getWidth(window);
- uint32_t height = ANativeWindow_getHeight(window);
- uint32_t format = ANativeWindow_getFormat(window);
- ASSERT_EQ(kBufferWidth, width);
- ASSERT_EQ(kBufferHeight, height);
- ASSERT_EQ(kBufferFormat, format);
-}
-
-// Create buffer queue of three buffers and dequeue three buffers out of it.
-// Before each dequeue operation, we resize the buffer queue and expect the
-// queue always return buffer with desired dimension.
-TEST_F(DvrBufferQueueTest, ResizeBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- int fence_fd = -1;
-
- DvrNativeBufferMetadata meta;
- DvrReadBufferQueue* read_queue = nullptr;
- DvrWriteBuffer* wb1 = nullptr;
- DvrWriteBuffer* wb2 = nullptr;
- DvrWriteBuffer* wb3 = nullptr;
- AHardwareBuffer* ahb1 = nullptr;
- AHardwareBuffer* ahb2 = nullptr;
- AHardwareBuffer* ahb3 = nullptr;
- AHardwareBuffer_Desc buffer_desc;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- api_.ReadBufferQueueSetBufferRemovedCallback(read_queue,
- &BufferRemovedCallback, this);
-
- // Handle all pending events on the read queue.
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
-
- size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue);
- ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-
- // Resize before dequeuing.
- constexpr uint32_t w1 = 10;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // Gain first buffer for writing. All buffers will be resized.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb1));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1);
- close(fence_fd);
-
- // Check the buffer dimension.
- ret = api_.WriteBufferGetAHardwareBuffer(wb1, &ahb1);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb1, &buffer_desc);
- ASSERT_EQ(w1, buffer_desc.width);
- ASSERT_EQ(kBufferHeight, buffer_desc.height);
- AHardwareBuffer_release(ahb1);
-
- // For the first resize, all buffers are reallocated.
- int expected_buffer_removed_count = kQueueCapacity;
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- // Resize the queue. We are testing with blob format, keep height to be 1.
- constexpr uint32_t w2 = 20;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // The next buffer we dequeued should have new width.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb2));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2,
- fence_fd);
- close(fence_fd);
-
- // Check the buffer dimension, should be new width
- ret = api_.WriteBufferGetAHardwareBuffer(wb2, &ahb2);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb2, &buffer_desc);
- ASSERT_EQ(w2, buffer_desc.width);
- AHardwareBuffer_release(ahb2);
-
- // For the second resize, all but one buffers are reallocated.
- expected_buffer_removed_count += (kQueueCapacity - 1);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- // Resize the queue for the third time.
- constexpr uint32_t w3 = 30;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // The next buffer we dequeued should have new width.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb3));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3,
- fence_fd);
- close(fence_fd);
-
- // Check the buffer dimension, should be new width
- ret = api_.WriteBufferGetAHardwareBuffer(wb3, &ahb3);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb3, &buffer_desc);
- ASSERT_EQ(w3, buffer_desc.width);
- AHardwareBuffer_release(ahb3);
-
- // For the third resize, all but two buffers are reallocated.
- expected_buffer_removed_count += (kQueueCapacity - 2);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, ReadQueueEventFd) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- int event_fd = api_.ReadBufferQueueGetEventFd(read_queue);
- ASSERT_GT(event_fd, 0);
-}
-
-// Verifies a Dvr{Read,Write}BufferQueue contains the same set of
-// Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id,
-// the corresponding AHardwareBuffer handle stays the same.
-TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- int fence_fd = -1;
- DvrReadBufferQueue* read_queue = nullptr;
- EXPECT_EQ(0, api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
- // Read buffers.
- std::array<DvrReadBuffer*, kQueueCapacity> rbs;
- // Write buffers.
- std::array<DvrWriteBuffer*, kQueueCapacity> wbs;
- // Buffer metadata.
- std::array<DvrNativeBufferMetadata, kQueueCapacity> metas;
- // Hardware buffers for Read buffers.
- std::unordered_map<int, AHardwareBuffer*> rhbs;
- // Hardware buffers for Write buffers.
- std::unordered_map<int, AHardwareBuffer*> whbs;
-
- constexpr int kNumTests = 100;
-
- // This test runs the following operations many many times. Thus we prefer to
- // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output.
- std::function<void(size_t i)> Gain = [&](size_t i) {
- int ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10,
- &wbs[i], &metas[i], &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_LT(fence_fd, 0); // expect invalid fence.
- ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i]));
- int buffer_id = api_.WriteBufferGetId(wbs[i]);
- ASSERT_GT(buffer_id, 0);
-
- AHardwareBuffer* hb = nullptr;
- ASSERT_EQ(0, api_.WriteBufferGetAHardwareBuffer(wbs[i], &hb));
-
- auto whb_it = whbs.find(buffer_id);
- if (whb_it == whbs.end()) {
- // If this is a new buffer id, check that total number of unique
- // hardware buffers won't exceed queue capacity.
- ASSERT_LT(whbs.size(), kQueueCapacity);
- whbs.emplace(buffer_id, hb);
- } else {
- // If this is a buffer id we have seen before, check that the
- // buffer_id maps to the same AHardwareBuffer handle.
- ASSERT_EQ(hb, whb_it->second);
- }
- };
-
- std::function<void(size_t i)> Post = [&](size_t i) {
- ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i]));
-
- metas[i].timestamp++;
- int ret = api_.WriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i],
- /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- };
-
- std::function<void(size_t i)> Acquire = [&](size_t i) {
- int ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10,
- &rbs[i], &metas[i], &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_LT(fence_fd, 0); // expect invalid fence.
- ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i]));
-
- int buffer_id = api_.ReadBufferGetId(rbs[i]);
- ASSERT_GT(buffer_id, 0);
-
- AHardwareBuffer* hb = nullptr;
- ASSERT_EQ(0, api_.ReadBufferGetAHardwareBuffer(rbs[i], &hb));
-
- auto rhb_it = rhbs.find(buffer_id);
- if (rhb_it == rhbs.end()) {
- // If this is a new buffer id, check that total number of unique hardware
- // buffers won't exceed queue capacity.
- ASSERT_LT(rhbs.size(), kQueueCapacity);
- rhbs.emplace(buffer_id, hb);
- } else {
- // If this is a buffer id we have seen before, check that the buffer_id
- // maps to the same AHardwareBuffer handle.
- ASSERT_EQ(hb, rhb_it->second);
- }
- };
-
- std::function<void(size_t i)> Release = [&](size_t i) {
- ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i]));
-
- int ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i],
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- };
-
- // Scenario one:
- for (int i = 0; i < kNumTests; i++) {
- // Gain all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- }
- // Post all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Post(i));
- }
- // Acquire all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- }
- // Release all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Release(i));
- }
- }
-
- // Scenario two:
- for (int i = 0; i < kNumTests; i++) {
- // Gain and post all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- ASSERT_NO_FATAL_FAILURE(Post(i));
- }
- // Acquire and release all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- ASSERT_NO_FATAL_FAILURE(Release(i));
- }
- }
-
- // Scenario three:
- for (int i = 0; i < kNumTests; i++) {
- // Gain all write buffers then post them in reversed order.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- }
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i));
- }
-
- // Acquire all write buffers then release them in reversed order.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- }
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i));
- }
- }
-}
-
-TEST_F(DvrBufferQueueTest, ConsumerReleaseAfterProducerDestroy) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrReadBufferQueue* read_queue = nullptr;
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- DvrNativeBufferMetadata meta1;
- DvrNativeBufferMetadata meta2;
- int fence_fd = -1;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
- ASSERT_EQ(ret, 0);
-
- api_.ReadBufferQueueSetBufferAvailableCallback(
- read_queue, &BufferAvailableCallback, this);
-
- // Gain buffer for writing.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb,
- &meta1, &fence_fd);
- ASSERT_EQ(ret, 0);
- close(fence_fd);
-
- // Post buffer to the read_queue.
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- wb = nullptr;
-
- // Acquire buffer for reading.
- ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb,
- &meta2, &fence_fd);
- ASSERT_EQ(ret, 0);
- close(fence_fd);
-
- // Destroy the write buffer queue and make sure the reader queue is picking
- // these events up.
- api_.WriteBufferQueueDestroy(write_queue_);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
-
- // Release buffer to the write_queue.
- ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- rb = nullptr;
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-} // namespace
diff --git a/libs/vr/libdvr/tests/dvr_display-test.cpp b/libs/vr/libdvr/tests/dvr_display-test.cpp
deleted file mode 100644
index c72f940..0000000
--- a/libs/vr/libdvr/tests/dvr_display-test.cpp
+++ /dev/null
@@ -1,351 +0,0 @@
-#include <android/hardware_buffer.h>
-#include <android/log.h>
-#include <dvr/dvr_api.h>
-#include <dvr/dvr_display_types.h>
-#include <dvr/dvr_surface.h>
-
-#include <gtest/gtest.h>
-
-#include "dvr_api_test.h"
-
-#define LOG_TAG "dvr_display-test"
-
-#ifndef ALOGD
-#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#endif
-
-class DvrDisplayTest : public DvrApiTest {
- protected:
- void SetUp() override {
- DvrApiTest::SetUp();
- int ret = api_.GetNativeDisplayMetrics(sizeof(display_metrics_),
- &display_metrics_);
- ASSERT_EQ(ret, 0) << "Failed to get display metrics.";
- ALOGD(
- "display_width: %d, display_height: %d, display_x_dpi: %d, "
- "display_y_dpi: %d, vsync_period_ns: %d.",
- display_metrics_.display_width, display_metrics_.display_height,
- display_metrics_.display_x_dpi, display_metrics_.display_y_dpi,
- display_metrics_.vsync_period_ns);
- }
-
- void TearDown() override {
- if (write_queue_ != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
- }
- if (direct_surface_ != nullptr) {
- api_.SurfaceDestroy(direct_surface_);
- direct_surface_ = nullptr;
- }
- DvrApiTest::TearDown();
- }
-
- /* Convert a write buffer to an android hardware buffer and fill in
- * color_textures evenly to the buffer.
- * AssertionError if the width of the buffer is not equal to the input width,
- * AssertionError if the height of the buffer is not equal to the input
- * height.
- */
- void FillWriteBuffer(DvrWriteBuffer* write_buffer,
- const std::vector<uint32_t>& color_textures,
- uint32_t width, uint32_t height);
-
- // Write buffer queue properties.
- static constexpr uint64_t kUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- uint32_t kFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
- static constexpr size_t kMetadataSize = 0;
- static constexpr int kTimeoutMs = 1000; // Time for getting buffer.
- uint32_t kLayerCount = 1;
- DvrWriteBufferQueue* write_queue_ = nullptr;
- DvrSurface* direct_surface_ = nullptr;
-
- // Device display properties.
- DvrNativeDisplayMetrics display_metrics_;
-};
-
-TEST_F(DvrDisplayTest, DisplayWithOneBuffer) {
- // Create a direct surface.
- std::vector<DvrSurfaceAttribute> direct_surface_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_attributes.data(),
- direct_surface_attributes.size(), &direct_surface_);
- ASSERT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue with the direct surface.
- constexpr size_t kCapacity = 1;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null.";
-
- // Get buffer from WriteBufferQueue.
- DvrWriteBuffer* write_buffer = nullptr;
- DvrNativeBufferMetadata out_meta;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, kTimeoutMs, &write_buffer,
- &out_meta, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- ASSERT_NE(nullptr, write_buffer) << "Gained buffer should not be null.";
-
- // Color the write buffer.
- FillWriteBuffer(write_buffer,
- {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
- width, height);
-
- // Post buffer.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta,
- ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- sleep(5); // For visual check on the device under test.
- // Should observe three primary colors on the screen center.
-}
-
-TEST_F(DvrDisplayTest, DisplayWithDoubleBuffering) {
- // Create a direct surface.
- std::vector<DvrSurfaceAttribute> direct_surface_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_attributes.data(),
- direct_surface_attributes.size(), &direct_surface_);
- ASSERT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue with the direct surface.
- constexpr size_t kCapacity = 2;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null.";
-
- int num_display_cycles_in_5s = 5 / (display_metrics_.vsync_period_ns / 1e9);
- ALOGD("The number of display cycles: %d", num_display_cycles_in_5s);
- int bufferhub_id_prev_write_buffer = -1;
- for (int i = 0; i < num_display_cycles_in_5s; ++i) {
- // Get a buffer from the WriteBufferQueue.
- DvrWriteBuffer* write_buffer = nullptr;
- DvrNativeBufferMetadata out_meta;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the a write buffer.";
- ASSERT_NE(nullptr, write_buffer) << "The gained buffer should not be null.";
-
- int bufferhub_id = api_.WriteBufferGetId(write_buffer);
- ALOGD("Display cycle: %d, bufferhub id of the write buffer: %d", i,
- bufferhub_id);
- EXPECT_NE(bufferhub_id_prev_write_buffer, bufferhub_id)
- << "Double buffering should be using the two buffers in turns, not "
- "reusing the same write buffer.";
- bufferhub_id_prev_write_buffer = bufferhub_id;
-
- // Color the write buffer.
- if (i % 2) {
- FillWriteBuffer(write_buffer, {0xffff0000, 0xff00ff00, 0xff0000ff}, width,
- height);
- } else {
- FillWriteBuffer(write_buffer, {0xff00ff00, 0xff0000ff, 0xffff0000}, width,
- height);
- }
-
- // Post the write buffer.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta,
- ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
- }
- // Should observe blinking screen in secondary colors
- // although it is actually displaying primary colors.
-}
-
-TEST_F(DvrDisplayTest, DisplayWithTwoHardwareLayers) {
- // Create the direct_surface_0 of z order 10 and direct_surface_1 of z
- // order 11.
- DvrSurface* direct_surface_0 = nullptr;
- std::vector<DvrSurfaceAttribute> direct_surface_0_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_0_attributes.data(),
- direct_surface_0_attributes.size(), &direct_surface_0);
- EXPECT_EQ(ret, 0) << "Failed to create direct surface.";
-
- DvrSurface* direct_surface_1 = nullptr;
- std::vector<DvrSurfaceAttribute> direct_surface_1_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 11},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- ret =
- api_.SurfaceCreate(direct_surface_1_attributes.data(),
- direct_surface_1_attributes.size(), &direct_surface_1);
- EXPECT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue for each of the direct surfaces.
- constexpr size_t kCapacity = 1;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
-
- DvrWriteBufferQueue* write_queue_0 = nullptr;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_0, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_0);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- EXPECT_NE(nullptr, write_queue_0) << "Write buffer queue should not be null.";
-
- DvrWriteBufferQueue* write_queue_1 = nullptr;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_1, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_1);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- EXPECT_NE(nullptr, write_queue_1) << "Write buffer queue should not be null.";
-
- // Get a buffer from each of the write buffer queues.
- DvrWriteBuffer* write_buffer_0 = nullptr;
- DvrNativeBufferMetadata out_meta_0;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_0, kTimeoutMs, &write_buffer_0, &out_meta_0, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- EXPECT_NE(nullptr, write_buffer_0) << "Gained buffer should not be null.";
-
- DvrWriteBuffer* write_buffer_1 = nullptr;
- DvrNativeBufferMetadata out_meta_1;
- out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_1, kTimeoutMs, &write_buffer_1, &out_meta_1, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- EXPECT_NE(nullptr, write_buffer_1) << "Gained buffer should not be null.";
-
- // Color the write buffers.
- FillWriteBuffer(write_buffer_0, {0xffff0000, 0xff00ff00, 0xff0000ff}, width,
- height);
- FillWriteBuffer(write_buffer_1, {0x7f00ff00, 0x7f0000ff, 0x7fff0000}, width,
- height);
-
- // Post buffers.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_0, write_buffer_0,
- &out_meta_0, ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_1, write_buffer_1,
- &out_meta_1, ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- sleep(5); // For visual check on the device under test.
- // Should observe three secondary colors.
-
- // Test finished. Clean up buffers and surfaces.
- if (write_queue_0 != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_0);
- write_queue_0 = nullptr;
- }
- if (write_queue_1 != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_1);
- write_queue_1 = nullptr;
- }
- if (direct_surface_0 != nullptr) {
- api_.SurfaceDestroy(direct_surface_0);
- }
- if (direct_surface_1 != nullptr) {
- api_.SurfaceDestroy(direct_surface_1);
- }
-}
-
-void DvrDisplayTest::FillWriteBuffer(
- DvrWriteBuffer* write_buffer, const std::vector<uint32_t>& color_textures,
- uint32_t width, uint32_t height) {
- uint32_t num_colors = color_textures.size();
- // Convert the first write buffer to an android hardware buffer.
- AHardwareBuffer* ah_buffer = nullptr;
- int ret = api_.WriteBufferGetAHardwareBuffer(write_buffer, &ah_buffer);
- ASSERT_EQ(0, ret) << "Failed to get a hardware buffer from the write buffer.";
- ASSERT_NE(nullptr, ah_buffer) << "AHardware buffer should not be null.";
- AHardwareBuffer_Desc ah_buffer_describe;
- AHardwareBuffer_describe(ah_buffer, &ah_buffer_describe);
- ASSERT_EQ(ah_buffer_describe.format, kFormat)
- << "The format of the android hardware buffer is wrong.";
- ASSERT_EQ(ah_buffer_describe.layers, kLayerCount)
- << "The obtained android hardware buffer should have 2 layers.";
- ASSERT_EQ(ah_buffer_describe.width, width)
- << "The obtained android hardware buffer width is wrong.";
- ASSERT_EQ(ah_buffer_describe.height, height)
- << "The obtained android hardware buffer height is wrong.";
- // Change the content of the android hardware buffer.
- void* buffer_data = nullptr;
- int32_t fence = -1;
- ret = AHardwareBuffer_lock(ah_buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
- fence, nullptr, &buffer_data);
- ASSERT_EQ(0, ret) << "Failed to lock the hardware buffer.";
- ASSERT_NE(nullptr, buffer_data) << "Buffer data should not be null.";
-
- uint32_t num_pixels = width * height / num_colors;
- for (uint32_t color_index = 0; color_index < num_colors - 1; ++color_index) {
- uint32_t color_texture = color_textures[color_index];
- for (uint32_t i = 0; i < num_pixels; ++i) {
- memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) +
- (i + num_pixels * color_index) *
- sizeof(color_texture)),
- &color_texture, sizeof(color_texture));
- }
- }
- uint32_t color_texture = color_textures[num_colors - 1];
- uint32_t num_colored_pixels = num_pixels * (num_colors - 1);
- num_pixels = width * height - num_colored_pixels;
- for (uint32_t i = 0; i < num_pixels; ++i) {
- memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) +
- (i + num_colored_pixels) *
- sizeof(color_texture)),
- &color_texture, sizeof(color_texture));
- }
- fence = -1;
- ret = AHardwareBuffer_unlock(ah_buffer, &fence);
- EXPECT_EQ(0, ret) << "Failed to unlock the hardware buffer.";
-
- // Release the android hardware buffer.
- AHardwareBuffer_release(ah_buffer);
-}
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
deleted file mode 100644
index 07e2121..0000000
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ /dev/null
@@ -1,891 +0,0 @@
-#include <android-base/properties.h>
-#include <base/logging.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-#include <poll.h>
-
-#include <android/hardware_buffer.h>
-
-#include <algorithm>
-#include <array>
-#include <set>
-#include <thread>
-#include <vector>
-
-#include <dvr/dvr_configuration_data.h>
-#include <dvr/dvr_deleter.h>
-#include <dvr/dvr_display_manager.h>
-#include <dvr/dvr_surface.h>
-
-#include <pdx/status.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-using ::testing::Test;
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
- attribute.value.int32_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
- attribute.value.int64_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
- attribute.value.bool_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
- attribute.value.float_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 2>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
- std::copy(value.begin(), value.end(), attribute.value.float2_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 3>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
- std::copy(value.begin(), value.end(), attribute.value.float3_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 4>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
- std::copy(value.begin(), value.end(), attribute.value.float4_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 8>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
- std::copy(value.begin(), value.end(), attribute.value.float8_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 16>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
- std::copy(value.begin(), value.end(), attribute.value.float16_value);
- return attribute;
-}
-
-Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false,
- int32_t z_order = 0) {
- DvrSurface* surface = nullptr;
- DvrSurfaceAttribute attributes[] = {
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
-
- const int ret = dvrSurfaceCreate(
- attributes, std::extent<decltype(attributes)>::value, &surface);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrSurface(surface)};
-}
-
-Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue(
- const UniqueDvrSurface& surface, uint32_t width, uint32_t height,
- uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity,
- size_t metadata_size) {
- DvrWriteBufferQueue* queue;
- const int ret = dvrSurfaceCreateWriteBufferQueue(
- surface.get(), width, height, format, layer_count, usage, capacity,
- metadata_size, &queue);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrWriteBufferQueue(queue)};
-}
-
-Status<std::vector<uint8_t>> GetConfigData(int config_type) {
- uint8_t* data = nullptr;
- size_t data_size = 0;
- int error = dvrConfigurationDataGet(config_type, &data, &data_size);
- if (error < 0) {
- return ErrorStatus(-error);
- }
-
- if (!data || data_size == 0) {
- return ErrorStatus(EINVAL);
- }
- std::vector<uint8_t> data_result(data, data + data_size);
- dvrConfigurationDataDestroy(data);
- std::string s(data, data + data_size);
- return {std::move(data_result)};
-}
-
-class TestDisplayManager {
- public:
- TestDisplayManager(UniqueDvrDisplayManager display_manager,
- UniqueDvrSurfaceState surface_state)
- : display_manager_(std::move(display_manager)),
- surface_state_(std::move(surface_state)) {
- const int fd = dvrDisplayManagerGetEventFd(display_manager_.get());
- LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd);
- display_manager_event_fd_ = fd;
- }
-
- Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id,
- int queue_id) {
- DvrReadBufferQueue* queue;
- const int ret = dvrDisplayManagerGetReadBufferQueue(
- display_manager_.get(), surface_id, queue_id, &queue);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrReadBufferQueue(queue)};
- }
-
- Status<void> UpdateSurfaceState() {
- const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(),
- surface_state_.get());
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {};
- }
-
- enum : int { kTimeoutMs = 10000 }; // 10s
-
- Status<void> WaitForUpdate(int timeout_ms = kTimeoutMs) {
- if (display_manager_event_fd_ < 0)
- return ErrorStatus(-display_manager_event_fd_);
-
- pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
- const int count = poll(&pfd, 1, timeout_ms);
- if (count < 0)
- return ErrorStatus(errno);
- else if (count == 0)
- return ErrorStatus(ETIMEDOUT);
-
- int events;
- const int ret = dvrDisplayManagerTranslateEpollEventMask(
- display_manager_.get(), pfd.revents, &events);
- if (ret < 0)
- return ErrorStatus(-ret);
- else if (events & POLLIN)
- return UpdateSurfaceState();
- else
- return ErrorStatus(EPROTO);
- }
-
- Status<size_t> GetSurfaceCount() {
- size_t count = 0;
- const int ret =
- dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {count};
- }
-
- Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) {
- DvrSurfaceUpdateFlags update_flags;
- const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(),
- surface_index, &update_flags);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {update_flags};
- }
-
- Status<int> GetSurfaceId(size_t surface_index) {
- int surface_id;
- const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(),
- surface_index, &surface_id);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {surface_id};
- }
-
- Status<int> GetProcessId(size_t surface_index) {
- int process_id;
- const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(),
- surface_index, &process_id);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {process_id};
- }
-
- Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) {
- std::vector<DvrSurfaceAttribute> attributes;
- size_t count = 0;
- const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(),
- surface_index, &count);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- attributes.resize(count);
- const ssize_t return_count = dvrSurfaceStateGetAttributes(
- surface_state_.get(), surface_index, attributes.data(), count);
- if (return_count < 0)
- return ErrorStatus(-return_count);
-
- attributes.resize(return_count);
- return {std::move(attributes)};
- }
-
- Status<std::vector<int>> GetQueueIds(size_t surface_index) {
- std::vector<int> queue_ids;
- size_t count = 0;
- const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(),
- surface_index, &count);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- if (count > 0) {
- queue_ids.resize(count);
- const ssize_t return_count = dvrSurfaceStateGetQueueIds(
- surface_state_.get(), surface_index, queue_ids.data(), count);
- if (return_count < 0)
- return ErrorStatus(-return_count);
-
- queue_ids.resize(return_count);
- }
-
- return {std::move(queue_ids)};
- }
-
- private:
- UniqueDvrDisplayManager display_manager_;
- UniqueDvrSurfaceState surface_state_;
-
- // Owned by object in display_manager_, do not explicitly close.
- int display_manager_event_fd_;
-
- TestDisplayManager(const TestDisplayManager&) = delete;
- void operator=(const TestDisplayManager&) = delete;
-};
-
-class DvrDisplayManagerTest : public Test {
- protected:
- void SetUp() override {
- int ret;
- DvrDisplayManager* display_manager;
- DvrSurfaceState* surface_state;
-
- ret = dvrDisplayManagerCreate(&display_manager);
- ASSERT_EQ(0, ret) << "Failed to create display manager client";
- ASSERT_NE(nullptr, display_manager);
-
- ret = dvrSurfaceStateCreate(&surface_state);
- ASSERT_EQ(0, ret) << "Failed to create surface state object";
- ASSERT_NE(nullptr, surface_state);
-
- manager_.reset(
- new TestDisplayManager(UniqueDvrDisplayManager(display_manager),
- UniqueDvrSurfaceState(surface_state)));
- }
- void TearDown() override {}
-
- std::unique_ptr<TestDisplayManager> manager_;
-};
-
-// TODO(eieio): Consider moving these somewhere more central because they are
-// broadly useful.
-
-template <typename T>
-testing::AssertionResult StatusOk(const char* status_expression,
- const Status<T>& status) {
- if (!status.ok()) {
- return testing::AssertionFailure()
- << "(" << status_expression
- << ") expected to indicate success but actually contains error ("
- << status.error() << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T>
-testing::AssertionResult StatusError(const char* status_expression,
- const Status<T>& status) {
- if (status.ok()) {
- return testing::AssertionFailure()
- << "(" << status_expression
- << ") expected to indicate error but instead indicates success.";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T>
-testing::AssertionResult StatusHasError(const char* status_expression,
- const char* /*error_code_expression*/,
- const Status<T>& status,
- int error_code) {
- if (status.ok()) {
- return StatusError(status_expression, status);
- } else if (status.error() != error_code) {
- return testing::AssertionFailure()
- << "(" << status_expression << ") expected to indicate error ("
- << error_code << ") but actually indicates error (" << status.error()
- << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T, typename U>
-testing::AssertionResult StatusHasValue(const char* status_expression,
- const char* /*value_expression*/,
- const Status<T>& status,
- const U& value) {
- if (!status.ok()) {
- return StatusOk(status_expression, status);
- } else if (status.get() != value) {
- return testing::AssertionFailure()
- << "(" << status_expression << ") expected to contain value ("
- << testing::PrintToString(value) << ") but actually contains value ("
- << testing::PrintToString(status.get()) << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T, typename Op>
-testing::AssertionResult StatusPred(const char* status_expression,
- const char* pred_expression,
- const Status<T>& status, Op pred) {
- if (!status.ok()) {
- return StatusOk(status_expression, status);
- } else if (!pred(status.get())) {
- return testing::AssertionFailure()
- << status_expression << " value ("
- << testing::PrintToString(status.get())
- << ") failed to pass predicate " << pred_expression;
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status)
-#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status)
-
-#define ASSERT_STATUS_ERROR_VALUE(value, status) \
- ASSERT_PRED_FORMAT2(StatusHasError, status, value)
-
-#define ASSERT_STATUS_EQ(value, status) \
- ASSERT_PRED_FORMAT2(StatusHasValue, status, value)
-
-#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status)
-#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status)
-
-#define EXPECT_STATUS_ERROR_VALUE(value, status) \
- EXPECT_PRED_FORMAT2(StatusHasError, status, value)
-
-#define EXPECT_STATUS_EQ(value, status) \
- EXPECT_PRED_FORMAT2(StatusHasValue, status, value)
-
-#define EXPECT_STATUS_PRED(pred, status) \
- EXPECT_PRED_FORMAT2(StatusPred, status, pred)
-
-#if 0
-// Verify utility predicate/macro functionality. This section is commented out
-// because it is designed to fail in some cases to validate the helpers.
-TEST_F(Test, ExpectVoid) {
- Status<void> status_error{ErrorStatus{EINVAL}};
- Status<void> status_ok{};
-
- EXPECT_STATUS_ERROR(status_error);
- EXPECT_STATUS_ERROR(status_ok);
- EXPECT_STATUS_OK(status_error);
- EXPECT_STATUS_OK(status_ok);
-
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
-}
-
-TEST_F(Test, ExpectInt) {
- Status<int> status_error{ErrorStatus{EINVAL}};
- Status<int> status_ok{10};
-
- EXPECT_STATUS_ERROR(status_error);
- EXPECT_STATUS_ERROR(status_ok);
- EXPECT_STATUS_OK(status_error);
- EXPECT_STATUS_OK(status_ok);
-
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
-
- EXPECT_STATUS_EQ(10, status_error);
- EXPECT_STATUS_EQ(20, status_error);
- EXPECT_STATUS_EQ(10, status_ok);
- EXPECT_STATUS_EQ(20, status_ok);
-
- auto pred1 = [](const auto& value) { return value < 15; };
- auto pred2 = [](const auto& value) { return value > 5; };
- auto pred3 = [](const auto& value) { return value > 15; };
- auto pred4 = [](const auto& value) { return value < 5; };
-
- EXPECT_STATUS_PRED(pred1, status_error);
- EXPECT_STATUS_PRED(pred2, status_error);
- EXPECT_STATUS_PRED(pred3, status_error);
- EXPECT_STATUS_PRED(pred4, status_error);
- EXPECT_STATUS_PRED(pred1, status_ok);
- EXPECT_STATUS_PRED(pred2, status_ok);
- EXPECT_STATUS_PRED(pred3, status_ok);
- EXPECT_STATUS_PRED(pred4, status_ok);
-}
-#endif
-
-TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
- // Get surface state and verify there are no surfaces.
- ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
- ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-
- // Get flags for invalid surface index.
- EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0));
-
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- const int surface_id = dvrSurfaceGetId(surface.get());
- ASSERT_GE(surface_id, 0);
-
- // Now there should be one new surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify the new surface flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- // Verify the surface id matches.
- EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0));
-
- // Verify the owning process of the surface.
- EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0));
-
- surface.reset();
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
- // Get surface state and verify there are no surfaces.
- ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
- ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-
- // Get attributes for an invalid surface index.
- EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0));
-
- const bool kInitialVisibility = true;
- const int32_t kInitialZOrder = 10;
- auto surface_status =
- CreateApplicationSurface(kInitialVisibility, kInitialZOrder);
- ASSERT_STATUS_OK(surface_status);
- auto surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Check the initial attribute values.
- auto attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- auto attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- std::set<int32_t> actual_keys;
- std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- // Collect all the keys in attributes that match the expected keys.
- auto compare_keys = [](const auto& attributes, const auto& expected_keys) {
- std::set<int32_t> keys;
- for (const auto& attribute : attributes) {
- if (expected_keys.find(attribute.key) != expected_keys.end())
- keys.emplace(attribute.key);
- }
- return keys;
- };
-
- // If the sets match then attributes contained at least the expected keys,
- // even if other keys were also present.
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- std::vector<DvrSurfaceAttribute> attributes_to_set = {
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)};
-
- // Test invalid args.
- EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(),
- attributes_to_set.size()));
- EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr,
- attributes_to_set.size()));
-
- // Test attribute change events.
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
-
- // Verify the attributes changed flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- // Test setting and then deleting an attribute.
- const DvrSurfaceAttributeKey kUserKey = 1;
- attributes_to_set = {MakeAttribute(kUserKey, 1024)};
-
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
- kUserKey};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- // Delete the attribute.
- attributes_to_set = {MakeAttribute(kUserKey, nullptr)};
-
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
- kUserKey};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_NE(expected_keys, actual_keys);
-
- // Test deleting a reserved attribute.
- attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)};
-
- EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
-
- // Failed attribute operations should not trigger update events.
- const int kTimeoutMs = 100; // 0.1s
- EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- enum : std::int32_t {
- kInt32Key = 1,
- kInt64Key,
- kBoolKey,
- kFloatKey,
- kFloat2Key,
- kFloat3Key,
- kFloat4Key,
- kFloat8Key,
- kFloat16Key,
- };
-
- const std::vector<DvrSurfaceAttribute> attributes_to_set = {
- MakeAttribute(kInt32Key, int32_t{0}),
- MakeAttribute(kInt64Key, int64_t{0}),
- MakeAttribute(kBoolKey, false),
- MakeAttribute(kFloatKey, 0.0f),
- MakeAttribute(kFloat2Key, std::array<float, 2>{{1.0f, 2.0f}}),
- MakeAttribute(kFloat3Key, std::array<float, 3>{{3.0f, 4.0f, 5.0f}}),
- MakeAttribute(kFloat4Key, std::array<float, 4>{{6.0f, 7.0f, 8.0f, 9.0f}}),
- MakeAttribute(kFloat8Key,
- std::array<float, 8>{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f,
- 15.0f, 16.0f, 17.0f}}),
- MakeAttribute(kFloat16Key, std::array<float, 16>{
- {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f,
- 24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f,
- 30.0f, 31.0f, 32.0f, 33.0f}})};
-
- EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- auto attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- auto attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), attributes_to_set.size());
-
- auto HasAttribute = [](const auto& attributes,
- DvrSurfaceAttributeKey key) -> bool {
- for (const auto& attribute : attributes) {
- if (attribute.key == key)
- return true;
- }
- return false;
- };
- auto AttributeType =
- [](const auto& attributes,
- DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType {
- for (const auto& attribute : attributes) {
- if (attribute.key == key)
- return attribute.value.type;
- }
- return DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- };
-
- ASSERT_TRUE(HasAttribute(attributes, kInt32Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- AttributeType(attributes, kInt32Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kInt64Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64,
- AttributeType(attributes, kInt64Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kBoolKey));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- AttributeType(attributes, kBoolKey));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloatKey));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT,
- AttributeType(attributes, kFloatKey));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat2Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2,
- AttributeType(attributes, kFloat2Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat3Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3,
- AttributeType(attributes, kFloat3Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat4Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4,
- AttributeType(attributes, kFloat4Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat8Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8,
- AttributeType(attributes, kFloat8Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat16Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16,
- AttributeType(attributes, kFloat16Key));
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- const int surface_id = dvrSurfaceGetId(surface.get());
- ASSERT_GE(surface_id, 0);
- // Get surface state and verify there is one surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify there are no queues for the surface recorded in the state
- // snapshot.
- EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));
-
- // Create a new queue in the surface.
- auto write_queue_status = CreateSurfaceQueue(
- surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1,
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
- ASSERT_STATUS_OK(write_queue_status);
- UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
- ASSERT_NE(nullptr, write_queue.get());
-
- const int queue_id = dvrWriteBufferQueueGetId(write_queue.get());
- ASSERT_GE(queue_id, 0);
-
- // Update surface state.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify the buffers changed flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- auto queue_ids_status = manager_->GetQueueIds(0);
- ASSERT_STATUS_OK(queue_ids_status);
-
- auto queue_ids = queue_ids_status.take();
- ASSERT_EQ(1u, queue_ids.size());
- EXPECT_EQ(queue_id, queue_ids[0]);
-
- auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id);
- ASSERT_STATUS_OK(read_queue_status);
- UniqueDvrReadBufferQueue read_queue = read_queue_status.take();
- ASSERT_NE(nullptr, read_queue.get());
- EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get()));
-
- write_queue.reset();
-
- // Verify that destroying the queue generates a surface update event.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify that the buffers changed flag is set.
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- // Verify that the queue ids reflect the change.
- queue_ids_status = manager_->GetQueueIds(0);
- ASSERT_STATUS_OK(queue_ids_status);
-
- queue_ids = queue_ids_status.take();
- ASSERT_EQ(0u, queue_ids.size());
-}
-
-TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- // Get surface state and verify there is one surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Create a new queue in the surface.
- const uint32_t kLayerCount = 3;
- auto write_queue_status = CreateSurfaceQueue(
- surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount,
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
- ASSERT_STATUS_OK(write_queue_status);
- UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
- ASSERT_NE(nullptr, write_queue.get());
-
- DvrWriteBuffer* buffer = nullptr;
- DvrNativeBufferMetadata metadata;
- int fence_fd = -1;
- int error = dvrWriteBufferQueueGainBuffer(write_queue.get(), /*timeout=*/1000,
- &buffer, &metadata, &fence_fd);
- ASSERT_EQ(0, error);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer);
- ASSERT_EQ(0, error);
-
- AHardwareBuffer_Desc desc = {};
- AHardwareBuffer_describe(hardware_buffer, &desc);
- ASSERT_EQ(kLayerCount, desc.layers);
-
- AHardwareBuffer_release(hardware_buffer);
- dvrWriteBufferDestroy(buffer);
-}
-
-TEST_F(Test, ConfigurationData) {
- // TODO(hendrikw): Move this test and GetConfigData helper function out of the
- // display manager tests.
- auto data1 = GetConfigData(-1);
- ASSERT_STATUS_ERROR(data1);
-
- const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
-
- // This should be run on devices with and without built in metrics.
- bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
- auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
- if (has_metric) {
- ASSERT_STATUS_OK(data2);
- ASSERT_NE(0u, data2.get().size());
- } else {
- ASSERT_STATUS_ERROR(data2);
- }
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
deleted file mode 100644
index 5c837e7..0000000
--- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-#include <android/hardware_buffer.h>
-#include <dvr/dvr_buffer.h>
-#include <dvr/dvr_config.h>
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/dvr_surface.h>
-#include <system/graphics.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-TEST(DvrGlobalBufferTest, TestGlobalBuffersSameName) {
- const DvrGlobalBufferKey buffer_key = 101;
- DvrBuffer* buffer1 = nullptr;
- int ret1 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer1);
- ASSERT_EQ(0, ret1);
- ASSERT_NE(nullptr, buffer1);
-
- DvrBuffer* buffer2 = nullptr;
- int ret2 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer2);
- ASSERT_EQ(0, ret2);
- ASSERT_NE(nullptr, buffer2);
-
- AHardwareBuffer* hardware_buffer1 = nullptr;
- int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1);
- ASSERT_EQ(0, e1);
- ASSERT_NE(nullptr, hardware_buffer1);
-
- AHardwareBuffer* hardware_buffer2 = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer2);
-
- AHardwareBuffer_Desc desc1 = {};
- AHardwareBuffer_describe(hardware_buffer1, &desc1);
- AHardwareBuffer_Desc desc2 = {};
- AHardwareBuffer_describe(hardware_buffer2, &desc2);
- ASSERT_EQ(desc1.width, 10u);
- ASSERT_EQ(desc1.height, 1u);
- ASSERT_EQ(desc1.layers, 1u);
- ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc1.usage, 0u);
- ASSERT_EQ(desc2.width, 10u);
- ASSERT_EQ(desc2.height, 1u);
- ASSERT_EQ(desc2.layers, 1u);
- ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc2.usage, 0u);
-
- dvrBufferDestroy(buffer1);
- dvrBufferDestroy(buffer2);
-
- DvrBuffer* buffer3 = nullptr;
- int e3 = dvrGetGlobalBuffer(buffer_key, &buffer3);
- ASSERT_NE(nullptr, buffer3);
- ASSERT_EQ(0, e3);
-
- AHardwareBuffer* hardware_buffer3 = nullptr;
- int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3);
- ASSERT_EQ(0, e4);
- ASSERT_NE(nullptr, hardware_buffer3);
-
- AHardwareBuffer_Desc desc3 = {};
- AHardwareBuffer_describe(hardware_buffer3, &desc3);
- ASSERT_EQ(desc3.width, 10u);
- ASSERT_EQ(desc3.height, 1u);
- ASSERT_EQ(desc3.layers, 1u);
- ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc3.usage, 0u);
-
- dvrBufferDestroy(buffer3);
-
- AHardwareBuffer_release(hardware_buffer1);
- AHardwareBuffer_release(hardware_buffer2);
- AHardwareBuffer_release(hardware_buffer3);
-}
-
-TEST(DvrGlobalBufferTest, TestMultipleGlobalBuffers) {
- const DvrGlobalBufferKey buffer_key1 = 102;
- const DvrGlobalBufferKey buffer_key2 = 103;
- DvrBuffer* setup_buffer1 = nullptr;
- int ret1 = dvrSetupGlobalBuffer(buffer_key1, 10, 0, &setup_buffer1);
- ASSERT_EQ(0, ret1);
- ASSERT_NE(nullptr, setup_buffer1);
- dvrBufferDestroy(setup_buffer1);
-
- DvrBuffer* setup_buffer2 = nullptr;
- int ret2 = dvrSetupGlobalBuffer(buffer_key2, 10, 0, &setup_buffer2);
- ASSERT_EQ(0, ret2);
- ASSERT_NE(nullptr, setup_buffer2);
- dvrBufferDestroy(setup_buffer2);
-
- DvrBuffer* buffer1 = nullptr;
- int e1 = dvrGetGlobalBuffer(buffer_key1, &buffer1);
- ASSERT_NE(nullptr, buffer1);
- ASSERT_EQ(0, e1);
- dvrBufferDestroy(buffer1);
-
- DvrBuffer* buffer2 = nullptr;
- int e2 = dvrGetGlobalBuffer(buffer_key2, &buffer2);
- ASSERT_NE(nullptr, buffer2);
- ASSERT_EQ(0, e2);
- dvrBufferDestroy(buffer2);
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferUsage) {
- const DvrGlobalBufferKey buffer_key = 100;
-
- // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because
- // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to
- // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different.
- // If all is good, when we get the AHardwareBuffer, it should be converted
- // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE.
- const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE;
-
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_key, 10, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- AHardwareBuffer_Desc desc = {};
- AHardwareBuffer_describe(hardware_buffer, &desc);
- ASSERT_EQ(usage, desc.usage);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferCarriesData) {
- const DvrGlobalBufferKey buffer_name = 110;
-
- uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- constexpr size_t size = 1024 * sizeof(uint64_t);
- constexpr uint64_t value = 0x123456787654321;
-
- {
- // Allocate some data and set it to something.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- for (size_t i = 0; i < num_values; ++i) {
- data[i] = value;
- }
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
- }
-
- {
- // Get the buffer and check that all the data is still present.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrGetGlobalBuffer(buffer_name, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- bool is_equal = true;
- for (size_t i = 0; i < num_values; ++i) {
- is_equal &= (data[i] == value);
- }
- ASSERT_TRUE(is_equal);
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
- }
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferZeroed) {
- const DvrGlobalBufferKey buffer_name = 120;
-
- // Allocate 1MB and check that it is all zeros.
- uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- constexpr size_t size = 1024 * 1024;
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- uint64_t zero = 0;
- for (size_t i = 0; i < num_values; ++i) {
- zero |= data[i];
- }
- ASSERT_EQ(0U, zero);
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
-}
-
-TEST(DvrGlobalBufferTest, TestVrflingerConfigBuffer) {
- const DvrGlobalBufferKey buffer_name =
- DvrGlobalBuffers::kVrFlingerConfigBufferKey;
-
- // First delete any existing buffer so we can test the failure case.
- dvrDeleteGlobalBuffer(buffer_name);
-
- const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY;
-
- size_t correct_size = DvrConfigRing::MemorySize();
- size_t wrong_size = DvrConfigRing::MemorySize(0);
-
- // Setup an invalid config buffer (too small) and assert that it fails.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, wrong_size, usage, &setup_buffer);
- ASSERT_EQ(nullptr, setup_buffer);
- ASSERT_GT(0, e1);
-
- // Setup a correct config buffer.
- int e2 =
- dvrSetupGlobalBuffer(buffer_name, correct_size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e2);
-
- dvrBufferDestroy(setup_buffer);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
deleted file mode 100644
index 3b6d6e1..0000000
--- a/libs/vr/libdvr/tests/dvr_tracking-test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <android/log.h>
-#include <gtest/gtest.h>
-
-#include "dvr_api_test.h"
-
-namespace {
-
-class DvrTrackingTest : public DvrApiTest {};
-
-#if DVR_TRACKING_IMPLEMENTED
-
-TEST_F(DvrTrackingTest, Implemented) {
- ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
-
- ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
-}
-
-TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) {
- int ret;
- ret = api_.TrackingCameraCreate(nullptr);
- EXPECT_EQ(ret, -EINVAL);
-
- DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42);
- ret = api_.TrackingCameraCreate(&camera);
- EXPECT_EQ(ret, -EINVAL);
-}
-
-TEST_F(DvrTrackingTest, CameraCreateDestroy) {
- DvrTrackingCamera* camera = nullptr;
- int ret = api_.TrackingCameraCreate(&camera);
-
- EXPECT_EQ(ret, 0);
- ASSERT_TRUE(camera != nullptr);
-
- api_.TrackingCameraDestroy(camera);
-}
-
-TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) {
- int ret;
- ret = api_.TrackingFeatureExtractorCreate(nullptr);
- EXPECT_EQ(ret, -EINVAL);
-
- DvrTrackingFeatureExtractor* camera =
- reinterpret_cast<DvrTrackingFeatureExtractor*>(42);
- ret = api_.TrackingFeatureExtractorCreate(&camera);
- EXPECT_EQ(ret, -EINVAL);
-}
-
-TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) {
- DvrTrackingFeatureExtractor* camera = nullptr;
- int ret = api_.TrackingFeatureExtractorCreate(&camera);
-
- EXPECT_EQ(ret, 0);
- ASSERT_TRUE(camera != nullptr);
-
- api_.TrackingFeatureExtractorDestroy(camera);
-}
-
-#else // !DVR_TRACKING_IMPLEMENTED
-
-TEST_F(DvrTrackingTest, NotImplemented) {
- ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
- ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
-
- EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS);
-
- ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
-
- EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr),
- -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr,
- nullptr, nullptr),
- -ENOSYS);
-
- ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsStart != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsStop != nullptr);
-
- EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS);
-}
-
-#endif // DVR_TRACKING_IMPLEMENTED
-
-} // namespace
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index b3d6f74..e0ba946 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -118,70 +118,27 @@
: "rax", "cc" \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
+ #define API_ENTRY(_api) __attribute__((noinline)) _api
- #define API_ENTRY(_api) __attribute__((noinline)) _api
-
- #define CALL_GL_EXTENSION_API(_api, ...) \
- register unsigned int _t0 asm("$12"); \
- register unsigned int _fn asm("$25"); \
- register unsigned int _tls asm("$3"); \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr %[tls], $29\n\t" \
- "ld %[t0], %[OPENGL_API](%[tls])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " move %[fn], $ra\n\t" \
- "ld %[t0], %[API](%[t0])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " nop\n\t" \
- "move %[fn], %[t0]\n\t" \
- "1:\n\t" \
- "jalr $0, %[fn]\n\t" \
- " nop\n\t" \
- ".set pop\n\t" \
- : [fn] "=c"(_fn), \
- [tls] "=&r"(_tls), \
- [t0] "=&r"(_t0) \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, \
- ext.extensions[_api])) \
- : \
- );
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((noinline)) _api
-
- #define CALL_GL_EXTENSION_API(_api, ...) \
- register unsigned int _t0 asm("$8"); \
- register unsigned int _fn asm("$25"); \
- register unsigned int _tls asm("$3"); \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr %[tls], $29\n\t" \
- "lw %[t0], %[OPENGL_API](%[tls])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " move %[fn], $ra\n\t" \
- "lw %[t0], %[API](%[t0])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " nop\n\t" \
- "move %[fn], %[t0]\n\t" \
- "1:\n\t" \
- "jalr $0, %[fn]\n\t" \
- " nop\n\t" \
- ".set pop\n\t" \
- : [fn] "=c"(_fn), \
- [tls] "=&r"(_tls), \
- [t0] "=&r"(_t0) \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, \
- ext.extensions[_api])) \
- : \
- );
+ #define CALL_GL_EXTENSION_API(_api) \
+ asm volatile( \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1: ret\n" \
+ : \
+ : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \
+ [api] "i" (__builtin_offsetof(gl_hooks_t, \
+ ext.extensions[_api])) \
+ : "t0", "t1" \
+ );
#endif
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 65f50f5..5bd5c14 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -191,73 +191,44 @@
: \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
#define API_ENTRY(_api) __attribute__((naked,noinline)) _api
- // t0: $12
- // fn: $25
- // tls: $3
- // v0: $2
- #define CALL_GL_API_INTERNAL_CALL(_api, ...) \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr $3, $29\n\t" \
- "ld $12, %[OPENGL_API]($3)\n\t" \
- "beqz $12, 1f\n\t" \
- " move $25, $ra\n\t" \
- "ld $12, %[API]($12)\n\t" \
- "beqz $12, 1f\n\t" \
- " nop\n\t" \
- "move $25, $12\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
- : \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \
- "$10", "$11", "$12", "$25" \
- );
-
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
-
- // t0: $8
- // fn: $25
- // tls: $3
- // v0: $2
#define CALL_GL_API_INTERNAL_CALL(_api, ...) \
asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr $3, $29\n\t" \
- "lw $3, %[OPENGL_API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " move $25,$ra\n\t" \
- "lw $3, %[API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " nop\n\t" \
- "move $25, $3\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1:\n" \
: \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \
+ : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \
+ [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+ : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \
+ "a5", "t6", "t3", "t4", "t5", "t6" \
);
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
+ #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+ asm volatile( \
+ "li a0, 0\n" \
+ : \
+ : \
+ : "a0" \
+ );
+
+ #define CALL_GL_API_INTERNAL_DO_RETURN \
+ asm volatile( \
+ "ret\n" \
+ : \
+ : \
+ : \
+ );
#endif
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index bacd4b4..64c0f97 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -247,73 +247,44 @@
: \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
#define API_ENTRY(_api) __attribute__((naked,noinline)) _api
- // t0: $12
- // fn: $25
- // tls: $3
- // v0: $2
- #define CALL_GL_API_INTERNAL_CALL(_api, ...) \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr $3, $29\n\t" \
- "ld $12, %[OPENGL_API]($3)\n\t" \
- "beqz $12, 1f\n\t" \
- " move $25, $ra\n\t" \
- "ld $12, %[API]($12)\n\t" \
- "beqz $12, 1f\n\t" \
- " nop\n\t" \
- "move $25, $12\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
- : \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \
- "$10", "$11", "$12", "$25" \
- );
-
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
-
- // t0: $8
- // fn: $25
- // tls: $3
- // v0: $2
#define CALL_GL_API_INTERNAL_CALL(_api, ...) \
asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr $3, $29\n\t" \
- "lw $3, %[OPENGL_API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " move $25,$ra\n\t" \
- "lw $3, %[API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " nop\n\t" \
- "move $25, $3\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1:\n" \
: \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \
+ : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \
+ [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+ : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \
+ "a5", "t6", "t3", "t4", "t5", "t6" \
);
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
+ #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+ asm volatile( \
+ "li a0, 0\n" \
+ : \
+ : \
+ : "a0" \
+ );
+
+ #define CALL_GL_API_INTERNAL_DO_RETURN \
+ asm volatile( \
+ "ret\n" \
+ : \
+ : \
+ : \
+ );
#endif
diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp
index 1e37991..9b78391 100644
--- a/services/batteryservice/Android.bp
+++ b/services/batteryservice/Android.bp
@@ -9,6 +9,7 @@
cc_library_headers {
name: "libbatteryservice_headers",
+ host_supported: true,
vendor_available: true,
recovery_available: true,
export_include_dirs: ["include"],
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ddcd51f..d1de551 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -41,7 +41,9 @@
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
sanitize: {
- misc_undefined: ["bounds"],
+ misc_undefined: [
+ "bounds",
+ ],
},
tidy: true,
tidy_checks: [
@@ -57,11 +59,11 @@
filegroup {
name: "libinputflinger_sources",
srcs: [
- "InputProcessor.cpp",
"InputCommonConverter.cpp",
+ "InputManager.cpp",
+ "InputProcessor.cpp",
"PreferStylusOverTouchBlocker.cpp",
"UnwantedInteractionBlocker.cpp",
- "InputManager.cpp",
],
}
@@ -77,13 +79,10 @@
"libcrypto",
"libcutils",
"libhidlbase",
- "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libstatspull",
- "libstatssocket",
"libutils",
"server_configurable_flags",
],
@@ -92,6 +91,23 @@
"libpalmrejection",
"libui-types",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libgui",
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ },
}
cc_library_shared {
@@ -108,9 +124,8 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
- "libinputreporter",
"libinputreader",
- "libgui",
+ "libinputreporter",
],
static_libs: [
"libinputdispatcher",
@@ -130,6 +145,7 @@
cc_library_headers {
name: "libinputflinger_headers",
+ host_supported: true,
export_include_dirs: ["include"],
}
@@ -151,17 +167,30 @@
"libbase",
"libbinder",
"libcutils",
- "libinput",
"liblog",
"libutils",
],
header_libs: [
"libinputflinger_headers",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libinput",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libui-types",
+ ],
+ },
+ },
}
cc_library_shared {
name: "libinputflinger_base",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputflinger_base_defaults",
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 23b6f57..6db89d4 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -263,8 +263,11 @@
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14);
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15);
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16);
+// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET.
+// If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the
+// static_assert below and add the new axis here, or leave a comment summarizing your decision.
static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) ==
- static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16));
+ static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
common::VideoFrame out;
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index e2e64f9..e74f258 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -54,7 +54,13 @@
}
bool InputThread::isCallingThread() {
+#if defined(__ANDROID__)
return gettid() == mThread->getTid();
+#else
+ // Assume that the caller is doing everything correctly,
+ // since thread information is not available on host
+ return false;
+#endif
}
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index e5c19af..4e2a6fb 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -21,7 +21,6 @@
"libbinder",
"libcrypto",
"libcutils",
- "libinput",
"libinputflinger_base",
"libinputreporter",
"liblog",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index eb79b76..99c4936 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -23,6 +23,7 @@
cc_library_headers {
name: "libinputdispatcher_headers",
+ host_supported: true,
export_include_dirs: [
"include",
],
@@ -33,6 +34,7 @@
srcs: [
"AnrTracker.cpp",
"Connection.cpp",
+ "DragState.cpp",
"Entry.cpp",
"FocusResolver.cpp",
"InjectionState.cpp",
@@ -45,7 +47,6 @@
"LatencyTracker.cpp",
"Monitor.cpp",
"TouchState.cpp",
- "DragState.cpp",
],
}
@@ -56,20 +57,34 @@
"libbase",
"libcrypto",
"libcutils",
- "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libstatspull",
- "libstatssocket",
- "libgui",
"libutils",
"server_configurable_flags",
],
static_libs: [
"libattestation",
+ "libgui_window_info_static",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libgui",
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ },
header_libs: [
"libinputdispatcher_headers",
],
@@ -84,8 +99,8 @@
shared_libs: [
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
- "libinputreporter",
"libinputflinger_base",
+ "libinputreporter",
],
export_header_lib_headers: [
"libinputdispatcher_headers",
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
index c3f611e..a18063f 100644
--- a/services/inputflinger/dispatcher/AnrTracker.cpp
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -54,7 +54,7 @@
}
// If empty() is false, return the time at which the next connection should cause an ANR
-// If empty() is true, return LONG_LONG_MAX
+// If empty() is true, return LLONG_MAX
nsecs_t AnrTracker::firstTimeout() const {
if (mAnrTimeouts.empty()) {
return std::numeric_limits<nsecs_t>::max();
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
index 5e5e0c4..cff5d00 100644
--- a/services/inputflinger/dispatcher/AnrTracker.h
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -35,7 +35,7 @@
bool empty() const;
// If empty() is false, return the time at which the next connection should cause an ANR
- // If empty() is true, return LONG_LONG_MAX
+ // If empty() is true, return LLONG_MAX
nsecs_t firstTimeout() const;
// Return the token of the next connection that should cause an ANR.
// Do not call this unless empty() is false, you will encounter undefined behaviour.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a793f57..694c127 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -25,7 +25,9 @@
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include <ftl/enum.h>
+#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
+#endif
#include <input/InputDevice.h>
#include <powermanager/PowerManager.h>
#include <unistd.h>
@@ -539,6 +541,17 @@
entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
}
+std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
+ if (eventEntry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ return keyEntry.downTime;
+ } else if (eventEntry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ return motionEntry.downTime;
+ }
+ return std::nullopt;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -553,7 +566,7 @@
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mAppSwitchSawKeyDown(false),
- mAppSwitchDueTime(LONG_LONG_MAX),
+ mAppSwitchDueTime(LLONG_MAX),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
@@ -569,8 +582,9 @@
mReporter = createInputReporter();
mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
+#if defined(__ANDROID__)
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
-
+#endif
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
@@ -609,7 +623,7 @@
}
void InputDispatcher::dispatchOnce() {
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
+ nsecs_t nextWakeupTime = LLONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
@@ -623,7 +637,7 @@
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptable()) {
- nextWakeupTime = LONG_LONG_MIN;
+ nextWakeupTime = LLONG_MIN;
}
// If we are still waiting for ack on some events,
@@ -633,7 +647,7 @@
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
- if (nextWakeupTime == LONG_LONG_MAX) {
+ if (nextWakeupTime == LLONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
@@ -678,14 +692,14 @@
*/
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
- nsecs_t nextAnrCheck = LONG_LONG_MAX;
+ nsecs_t nextAnrCheck = LLONG_MAX;
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
mNoFocusedWindowTimeoutTime = std::nullopt;
- return LONG_LONG_MIN;
+ return LLONG_MIN;
} else {
// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
@@ -708,7 +722,7 @@
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
onAnrLocked(connection);
- return LONG_LONG_MIN;
+ return LLONG_MIN;
}
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
@@ -915,7 +929,7 @@
mLastDropReason = dropReason;
releasePendingEventLocked();
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
}
@@ -1068,7 +1082,7 @@
int32_t y, TouchState* touchState,
bool isStylus,
bool addOutsideTargets,
- bool ignoreDragWindow) {
+ bool ignoreDragWindow) const {
if (addOutsideTargets && touchState == nullptr) {
LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
}
@@ -1198,11 +1212,11 @@
}
bool InputDispatcher::isAppSwitchPendingLocked() {
- return mAppSwitchDueTime != LONG_LONG_MAX;
+ return mAppSwitchDueTime != LLONG_MAX;
}
void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
- mAppSwitchDueTime = LONG_LONG_MAX;
+ mAppSwitchDueTime = LLONG_MAX;
if (DEBUG_APP_SWITCH) {
if (handled) {
@@ -1495,7 +1509,7 @@
// stop the key repeat on current device.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+ mKeyRepeatState.nextRepeatTime = LLONG_MAX; // don't generate repeats ourselves
} else {
// Not a repeat. Save key down state in case we do see a repeat later.
resetKeyRepeatLocked();
@@ -1565,9 +1579,10 @@
}
// Identify targets.
- std::vector<InputTarget> inputTargets;
- InputEventInjectionResult injectionResult =
- findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+ InputEventInjectionResult injectionResult;
+ sp<WindowInfoHandle> focusedWindow =
+ findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime,
+ /*byref*/ injectionResult);
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
@@ -1576,6 +1591,12 @@
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
+ LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+
+ std::vector<InputTarget> inputTargets;
+ addWindowTargetLocked(focusedWindow,
+ InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+ BitSet32(0), getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1670,13 +1691,27 @@
pilferPointersLocked(mDragState->dragWindow->getToken());
}
- injectionResult =
- findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
- &conflictingPointerActions);
+ std::vector<TouchedWindow> touchedWindows =
+ findTouchedWindowTargetsLocked(currentTime, *entry, nextWakeupTime,
+ &conflictingPointerActions,
+ /*byref*/ injectionResult);
+ for (const TouchedWindow& touchedWindow : touchedWindows) {
+ LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED,
+ "Shouldn't be adding window if the injection didn't succeed.");
+ addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+ inputTargets);
+ }
} else {
// Non touch event. (eg. trackball)
- injectionResult =
- findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+ sp<WindowInfoHandle> focusedWindow =
+ findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult);
+ if (injectionResult == InputEventInjectionResult::SUCCEEDED) {
+ LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+ addWindowTargetLocked(focusedWindow,
+ InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+ BitSet32(0), getDownTime(*entry), inputTargets);
+ }
}
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
@@ -1878,21 +1913,11 @@
return false;
}
-static std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
- if (eventEntry.type == EventEntry::Type::KEY) {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
- return keyEntry.downTime;
- } else if (eventEntry.type == EventEntry::Type::MOTION) {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
- return motionEntry.downTime;
- }
- return std::nullopt;
-}
-
-InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
- nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) {
+sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ InputEventInjectionResult& outInjectionResult) {
std::string reason;
+ outInjectionResult = InputEventInjectionResult::FAILED; // Default result
int32_t displayId = getTargetDisplayId(entry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
@@ -1905,12 +1930,12 @@
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
ftl::enum_string(entry.type).c_str(), displayId);
- return InputEventInjectionResult::FAILED;
+ return nullptr;
}
// Drop key events if requested by input feature
if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
- return InputEventInjectionResult::FAILED;
+ return nullptr;
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -1930,15 +1955,17 @@
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
ftl::enum_string(entry.type).c_str());
- return InputEventInjectionResult::FAILED;
+ return nullptr;
} else {
// Still waiting for the focused window
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
}
@@ -1948,13 +1975,15 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- return InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+ return nullptr;
}
if (focusedWindowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
// If the event is a key event, then we must wait for all previous events to
@@ -1971,17 +2000,13 @@
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
}
- // Success! Output targets.
- addWindowTargetLocked(focusedWindowHandle,
- InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0), getDownTime(entry), inputTargets);
-
- // Done.
- return InputEventInjectionResult::SUCCEEDED;
+ outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+ return focusedWindowHandle;
}
/**
@@ -2010,11 +2035,12 @@
return responsiveMonitors;
}
-InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
+std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, nsecs_t* nextWakeupTime,
+ bool* outConflictingPointerActions, InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
+ std::vector<TouchedWindow> touchedWindows;
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
const int32_t displayId = entry.displayId;
@@ -2022,7 +2048,7 @@
const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
- InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
sp<WindowInfoHandle> newTouchedWindowHandle;
@@ -2055,7 +2081,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -2071,7 +2097,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -2097,7 +2123,7 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected touch event: %s", (*err).c_str());
- injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
newTouchedWindowHandle = nullptr;
goto Failed;
}
@@ -2138,7 +2164,7 @@
if (newTouchedWindows.empty()) {
ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
x, y, displayId);
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -2190,7 +2216,7 @@
"dropped the pointer down event in display %" PRId32,
displayId);
}
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -2209,7 +2235,7 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
newTouchedWindowHandle = nullptr;
goto Failed;
}
@@ -2300,7 +2326,7 @@
})) {
ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
displayId, entry.getDescription().c_str());
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -2320,7 +2346,7 @@
ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
"%d:%s",
*entry.injectionState->targetUid, errs.c_str());
- injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
goto Failed;
}
}
@@ -2377,13 +2403,8 @@
}
// Success! Output targets.
- injectionResult = InputEventInjectionResult::SUCCEEDED;
-
- for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
- inputTargets);
- }
+ touchedWindows = tempTouchState.windows;
+ outInjectionResult = InputEventInjectionResult::SUCCEEDED;
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
@@ -2468,7 +2489,7 @@
mLastHoverWindowHandle = newHoverWindowHandle;
}
- return injectionResult;
+ return touchedWindows;
}
void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
@@ -2575,7 +2596,7 @@
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) {
+ std::vector<InputTarget>& inputTargets) const {
std::vector<InputTarget>::iterator it =
std::find_if(inputTargets.begin(), inputTargets.end(),
[&windowHandle](const InputTarget& inputTarget) {
@@ -3202,6 +3223,55 @@
postCommandLocked(std::move(command));
}
+status_t InputDispatcher::publishMotionEvent(Connection& connection,
+ DispatchEntry& dispatchEntry) const {
+ const EventEntry& eventEntry = *(dispatchEntry.eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+
+ PointerCoords scaledCoords[MAX_POINTERS];
+ const PointerCoords* usingCoords = motionEntry.pointerCoords;
+
+ // Set the X and Y offset and X and Y scale depending on the input source.
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
+ !(dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+ float globalScaleFactor = dispatchEntry.globalScaleFactor;
+ if (globalScaleFactor != 1.0f) {
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i] = motionEntry.pointerCoords[i];
+ // Don't apply window scale here since we don't want scale to affect raw
+ // coordinates. The scale will be sent back to the client and applied
+ // later when requesting relative coordinates.
+ scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+ 1 /* windowYScale */);
+ }
+ usingCoords = scaledCoords;
+ }
+ } else if (dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+ // We don't want the dispatch target to know the coordinates
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i].clear();
+ }
+ usingCoords = scaledCoords;
+ }
+
+ std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry);
+
+ // Publish the motion event.
+ return connection.inputPublisher
+ .publishMotionEvent(dispatchEntry.seq, dispatchEntry.resolvedEventId,
+ motionEntry.deviceId, motionEntry.source, motionEntry.displayId,
+ std::move(hmac), dispatchEntry.resolvedAction,
+ motionEntry.actionButton, dispatchEntry.resolvedFlags,
+ motionEntry.edgeFlags, motionEntry.metaState,
+ motionEntry.buttonState, motionEntry.classification,
+ dispatchEntry.transform, motionEntry.xPrecision,
+ motionEntry.yPrecision, motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition, dispatchEntry.rawTransform,
+ motionEntry.downTime, motionEntry.eventTime,
+ motionEntry.pointerCount, motionEntry.pointerProperties,
+ usingCoords);
+}
+
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
@@ -3241,58 +3311,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
-
- PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry.pointerCoords;
-
- // Set the X and Y offset and X and Y scale depending on the input source.
- if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
- !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float globalScaleFactor = dispatchEntry->globalScaleFactor;
- if (globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
- scaledCoords[i] = motionEntry.pointerCoords[i];
- // Don't apply window scale here since we don't want scale to affect raw
- // coordinates. The scale will be sent back to the client and applied
- // later when requesting relative coordinates.
- scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
- 1 /* windowYScale */);
- }
- usingCoords = scaledCoords;
- }
- } else {
- // We don't want the dispatch target to know.
- if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
- scaledCoords[i].clear();
- }
- usingCoords = scaledCoords;
- }
- }
-
- std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
-
- // Publish the motion event.
- status = connection->inputPublisher
- .publishMotionEvent(dispatchEntry->seq,
- dispatchEntry->resolvedEventId,
- motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId, std::move(hmac),
- dispatchEntry->resolvedAction,
- motionEntry.actionButton,
- dispatchEntry->resolvedFlags,
- motionEntry.edgeFlags, motionEntry.metaState,
- motionEntry.buttonState,
- motionEntry.classification,
- dispatchEntry->transform,
- motionEntry.xPrecision, motionEntry.yPrecision,
- motionEntry.xCursorPosition,
- motionEntry.yCursorPosition,
- dispatchEntry->rawTransform,
- motionEntry.downTime, motionEntry.eventTime,
- motionEntry.pointerCount,
- motionEntry.pointerProperties, usingCoords);
+ status = publishMotionEvent(*connection, *dispatchEntry);
break;
}
@@ -3981,13 +4000,14 @@
if (DEBUG_INBOUND_EVENT_DETAILS) {
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+ "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
args->id, args->eventTime, args->deviceId, args->source, args->displayId,
- args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
- args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
- args->xCursorPosition, args->yCursorPosition, args->downTime);
+ args->policyFlags, MotionEvent::actionToString(args->action).c_str(),
+ args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags,
+ args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+ args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
"x=%f, y=%f, pressure=%f, size=%f, "
@@ -5009,8 +5029,6 @@
? "not set"
: std::to_string(mTouchModePerDisplay[displayId]).c_str());
- // TODO(b/198499018): Ensure that WM can guarantee that touch mode is properly set when
- // display is created.
auto touchModeIt = mTouchModePerDisplay.find(displayId);
if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
return false;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 14b046a..dea2cae 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -239,7 +239,7 @@
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
- bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
+ bool addOutsideTargets = false, bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
@@ -542,19 +542,20 @@
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
- android::os::InputEventInjectionResult findFocusedWindowTargetsLocked(
- nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
+ std::vector<TouchedWindow> findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, nsecs_t* nextWakeupTime,
+ bool* outConflictingPointerActions,
+ android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
@@ -601,6 +602,7 @@
void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
const InputTarget& inputTarget, int32_t dispatchMode)
REQUIRES(mLock);
+ status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index cacb63c..3b0f2ac 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -142,6 +142,9 @@
virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
/* Get light player ID */
virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+ /* Get the Bluetooth address of an input device, if known. */
+ virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
};
// --- InputReaderConfiguration ---
@@ -393,6 +396,8 @@
/* Gets the affine calibration associated with the specified device. */
virtual TouchAffineTransformation getTouchAffineTransformation(
const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+ /* Notifies the input reader policy that a stylus gesture has started. */
+ virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 0f87201..a53fcd7 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -23,6 +23,7 @@
cc_library_headers {
name: "libinputreader_headers",
+ host_supported: true,
export_include_dirs: [
"controller",
"include",
@@ -36,11 +37,9 @@
srcs: [
"EventHub.cpp",
"InputDevice.cpp",
+ "InputReader.cpp",
+ "TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
- "mapper/accumulator/CursorButtonAccumulator.cpp",
- "mapper/accumulator/CursorScrollAccumulator.cpp",
- "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
- "mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
@@ -53,8 +52,11 @@
"mapper/SwitchInputMapper.cpp",
"mapper/TouchInputMapper.cpp",
"mapper/VibratorInputMapper.cpp",
- "InputReader.cpp",
- "TouchVideoDevice.cpp",
+ "mapper/accumulator/CursorButtonAccumulator.cpp",
+ "mapper/accumulator/CursorScrollAccumulator.cpp",
+ "mapper/accumulator/HidUsageAccumulator.cpp",
+ "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
+ "mapper/accumulator/TouchButtonAccumulator.cpp",
],
}
@@ -66,11 +68,9 @@
"libcap",
"libcrypto",
"libcutils",
- "libinput",
"liblog",
"libstatslog",
"libutils",
- "libPlatformProperties",
],
static_libs: [
"libc++fs",
@@ -80,10 +80,25 @@
"libbatteryservice_headers",
"libinputreader_headers",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libPlatformProperties",
+ "libinput",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libbinder",
+ ],
+ },
+ },
}
cc_library_shared {
name: "libinputreader",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputreader_defaults",
@@ -99,6 +114,14 @@
export_header_lib_headers: [
"libinputreader_headers",
],
+ target: {
+ host: {
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ },
+ },
static_libs: [
"libc++fs",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index ca7e426..0aaef53 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/ioctl.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,7 +29,6 @@
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
-#include <sys/limits.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
@@ -43,6 +43,7 @@
#include <ftl/enum.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
+#include <input/PrintTools.h>
#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
#include <statslog.h>
@@ -134,10 +135,6 @@
{"green", LightColor::GREEN},
{"blue", LightColor::BLUE}};
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
static std::string sha1(const std::string& in) {
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -152,6 +149,14 @@
return out;
}
+/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
+static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = {
+ AKEYCODE_STYLUS_BUTTON_PRIMARY,
+ AKEYCODE_STYLUS_BUTTON_SECONDARY,
+ AKEYCODE_STYLUS_BUTTON_TERTIARY,
+ AKEYCODE_STYLUS_BUTTON_TAIL,
+};
+
/**
* Return true if name matches "v4l-touch*"
*/
@@ -192,8 +197,8 @@
// calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
// system call that also queries ktime_get_ts().
- const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) +
- microseconds_to_nanoseconds(event.time.tv_usec);
+ const nsecs_t inputEventTime = seconds_to_nanoseconds(event.input_event_sec) +
+ microseconds_to_nanoseconds(event.input_event_usec);
return inputEventTime;
}
@@ -633,8 +638,8 @@
int32_t sc;
if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) {
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_LED;
ev.code = sc;
ev.value = on ? 1 : 0;
@@ -1463,8 +1468,8 @@
device->ffEffectId = effect.id;
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_FF;
ev.code = device->ffEffectId;
ev.value = 1;
@@ -1485,8 +1490,8 @@
device->ffEffectPlaying = false;
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_FF;
ev.code = device->ffEffectId;
ev.value = 0;
@@ -2128,6 +2133,17 @@
identifier.uniqueId = buffer;
}
+ // Attempt to get the bluetooth address of an input device from the uniqueId.
+ if (identifier.bus == BUS_BLUETOOTH &&
+ std::regex_match(identifier.uniqueId,
+ std::regex("^[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}$"))) {
+ identifier.bluetoothAddress = identifier.uniqueId;
+ // The Bluetooth stack requires alphabetic characters to be uppercase in a valid address.
+ for (auto& c : *identifier.bluetoothAddress) {
+ c = ::toupper(c);
+ }
+ }
+
// Fill in the descriptor.
assignDescriptorLocked(identifier);
@@ -2163,13 +2179,15 @@
device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
- // See if this is a keyboard. Ignore everything in the button range except for
- // joystick and gamepad buttons which are handled like keyboards for the most part.
+ // See if this is a device with keys. This could be full keyboard, or other devices like
+ // gamepads, joysticks, and styluses with buttons that should generate key presses.
bool haveKeyboardKeys =
device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
- if (haveKeyboardKeys || haveGamepadButtons) {
+ bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
+ device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
+ if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
device->classes |= InputDeviceClass::KEYBOARD;
}
@@ -2179,11 +2197,13 @@
device->classes |= InputDeviceClass::CURSOR;
}
- // See if this is a rotary encoder type device.
+ // See if the device is specially configured to be of a certain type.
std::string deviceType;
if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
if (deviceType == "rotaryEncoder") {
device->classes |= InputDeviceClass::ROTARY_ENCODER;
+ } else if (deviceType == "externalStylus") {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
}
}
@@ -2200,14 +2220,10 @@
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
device->absBitmask.test(ABS_Y)) {
device->classes |= InputDeviceClass::TOUCH;
- // Is this a BT stylus?
+ // Is this a stylus that reports contact/pressure independently of touch coordinates?
} else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
!device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
- // Keyboard will try to claim some of the buttons but we really want to reserve those so we
- // can fuse it with the touch screen data, so just take them back. Note this means an
- // external stylus cannot also be a keyboard device.
- device->classes &= ~InputDeviceClass::KEYBOARD;
}
// See if this device is a joystick.
@@ -2292,6 +2308,16 @@
break;
}
}
+
+ // See if this device has any stylus buttons that we would want to fuse with touch data.
+ if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) {
+ for (int32_t keycode : STYLUS_BUTTON_KEYCODES) {
+ if (device->hasKeycodeLocked(keycode)) {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
+ break;
+ }
+ }
+ }
}
// If the device isn't recognized as something we handle, don't monitor it.
@@ -2625,9 +2651,10 @@
dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
- "product=0x%04x, version=0x%04x\n",
+ "product=0x%04x, version=0x%04x, bluetoothAddress=%s\n",
device->identifier.bus, device->identifier.vendor,
- device->identifier.product, device->identifier.version);
+ device->identifier.product, device->identifier.version,
+ toString(device->identifier.bluetoothAddress).c_str());
dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
device->keyMap.keyLayoutFile.c_str());
dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 428e999..f8b1b3f 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -58,6 +58,18 @@
identifier1.location == identifier2.location);
}
+static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
+ const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
+ if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
+ actionMasked != AMOTION_EVENT_ACTION_DOWN &&
+ actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ return false;
+ }
+ const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
+ return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+ motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -101,8 +113,10 @@
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
+ // Copy some state so that we can access it outside the lock later.
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
+ std::list<NotifyArgs> notifyArgs;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -127,7 +141,7 @@
mReaderIsAliveCondition.notify_all();
if (!events.empty()) {
- notifyAll(processEventsLocked(events.data(), events.size()));
+ notifyArgs += processEventsLocked(events.data(), events.size());
}
if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +151,7 @@
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
- notifyAll(timeoutExpiredLocked(now));
+ notifyArgs += timeoutExpiredLocked(now);
}
}
@@ -152,6 +166,16 @@
mPolicy->notifyInputDevicesChanged(inputDevices);
}
+ // Notify the policy of the start of every new stylus gesture outside the lock.
+ for (const auto& args : notifyArgs) {
+ const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
+ if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
+ mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
+ }
+ }
+
+ notifyAll(std::move(notifyArgs));
+
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -851,6 +875,16 @@
return std::nullopt;
}
+std::optional<std::string> InputReader::getBluetoothAddress(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBluetoothAddress();
+ }
+ return std::nullopt;
+}
+
bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index 2f8138b..627dcba 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -198,8 +198,9 @@
if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
// We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
// we can't compare timestamps. Just log a warning, since this is a driver issue
- ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec,
- buf.timestamp.tv_usec);
+ ALOGW("The timestamp %lld.%lld was not acquired using CLOCK_MONOTONIC",
+ static_cast<long long>(buf.timestamp.tv_sec),
+ static_cast<long long>(buf.timestamp.tv_usec));
}
std::vector<int16_t> data(mHeight * mWidth);
const int16_t* readFrom = mReadLocations[buf.index];
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index afb1bed..439123b 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -51,6 +51,9 @@
inline int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+ inline std::optional<std::string> getBluetoothAddress() const {
+ return mIdentifier.bluetoothAddress;
+ }
inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -375,8 +378,11 @@
mEventHub->getAbsoluteAxisInfo(mId, code, &info);
return info.valid;
}
- inline bool isKeyPressed(int32_t code) const {
- return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+ inline bool isKeyPressed(int32_t scanCode) const {
+ return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
+ }
+ inline bool isKeyCodePressed(int32_t keyCode) const {
+ return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
}
inline int32_t getAbsoluteAxisValue(int32_t code) const {
int32_t value;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index de268cf..4f2503a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -113,6 +113,8 @@
std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override;
+ std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 0404c9a..56fc5fa 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -24,7 +24,7 @@
namespace android {
ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext) {}
+ : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {}
uint32_t ExternalStylusInputMapper::getSources() const {
return AINPUT_SOURCE_STYLUS;
@@ -48,13 +48,13 @@
const InputReaderConfiguration* config,
uint32_t changes) {
getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
- mTouchButtonAccumulator.configure(getDeviceContext());
+ mTouchButtonAccumulator.configure();
return {};
}
std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
mSingleTouchMotionAccumulator.reset(getDeviceContext());
- mTouchButtonAccumulator.reset(getDeviceContext());
+ mTouchButtonAccumulator.reset();
return InputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8704d1b..da9413e 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -24,67 +24,70 @@
// --- Static Definitions ---
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
- const int32_t map[][4], size_t mapSize) {
- if (orientation != DISPLAY_ORIENTATION_0) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][orientation];
- }
- }
- }
- return value;
-}
-
-static const int32_t keyCodeRotationMap[][4] = {
- // key codes enumerated counter-clockwise with the original (unrotated) key first
- // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
- {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
- {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
- {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
- {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
- {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
- AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
- {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
- AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
- {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
- AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
- {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
- AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
-};
-
-static const size_t keyCodeRotationMapSize =
- sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
- size_t mapSize) {
- if (orientation == DISPLAY_ORIENTATION_180) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][1];
- }
- }
- }
- return value;
-}
-
-// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
-static int32_t stemKeyRotationMap[][2] = {
- // key codes enumerated with the original (unrotated) key first
- // no rotation, 180 degree rotation
- {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
- {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
- {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
- {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
-};
-
-static const size_t stemKeyRotationMapSize =
- sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-
static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
- keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
- return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
- keyCodeRotationMapSize);
+ static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
+ // key codes enumerated counter-clockwise with the original (unrotated) key first
+ // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+ {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
+ {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
+ {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
+ {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
+ {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+ AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
+ {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+ AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
+ {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+ AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
+ {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
+ };
+
+ LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
+ if (orientation != DISPLAY_ORIENTATION_0) {
+ for (const auto& rotation : KEYCODE_ROTATION_MAP) {
+ if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
+ return rotation[orientation];
+ }
+ }
+ }
+ return keyCode;
+}
+
+static bool isSupportedScanCode(int32_t scanCode) {
+ // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses.
+ return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) ||
+ scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 ||
+ scanCode >= BTN_WHEEL;
+}
+
+static bool isMediaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MEDIA_PLAY:
+ case AKEYCODE_MEDIA_PAUSE:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_RECORD:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_BACKWARD:
+ case AKEYCODE_MEDIA_STEP_FORWARD:
+ case AKEYCODE_MEDIA_STEP_BACKWARD:
+ case AKEYCODE_MEDIA_AUDIO_TRACK:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_VOLUME_MUTE:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+ return true;
+ default:
+ return false;
+ }
}
// --- KeyboardInputMapper ---
@@ -93,8 +96,6 @@
int32_t keyboardType)
: InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}
-KeyboardInputMapper::~KeyboardInputMapper() {}
-
uint32_t KeyboardInputMapper::getSources() const {
return mSource;
}
@@ -130,7 +131,7 @@
}
std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
- nsecs_t when, const InputReaderConfiguration* config) {
+ const InputReaderConfiguration* config) {
if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
@@ -154,35 +155,16 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mViewport = findViewport(when, config);
+ mViewport = findViewport(config);
}
return out;
}
-static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
- int32_t mapped = 0;
- if (config.tryGetProperty(property, mapped) && mapped > 0) {
- for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
- if (stemKeyRotationMap[i][0] == keyCode) {
- stemKeyRotationMap[i][1] = mapped;
- return;
- }
- }
- }
-}
-
void KeyboardInputMapper::configureParameters() {
mParameters.orientationAware = false;
const PropertyMap& config = getDeviceContext().getConfiguration();
config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
- if (mParameters.orientationAware) {
- mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
- mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
- mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
- mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
- }
-
mParameters.handlesKeyRepeat = false;
config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
@@ -190,7 +172,7 @@
config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
}
-void KeyboardInputMapper::dumpParameters(std::string& dump) {
+void KeyboardInputMapper::dumpParameters(std::string& dump) const {
dump += INDENT3 "Parameters:\n";
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
@@ -198,7 +180,7 @@
std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
std::list<NotifyArgs> out = cancelAllDownKeys(when);
- mCurrentHidUsage = 0;
+ mHidUsageAccumulator.reset();
resetLedState();
@@ -208,68 +190,21 @@
std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
std::list<NotifyArgs> out;
+ mHidUsageAccumulator.process(*rawEvent);
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
- int32_t usageCode = mCurrentHidUsage;
- mCurrentHidUsage = 0;
- if (isKeyboardOrGamepadKey(scanCode)) {
+ if (isSupportedScanCode(scanCode)) {
out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
- scanCode, usageCode);
+ scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
}
break;
}
- case EV_MSC: {
- if (rawEvent->code == MSC_SCAN) {
- mCurrentHidUsage = rawEvent->value;
- }
- break;
- }
- case EV_SYN: {
- if (rawEvent->code == SYN_REPORT) {
- mCurrentHidUsage = 0;
- }
- }
}
return out;
}
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
- return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
- (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
- (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MUTE:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_MEDIA_SKIP_FORWARD:
- case AKEYCODE_MEDIA_SKIP_BACKWARD:
- case AKEYCODE_MEDIA_STEP_FORWARD:
- case AKEYCODE_MEDIA_STEP_BACKWARD:
- case AKEYCODE_MEDIA_AUDIO_TRACK:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_TV_AUDIO_DESCRIPTION:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
- return true;
- }
- return false;
-}
-
std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
int32_t scanCode, int32_t usageCode) {
std::list<NotifyArgs> out;
@@ -285,6 +220,7 @@
}
nsecs_t downTime = when;
+ std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode);
if (down) {
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware) {
@@ -292,11 +228,10 @@
}
// Add key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
+ if (keyDownIndex) {
// key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- downTime = mKeyDowns[keyDownIndex].downTime;
+ keyCode = mKeyDowns[*keyDownIndex].keyCode;
+ downTime = mKeyDowns[*keyDownIndex].downTime;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -315,12 +250,11 @@
}
} else {
// Remove key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
+ if (keyDownIndex) {
// key up, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- downTime = mKeyDowns[keyDownIndex].downTime;
- mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
+ keyCode = mKeyDowns[*keyDownIndex].keyCode;
+ downTime = mKeyDowns[*keyDownIndex].downTime;
+ mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
@@ -353,22 +287,22 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- getDisplayId(), policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
- downTime));
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, getDisplayId(), policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
+ downTime));
return out;
}
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) {
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
if (mKeyDowns[i].scanCode == scanCode) {
return i;
}
}
- return -1;
+ return {};
}
int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -481,12 +415,12 @@
std::list<NotifyArgs> out;
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
- out.push_back(NotifyKeyArgs(getContext()->getNextId(), when,
- systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
- getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
- mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
- mKeyDowns[i].downTime));
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
+ systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
+ getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+ mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+ mKeyDowns[i].downTime));
}
mKeyDowns.clear();
mMetaState = AMETA_NONE;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 8d72ee9..11d5ad2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,7 @@
#pragma once
+#include "HidUsageAccumulator.h"
#include "InputMapper.h"
namespace android {
@@ -23,7 +24,7 @@
class KeyboardInputMapper : public InputMapper {
public:
KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
- virtual ~KeyboardInputMapper();
+ ~KeyboardInputMapper() override = default;
uint32_t getSources() const override;
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
@@ -47,58 +48,54 @@
private:
// The current viewport.
- std::optional<DisplayViewport> mViewport;
+ std::optional<DisplayViewport> mViewport{};
struct KeyDown {
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
+ nsecs_t downTime{};
+ int32_t keyCode{};
+ int32_t scanCode{};
};
- uint32_t mSource;
- int32_t mKeyboardType;
+ uint32_t mSource{};
+ int32_t mKeyboardType{};
- std::vector<KeyDown> mKeyDowns; // keys that are down
- int32_t mMetaState;
+ std::vector<KeyDown> mKeyDowns{}; // keys that are down
+ int32_t mMetaState{};
- int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
+ HidUsageAccumulator mHidUsageAccumulator;
struct LedState {
- bool avail; // led is available
- bool on; // we think the led is currently on
+ bool avail{}; // led is available
+ bool on{}; // we think the led is currently on
};
- LedState mCapsLockLedState;
- LedState mNumLockLedState;
- LedState mScrollLockLedState;
+ LedState mCapsLockLedState{};
+ LedState mNumLockLedState{};
+ LedState mScrollLockLedState{};
// Immutable configuration parameters.
struct Parameters {
- bool orientationAware;
- bool handlesKeyRepeat;
- bool doNotWakeByDefault;
- } mParameters;
+ bool orientationAware{};
+ bool handlesKeyRepeat{};
+ bool doNotWakeByDefault{};
+ } mParameters{};
void configureParameters();
- void dumpParameters(std::string& dump);
+ void dumpParameters(std::string& dump) const;
int32_t getOrientation();
int32_t getDisplayId();
- bool isKeyboardOrGamepadKey(int32_t scanCode);
- bool isMediaKey(int32_t keyCode);
-
[[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
int32_t scanCode, int32_t usageCode);
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
- ssize_t findKeyDown(int32_t scanCode);
+ std::optional<size_t> findKeyDownIndex(int32_t scanCode);
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
- std::optional<DisplayViewport> findViewport(nsecs_t when,
- const InputReaderConfiguration* config);
+ std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
};
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 1d53eab..acba4f6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -17,8 +17,9 @@
#include "../Macros.h"
#include "MultiTouchInputMapper.h"
-
+#if defined(__ANDROID__)
#include <android/sysprop/InputProperties.sysprop.h>
+#endif
namespace android {
@@ -362,7 +363,12 @@
bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
static const bool SIMULATE_STYLUS_WITH_TOUCH =
+#if defined(__ANDROID__)
sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
+#else
+ // Disable this developer feature where sysproperties are not available
+ false;
+#endif
return SIMULATE_STYLUS_WITH_TOUCH &&
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 18a73ea..d17cdf5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -170,6 +170,7 @@
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
: InputMapper(deviceContext),
+ mTouchButtonAccumulator(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
mDisplayWidth(-1),
@@ -360,7 +361,7 @@
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDeviceContext());
- mTouchButtonAccumulator.configure(getDeviceContext());
+ mTouchButtonAccumulator.configure();
// Configure absolute axis information.
configureRawPointerAxes();
@@ -1449,7 +1450,7 @@
mCursorButtonAccumulator.reset(getDeviceContext());
mCursorScrollAccumulator.reset(getDeviceContext());
- mTouchButtonAccumulator.reset(getDeviceContext());
+ mTouchButtonAccumulator.reset();
mPointerVelocityControl.reset();
mWheelXVelocityControl.reset();
@@ -2844,54 +2845,20 @@
// Otherwise choose an arbitrary remaining pointer.
// This guarantees we always have an active touch id when there is at least one pointer.
// We keep the same active touch id for as long as possible.
- int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
- int32_t activeTouchId = lastActiveTouchId;
- if (activeTouchId < 0) {
+ if (mPointerGesture.activeTouchId < 0) {
if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
+ mPointerGesture.activeTouchId = mCurrentCookedState.fingerIdBits.firstMarkedBit();
mPointerGesture.firstTouchTime = when;
}
- } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
- if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
- } else {
- activeTouchId = mPointerGesture.activeTouchId = -1;
- }
+ } else if (!mCurrentCookedState.fingerIdBits.hasBit(mPointerGesture.activeTouchId)) {
+ mPointerGesture.activeTouchId = !mCurrentCookedState.fingerIdBits.isEmpty()
+ ? mCurrentCookedState.fingerIdBits.firstMarkedBit()
+ : -1;
}
-
- // Determine whether we are in quiet time.
- bool isQuietTime = false;
- if (activeTouchId < 0) {
- mPointerGesture.resetQuietTime();
- } else {
- isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
- if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
- mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
- mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
- currentFingerCount < 2) {
- // Enter quiet time when exiting swipe or freeform state.
- // This is to prevent accidentally entering the hover state and flinging the
- // pointer when finishing a swipe and there is still one pointer left onscreen.
- isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode ==
- PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
- currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
- // Enter quiet time when releasing the button and there are still two or more
- // fingers down. This may indicate that one finger was used to press the button
- // but it has not gone up yet.
- isQuietTime = true;
- }
- if (isQuietTime) {
- mPointerGesture.quietTime = when;
- }
- }
- }
+ const int32_t& activeTouchId = mPointerGesture.activeTouchId;
// Switch states based on button and pointer state.
- if (isQuietTime) {
+ if (checkForTouchpadQuietTime(when)) {
// Case 1: Quiet time. (QUIET)
ALOGD_IF(DEBUG_GESTURES, "Gestures: QUIET for next %0.3fms",
(mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
@@ -2931,24 +2898,9 @@
// Switch pointers if needed.
// Find the fastest pointer and follow it.
if (activeTouchId >= 0 && currentFingerCount > 1) {
- int32_t bestId = -1;
- float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- std::optional<float> vx =
- mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
- std::optional<float> vy =
- mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
- if (vx && vy) {
- float speed = hypotf(*vx, *vy);
- if (speed > bestSpeed) {
- bestId = id;
- bestSpeed = speed;
- }
- }
- }
+ const auto [bestId, bestSpeed] = getFastestFinger();
if (bestId >= 0 && bestId != activeTouchId) {
- mPointerGesture.activeTouchId = activeTouchId = bestId;
+ mPointerGesture.activeTouchId = bestId;
ALOGD_IF(DEBUG_GESTURES,
"Gestures: BUTTON_CLICK_OR_DRAG switched pointers, bestId=%d, "
"bestSpeed=%0.3f",
@@ -3114,335 +3066,7 @@
}
} else {
// Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
- // We need to provide feedback for each finger that goes down so we cannot wait
- // for the fingers to move before deciding what to do.
- //
- // The ambiguous case is deciding what to do when there are two fingers down but they
- // have not moved enough to determine whether they are part of a drag or part of a
- // freeform gesture, or just a press or long-press at the pointer location.
- //
- // When there are two fingers we start with the PRESS hypothesis and we generate a
- // down at the pointer location.
- //
- // When the two fingers move enough or when additional fingers are added, we make
- // a decision to transition into SWIPE or FREEFORM mode accordingly.
- ALOG_ASSERT(activeTouchId >= 0);
-
- bool settled = when >=
- mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
- mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
- mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
- *outFinishPreviousGesture = true;
- } else if (!settled && currentFingerCount > lastFingerCount) {
- // Additional pointers have gone down but not yet settled.
- // Reset the gesture.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: Resetting gesture since additional pointers went down for "
- "MULTITOUCH, settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime +
- mConfig.pointerGestureMultitouchSettleInterval - when) *
- 0.000001f);
- *outCancelPreviousGesture = true;
- } else {
- // Continue previous gesture.
- mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
- }
-
- if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.referenceIdBits.clear();
- mPointerVelocityControl.reset();
-
- // Use the centroid and pointer location as the reference points for the gesture.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining "
- "%0.3fms",
- (mPointerGesture.firstTouchTime +
- mConfig.pointerGestureMultitouchSettleInterval - when) *
- 0.000001f);
- mCurrentRawState.rawPointerData
- .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
- &mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
-
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
- ~mPointerGesture.referenceIdBits.value);
- !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
-
- // Add delta for all fingers and calculate a common movement delta.
- float commonDeltaX = 0, commonDeltaY = 0;
- BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
- mCurrentCookedState.fingerIdBits.value);
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
- bool first = (idBits == commonIdBits);
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
- const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
- }
-
- // Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
- float dist[MAX_POINTER_ID + 1];
- int32_t distOverThreshold = 0;
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
- if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
- distOverThreshold += 1;
- }
- }
-
- // Only transition when at least two pointers have moved further than
- // the minimum distance threshold.
- if (distOverThreshold >= 2) {
- if (currentFingerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- } else {
- // There are exactly two pointers.
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- uint32_t id1 = idBits.clearFirstMarkedBit();
- uint32_t id2 = idBits.firstMarkedBit();
- const RawPointerData::Pointer& p1 =
- mCurrentRawState.rawPointerData.pointerForId(id1);
- const RawPointerData::Pointer& p2 =
- mCurrentRawState.rawPointerData.pointerForId(id2);
- float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
- if (mutualDistance > mPointerGestureMaxSwipeWidth) {
- // There are two pointers but they are too far apart for a SWIPE,
- // switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- } else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- float dist1 = dist[id1];
- float dist2 = dist[id2];
- if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
- dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
- // Calculate the dot product of the displacement vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) *
- // mag(v2).
- PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
- PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
- float dx1 = delta1.dx * mPointerXZoomScale;
- float dy1 = delta1.dy * mPointerYZoomScale;
- float dx2 = delta2.dx * mPointerXZoomScale;
- float dy2 = delta2.dy * mPointerYZoomScale;
- float dot = dx1 * dx2 + dy1 * dy2;
- float cosine = dot / (dist1 * dist2); // denominator always > 0
- if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- }
- }
- }
- }
- }
- } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
- // Switch from SWIPE to FREEFORM if additional pointers go down.
- // Cancel previous gesture.
- if (currentFingerCount > 2) {
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- }
- }
-
- // Move the reference points based on the overall group motion of the fingers
- // except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
- (commonDeltaX || commonDeltaY)) {
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
-
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mPointerXMovementScale;
- commonDeltaY *= mPointerYMovementScale;
-
- rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
- mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
- }
-
- // Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
- // PRESS or SWIPE mode.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, "
- "currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- // FREEFORM mode.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, "
- "currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
-
- BitSet32 mappedTouchIdBits;
- BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
- // Initially, assign the active gesture id to the active touch point
- // if there is one. No other touch id bits are mapped yet.
- if (!*outCancelPreviousGesture) {
- mappedTouchIdBits.markBit(activeTouchId);
- usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
- mPointerGesture.activeGestureId;
- } else {
- mPointerGesture.activeGestureId = -1;
- }
- } else {
- // Otherwise, assume we mapped all touches from the previous frame.
- // Reuse all mappings that are still applicable.
- mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value &
- mCurrentCookedState.fingerIdBits.value;
- usedGestureIdBits = mPointerGesture.lastGestureIdBits;
-
- // Check whether we need to choose a new active gesture id because the
- // current went went up.
- for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
- ~mCurrentCookedState.fingerIdBits.value);
- !upTouchIdBits.isEmpty();) {
- uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
- uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
- if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
- mPointerGesture.activeGestureId = -1;
- break;
- }
- }
- }
-
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, "
- "usedGestureIdBits=0x%08x, activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- for (uint32_t i = 0; i < currentFingerCount; i++) {
- uint32_t touchId = idBits.clearFirstMarkedBit();
- uint32_t gestureId;
- if (!mappedTouchIdBits.hasBit(touchId)) {
- gestureId = usedGestureIdBits.markFirstUnmarkedBit();
- mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
- } else {
- gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
- }
- mPointerGesture.currentGestureIdBits.markBit(gestureId);
- mPointerGesture.currentGestureIdToIndex[gestureId] = i;
-
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(touchId);
- float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
- float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
- rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
-
- mPointerGesture.currentGestureProperties[i].clear();
- mPointerGesture.currentGestureProperties[i].id = gestureId;
- mPointerGesture.currentGestureProperties[i].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[i].clear();
- mPointerGesture.currentGestureCoords[i]
- .setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX + deltaX);
- mPointerGesture.currentGestureCoords[i]
- .setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY + deltaY);
- mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- 1.0f);
- }
-
- if (mPointerGesture.activeGestureId < 0) {
- mPointerGesture.activeGestureId =
- mPointerGesture.currentGestureIdBits.firstMarkedBit();
- ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
- mPointerGesture.activeGestureId);
- }
- }
+ prepareMultiFingerPointerGestures(when, outCancelPreviousGesture, outFinishPreviousGesture);
}
mPointerController->setButtonState(mCurrentRawState.buttonState);
@@ -3480,6 +3104,399 @@
return true;
}
+bool TouchInputMapper::checkForTouchpadQuietTime(nsecs_t when) {
+ if (mPointerGesture.activeTouchId < 0) {
+ mPointerGesture.resetQuietTime();
+ return false;
+ }
+
+ if (when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval) {
+ return true;
+ }
+
+ const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+ bool isQuietTime = false;
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
+ currentFingerCount < 2) {
+ // Enter quiet time when exiting swipe or freeform state.
+ // This is to prevent accidentally entering the hover state and flinging the
+ // pointer when finishing a swipe and there is still one pointer left onscreen.
+ isQuietTime = true;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
+ currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
+ // Enter quiet time when releasing the button and there are still two or more
+ // fingers down. This may indicate that one finger was used to press the button
+ // but it has not gone up yet.
+ isQuietTime = true;
+ }
+ if (isQuietTime) {
+ mPointerGesture.quietTime = when;
+ }
+ return isQuietTime;
+}
+
+std::pair<int32_t, float> TouchInputMapper::getFastestFinger() {
+ int32_t bestId = -1;
+ float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ std::optional<float> vx =
+ mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
+ std::optional<float> vy =
+ mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
+ if (vx && vy) {
+ float speed = hypotf(*vx, *vy);
+ if (speed > bestSpeed) {
+ bestId = id;
+ bestSpeed = speed;
+ }
+ }
+ }
+ return std::make_pair(bestId, bestSpeed);
+}
+
+void TouchInputMapper::prepareMultiFingerPointerGestures(nsecs_t when, bool* cancelPreviousGesture,
+ bool* finishPreviousGesture) {
+ // We need to provide feedback for each finger that goes down so we cannot wait for the fingers
+ // to move before deciding what to do.
+ //
+ // The ambiguous case is deciding what to do when there are two fingers down but they have not
+ // moved enough to determine whether they are part of a drag or part of a freeform gesture, or
+ // just a press or long-press at the pointer location.
+ //
+ // When there are two fingers we start with the PRESS hypothesis and we generate a down at the
+ // pointer location.
+ //
+ // When the two fingers move enough or when additional fingers are added, we make a decision to
+ // transition into SWIPE or FREEFORM mode accordingly.
+ const int32_t activeTouchId = mPointerGesture.activeTouchId;
+ ALOG_ASSERT(activeTouchId >= 0);
+
+ const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+ const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
+ bool settled =
+ when >= mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
+ *finishPreviousGesture = true;
+ } else if (!settled && currentFingerCount > lastFingerCount) {
+ // Additional pointers have gone down but not yet settled.
+ // Reset the gesture.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: Resetting gesture since additional pointers went down for "
+ "MULTITOUCH, settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+ *cancelPreviousGesture = true;
+ } else {
+ // Continue previous gesture.
+ mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+ }
+
+ if (*finishPreviousGesture || *cancelPreviousGesture) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
+ mPointerGesture.activeGestureId = 0;
+ mPointerGesture.referenceIdBits.clear();
+ mPointerVelocityControl.reset();
+
+ // Use the centroid and pointer location as the reference points for the gesture.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining "
+ "%0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+ mCurrentRawState.rawPointerData
+ .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
+ &mPointerGesture.referenceTouchY);
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ // Clear the reference deltas for fingers not yet included in the reference calculation.
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
+ ~mPointerGesture.referenceIdBits.value);
+ !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ mPointerGesture.referenceDeltas[id].dx = 0;
+ mPointerGesture.referenceDeltas[id].dy = 0;
+ }
+ mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
+
+ // Add delta for all fingers and calculate a common movement delta.
+ int32_t commonDeltaRawX = 0, commonDeltaRawY = 0;
+ BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
+ mCurrentCookedState.fingerIdBits.value);
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
+ const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx += cpd.x - lpd.x;
+ delta.dy += cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaRawX = delta.dx;
+ commonDeltaRawY = delta.dy;
+ } else {
+ commonDeltaRawX = calculateCommonVector(commonDeltaRawX, delta.dx);
+ commonDeltaRawY = calculateCommonVector(commonDeltaRawY, delta.dy);
+ }
+ }
+
+ // Consider transitions from PRESS to SWIPE or MULTITOUCH.
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
+ float dist[MAX_POINTER_ID + 1];
+ int32_t distOverThreshold = 0;
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
+ if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
+ distOverThreshold += 1;
+ }
+ }
+
+ // Only transition when at least two pointers have moved further than
+ // the minimum distance threshold.
+ if (distOverThreshold >= 2) {
+ if (currentFingerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ } else {
+ // There are exactly two pointers.
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ uint32_t id1 = idBits.clearFirstMarkedBit();
+ uint32_t id2 = idBits.firstMarkedBit();
+ const RawPointerData::Pointer& p1 =
+ mCurrentRawState.rawPointerData.pointerForId(id1);
+ const RawPointerData::Pointer& p2 =
+ mCurrentRawState.rawPointerData.pointerForId(id2);
+ float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
+ if (mutualDistance > mPointerGestureMaxSwipeWidth) {
+ // There are two pointers but they are too far apart for a SWIPE,
+ // switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ mutualDistance, mPointerGestureMaxSwipeWidth);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ float dist1 = dist[id1];
+ float dist2 = dist[id2];
+ if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
+ dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
+ // Calculate the dot product of the displacement vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approaches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) *
+ // mag(v2).
+ PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+ PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+ float dx1 = delta1.dx * mPointerXZoomScale;
+ float dy1 = delta1.dy * mPointerYZoomScale;
+ float dx2 = delta2.dx * mPointerXZoomScale;
+ float dy2 = delta2.dy * mPointerYZoomScale;
+ float dot = dx1 * dx2 + dy1 * dy2;
+ float cosine = dot / (dist1 * dist2); // denominator always > 0
+ if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ }
+ }
+ }
+ }
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ // Switch from SWIPE to FREEFORM if additional pointers go down.
+ // Cancel previous gesture.
+ if (currentFingerCount > 2) {
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ }
+ }
+
+ // Move the reference points based on the overall group motion of the fingers
+ // except in PRESS mode while waiting for a transition to occur.
+ if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
+ (commonDeltaRawX || commonDeltaRawY)) {
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx = 0;
+ delta.dy = 0;
+ }
+
+ mPointerGesture.referenceTouchX += commonDeltaRawX;
+ mPointerGesture.referenceTouchY += commonDeltaRawY;
+
+ float commonDeltaX = commonDeltaRawX * mPointerXMovementScale;
+ float commonDeltaY = commonDeltaRawY * mPointerYMovementScale;
+
+ rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
+ mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
+
+ mPointerGesture.referenceGestureX += commonDeltaX;
+ mPointerGesture.referenceGestureY += commonDeltaY;
+ }
+
+ // Report gestures.
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ // PRESS or SWIPE mode.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, "
+ "currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ float xOffset = static_cast<float>(commonDeltaRawX) /
+ (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue);
+ float yOffset = static_cast<float>(commonDeltaRawY) /
+ (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue);
+ mPointerGesture.currentGestureCoords[0]
+ .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
+ mPointerGesture.currentGestureCoords[0]
+ .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
+ // FREEFORM mode.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, "
+ "currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+
+ BitSet32 mappedTouchIdBits;
+ BitSet32 usedGestureIdBits;
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
+ // Initially, assign the active gesture id to the active touch point
+ // if there is one. No other touch id bits are mapped yet.
+ if (!*cancelPreviousGesture) {
+ mappedTouchIdBits.markBit(activeTouchId);
+ usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
+ mPointerGesture.activeGestureId;
+ } else {
+ mPointerGesture.activeGestureId = -1;
+ }
+ } else {
+ // Otherwise, assume we mapped all touches from the previous frame.
+ // Reuse all mappings that are still applicable.
+ mappedTouchIdBits.value =
+ mLastCookedState.fingerIdBits.value & mCurrentCookedState.fingerIdBits.value;
+ usedGestureIdBits = mPointerGesture.lastGestureIdBits;
+
+ // Check whether we need to choose a new active gesture id because the
+ // current went went up.
+ for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
+ ~mCurrentCookedState.fingerIdBits.value);
+ !upTouchIdBits.isEmpty();) {
+ uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
+ uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
+ if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
+ mPointerGesture.activeGestureId = -1;
+ break;
+ }
+ }
+ }
+
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value, mPointerGesture.activeGestureId);
+
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ for (uint32_t i = 0; i < currentFingerCount; i++) {
+ uint32_t touchId = idBits.clearFirstMarkedBit();
+ uint32_t gestureId;
+ if (!mappedTouchIdBits.hasBit(touchId)) {
+ gestureId = usedGestureIdBits.markFirstUnmarkedBit();
+ mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d", touchId,
+ gestureId);
+ } else {
+ gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
+ mPointerGesture.currentGestureIdBits.markBit(gestureId);
+ mPointerGesture.currentGestureIdToIndex[gestureId] = i;
+
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(touchId);
+ float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
+ float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
+
+ mPointerGesture.currentGestureProperties[i].clear();
+ mPointerGesture.currentGestureProperties[i].id = gestureId;
+ mPointerGesture.currentGestureProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[i].clear();
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX +
+ deltaX);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY +
+ deltaY);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ }
+
+ if (mPointerGesture.activeGestureId < 0) {
+ mPointerGesture.activeGestureId = mPointerGesture.currentGestureIdBits.firstMarkedBit();
+ ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
+ mPointerGesture.activeGestureId);
+ }
+ }
+}
+
void TouchInputMapper::moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId) {
const RawPointerData::Pointer& currentPointer =
mCurrentRawState.rawPointerData.pointerForId(pointerId);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7b0327e..7680090 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -773,6 +773,14 @@
bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
bool* outFinishPreviousGesture, bool isTimeout);
+ // Returns true if we're in a period of "quiet time" when touchpad gestures should be ignored.
+ bool checkForTouchpadQuietTime(nsecs_t when);
+
+ std::pair<int32_t, float> getFastestFinger();
+
+ void prepareMultiFingerPointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
+ bool* outFinishPreviousGesture);
+
// Moves the on-screen mouse pointer based on the movement of the pointer of the given ID
// between the last and current events. Uses a relative motion.
void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId);
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
new file mode 100644
index 0000000..2da1d81
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HidUsageAccumulator.h"
+
+namespace android {
+
+void HidUsageAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) {
+ mCurrentHidUsage = rawEvent.value;
+ return;
+ }
+
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ reset();
+ return;
+ }
+}
+
+int32_t HidUsageAccumulator::consumeCurrentHidUsage() {
+ const int32_t currentHidUsage = mCurrentHidUsage;
+ reset();
+ return currentHidUsage;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
new file mode 100644
index 0000000..740a710
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "EventHub.h"
+
+#include <cstdint>
+
+namespace android {
+
+/* Keeps track of the state of currently reported HID usage code. */
+class HidUsageAccumulator {
+public:
+ explicit HidUsageAccumulator() = default;
+ inline void reset() { *this = HidUsageAccumulator(); }
+
+ void process(const RawEvent& rawEvent);
+
+ /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */
+ int32_t consumeCurrentHidUsage();
+
+private:
+ int32_t mCurrentHidUsage{};
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 86153d3..5d5bee7 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -21,55 +21,39 @@
namespace android {
-TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
- clearButtons();
+void TouchButtonAccumulator::configure() {
+ mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH);
+ mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
}
-void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) {
- mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH);
- mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) ||
- deviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
- deviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
- deviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
- deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) {
- mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH);
- mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS);
+void TouchButtonAccumulator::reset() {
+ mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH);
+ mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) ||
+ mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY);
// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
- mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0);
- mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER);
- mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN);
- mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER);
- mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH);
- mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL);
- mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
- mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE);
- mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS);
- mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
- mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
- mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
- mBtnTouch = 0;
- mBtnStylus = 0;
- mBtnStylus2 = 0;
- mBtnToolFinger = 0;
- mBtnToolPen = 0;
- mBtnToolRubber = 0;
- mBtnToolBrush = 0;
- mBtnToolPencil = 0;
- mBtnToolAirbrush = 0;
- mBtnToolMouse = 0;
- mBtnToolLens = 0;
- mBtnToolDoubleTap = 0;
- mBtnToolTripleTap = 0;
- mBtnToolQuadTap = 0;
+ mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) ||
+ mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY);
+ mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER);
+ mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN);
+ mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER);
+ mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH);
+ mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL);
+ mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
+ mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE);
+ mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS);
+ mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
+ mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
+ mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+ mHidUsageAccumulator.reset();
}
void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+ mHidUsageAccumulator.process(*rawEvent);
+
if (rawEvent->type == EV_KEY) {
switch (rawEvent->code) {
case BTN_TOUCH:
@@ -116,7 +100,29 @@
case BTN_TOOL_QUADTAP:
mBtnToolQuadTap = rawEvent->value;
break;
+ default:
+ processMappedKey(rawEvent->code, rawEvent->value);
}
+ return;
+ }
+}
+
+void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) {
+ int32_t outKeyCode, outMetaState;
+ uint32_t outFlags;
+ if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(),
+ 0 /*metaState*/, &outKeyCode, &outMetaState, &outFlags) != OK) {
+ return;
+ }
+ switch (outKeyCode) {
+ case AKEYCODE_STYLUS_BUTTON_PRIMARY:
+ mBtnStylus = down;
+ break;
+ case AKEYCODE_STYLUS_BUTTON_SECONDARY:
+ mBtnStylus2 = down;
+ break;
+ default:
+ break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 7b889be..65b0a62 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -16,7 +16,8 @@
#pragma once
-#include <stdint.h>
+#include <cstdint>
+#include "HidUsageAccumulator.h"
namespace android {
@@ -26,9 +27,11 @@
/* Keeps track of the state of touch, stylus and tool buttons. */
class TouchButtonAccumulator {
public:
- TouchButtonAccumulator();
- void configure(InputDeviceContext& deviceContext);
- void reset(InputDeviceContext& deviceContext);
+ explicit TouchButtonAccumulator(InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext){};
+
+ void configure();
+ void reset();
void process(const RawEvent* rawEvent);
@@ -39,25 +42,29 @@
bool hasStylus() const;
private:
- bool mHaveBtnTouch;
- bool mHaveStylus;
+ bool mHaveBtnTouch{};
+ bool mHaveStylus{};
- bool mBtnTouch;
- bool mBtnStylus;
- bool mBtnStylus2;
- bool mBtnToolFinger;
- bool mBtnToolPen;
- bool mBtnToolRubber;
- bool mBtnToolBrush;
- bool mBtnToolPencil;
- bool mBtnToolAirbrush;
- bool mBtnToolMouse;
- bool mBtnToolLens;
- bool mBtnToolDoubleTap;
- bool mBtnToolTripleTap;
- bool mBtnToolQuadTap;
+ bool mBtnTouch{};
+ bool mBtnStylus{};
+ bool mBtnStylus2{};
+ bool mBtnToolFinger{};
+ bool mBtnToolPen{};
+ bool mBtnToolRubber{};
+ bool mBtnToolBrush{};
+ bool mBtnToolPencil{};
+ bool mBtnToolAirbrush{};
+ bool mBtnToolMouse{};
+ bool mBtnToolLens{};
+ bool mBtnToolDoubleTap{};
+ bool mBtnToolTripleTap{};
+ bool mBtnToolQuadTap{};
- void clearButtons();
+ HidUsageAccumulator mHidUsageAccumulator{};
+
+ InputDeviceContext& mDeviceContext;
+
+ void processMappedKey(int32_t scanCode, bool down);
};
} // namespace android
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 7430731..693ff06 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -23,13 +23,14 @@
cc_library_headers {
name: "libinputreporter_headers",
+ host_supported: true,
export_include_dirs: ["."],
}
filegroup {
name: "libinputreporter_sources",
srcs: [
- "InputReporter.cpp",
+ "InputReporter.cpp",
],
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index fcbb98f..b6d0709 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -23,6 +23,7 @@
cc_test {
name: "inputflinger_tests",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
// For all targets inside inputflinger, these tests build all of their sources using their
@@ -56,10 +57,36 @@
"frameworks/native/libs/input",
],
},
+ target: {
+ android: {
+ shared_libs: [
+ "libinput",
+ "libvintf",
+ ],
+ },
+ host: {
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ cflags: [
+ "-D__ANDROID_HOST__",
+ ],
+ static_libs: [
+ "libinput",
+ ],
+ },
+ },
static_libs: [
"libc++fs",
"libgmock",
],
+ shared_libs: [
+ "libinputreader",
+ ],
require_root: true,
+ test_options: {
+ unit_test: true,
+ },
test_suites: ["device-tests"],
}
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
index f8be48a..25adeea 100644
--- a/services/inputflinger/tests/AnrTracker_test.cpp
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -137,7 +137,7 @@
ASSERT_TRUE(tracker.empty());
- ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+ ASSERT_EQ(LLONG_MAX, tracker.firstTimeout());
// Can't call firstToken() if tracker.empty()
}
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 9380c71..2e296da 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -68,12 +68,18 @@
int32_t mDeviceId;
virtual void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "It's only possible to interact with uinput on device";
+#endif
mEventHub = std::make_unique<EventHub>();
consumeInitialDeviceAddedEvents();
mKeyboard = createUinputDevice<UinputHomeKey>();
ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation());
}
virtual void TearDown() override {
+#if !defined(__ANDROID__)
+ return;
+#endif
mKeyboard.reset();
waitForDeviceClose(mDeviceId);
assertNoMoreEvents();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 985c9c5..aaf50ce 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -7027,8 +7027,8 @@
break; // epoll_wait timed out
}
for (int i = 0; i < nFds; i++) {
- ASSERT_EQ(EPOLLIN, events[i].events);
- eventOrder.push_back(events[i].data.u64);
+ ASSERT_EQ(static_cast<uint32_t>(EPOLLIN), events[i].events);
+ eventOrder.push_back(static_cast<size_t>(events[i].data.u64));
channels[i]->consumeMotionDown();
}
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8ac8dfc..2142070 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -244,6 +244,7 @@
bool mInputDevicesChanged GUARDED_BY(mLock){false};
std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
+ std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
protected:
virtual ~FakeInputReaderPolicy() {}
@@ -268,6 +269,18 @@
});
}
+ void assertStylusGestureNotified(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mStylusGestureNotified);
+ ASSERT_EQ(deviceId, *mStylusGestureNotified);
+ mStylusGestureNotified.reset();
+ }
+
+ void assertStylusGestureNotNotified() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mStylusGestureNotified);
+ }
+
virtual void clearViewports() {
mViewports.clear();
mConfig.setDisplayViewports(mViewports);
@@ -428,6 +441,11 @@
ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
mInputDevicesChanged = false;
}
+
+ void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mStylusGestureNotified = deviceId;
+ }
};
// --- FakeEventHub ---
@@ -2300,6 +2318,9 @@
std::shared_ptr<FakePointerController> mFakePointerController;
void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
mFakePolicy = sp<FakeInputReaderPolicy>::make();
mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mFakePointerController);
@@ -2318,11 +2339,23 @@
}
void TearDown() override {
+#if !defined(__ANDROID__)
+ return;
+#endif
ASSERT_EQ(mReader->stop(), OK);
mReader.reset();
mTestListener.reset();
mFakePolicy.clear();
}
+
+ std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
+ const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
+ const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
+ [&name](const InputDeviceInfo& info) {
+ return info.getIdentifier().name == name;
+ });
+ return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
+ }
};
TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -2358,18 +2391,11 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
- // Find the test device by its name.
- const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
- const auto& it =
- std::find_if(inputDevices.begin(), inputDevices.end(),
- [&keyboard](const InputDeviceInfo& info) {
- return info.getIdentifier().name == keyboard->getName();
- });
-
- ASSERT_NE(it, inputDevices.end());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
- ASSERT_EQ(0U, it->getMotionRanges().size());
+ const auto device = findDeviceByName(keyboard->getName());
+ ASSERT_TRUE(device.has_value());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources());
+ ASSERT_EQ(0U, device->getMotionRanges().size());
keyboard.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2404,6 +2430,41 @@
ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
}
+TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) {
+ std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ const auto device = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(device.has_value());
+
+ // An external stylus with buttons should also be recognized as a keyboard.
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, device->getSources())
+ << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+
+ const auto DOWN =
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD));
+ const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS2);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS3);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+}
+
/**
* The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
* on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
@@ -2432,6 +2493,9 @@
const std::string UNIQUE_ID = "local:0";
void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
@@ -2441,6 +2505,9 @@
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mDevice->getName());
+ ASSERT_TRUE(info);
+ mDeviceInfo = *info;
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -2464,6 +2531,7 @@
}
std::unique_ptr<UinputTouchScreen> mDevice;
+ InputDeviceInfo mDeviceInfo;
};
TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
@@ -2680,6 +2748,72 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
+TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Send down with the pen tool selected. The policy should be notified of the stylus presence.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_PEN);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+
+ // Release the stylus touch.
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+ // Touch down with the finger, without the pen tool selected. The policy is not notified.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter.
+ // The policy should be notified of the stylus presence.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_PEN);
+ mDevice->sendMove(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+}
+
+TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) {
+ mDevice->sendKey(BTN_STYLUS, 1);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+
+ mDevice->sendKey(BTN_STYLUS, 0);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+}
+
// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
protected:
@@ -2690,6 +2824,7 @@
static const int32_t DEVICE_CONTROLLER_NUMBER;
static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
+ static const std::string DEVICE_BLUETOOTH_ADDRESS;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2706,6 +2841,7 @@
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
+ identifier.bluetoothAddress = DEVICE_BLUETOOTH_ADDRESS;
mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
identifier);
mReader->pushNextDevice(mDevice);
@@ -2727,6 +2863,7 @@
const ftl::Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
const int32_t InputDeviceTest::EVENTHUB_ID = 1;
+const std::string InputDeviceTest::DEVICE_BLUETOOTH_ADDRESS = "11:AA:22:BB:33:CC";
TEST_F(InputDeviceTest, ImmutableProperties) {
ASSERT_EQ(DEVICE_ID, mDevice->getId());
@@ -2993,6 +3130,12 @@
device.dump(dumpStr, eventHubDevStr);
}
+TEST_F(InputDeviceTest, GetBluetoothAddress) {
+ const auto& address = mReader->getBluetoothAddress(DEVICE_ID);
+ ASSERT_TRUE(address);
+ ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
+}
+
// --- InputMapperTest ---
class InputMapperTest : public testing::Test {
@@ -7195,6 +7338,7 @@
void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+ void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value);
void processMTSync(MultiTouchInputMapper& mapper);
void processSync(MultiTouchInputMapper& mapper);
};
@@ -7299,6 +7443,12 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
+void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode,
+ int32_t value) {
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value);
+}
+
void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
}
@@ -8414,6 +8564,63 @@
ASSERT_EQ(0, motionArgs.buttonState);
}
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0);
+
+ // Touch down.
+ processId(mapper, 1);
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+ // Press and release button mapped to the primary stylus button.
+ processKey(mapper, BTN_A, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ processKey(mapper, BTN_A, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Press and release the HID usage mapped to the secondary stylus button.
+ processHidUsage(mapper, 0xabcd, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+
+ processHidUsage(mapper, 0xabcd, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Release touch.
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+}
+
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -10118,6 +10325,52 @@
0, 0, 0, 0, 0));
}
+TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
+ preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Place two fingers down.
+ int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
+ ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET));
+ ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
+
+ // Move the two fingers down and to the left.
+ int32_t movingDistance = 200;
+ x1 -= movingDistance;
+ y1 += movingDistance;
+ x2 -= movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
+ ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0);
+ ASSERT_GT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET), 0);
+}
+
// --- JoystickInputMapperTest ---
class JoystickInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 29093ef..5e47b80 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -59,6 +59,12 @@
assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
}
+void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) {
+ NotifyKeyArgs outEventArgs;
+ ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs));
+ ASSERT_THAT(outEventArgs, matcher);
+}
+
void TestInputListener::assertNotifyKeyWasNotCalled() {
ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 4ad1c42..87752e1 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -44,6 +44,8 @@
void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+ void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher);
+
void assertNotifyKeyWasNotCalled();
void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index ff7455b..5107af7 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -19,10 +19,11 @@
#include <android/input.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/Input.h>
namespace android {
-MATCHER_P(WithMotionAction, action, "InputEvent with specified action") {
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
bool matches = action == arg.action;
if (!matches) {
*result_listener << "expected action " << MotionEvent::actionToString(action)
@@ -38,6 +39,12 @@
return matches;
}
+MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
+ *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
+ << KeyEvent::actionToString(arg.action);
+ return arg.action == action;
+}
+
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << source << ", but got " << arg.source;
return arg.source == source;
@@ -48,6 +55,11 @@
return arg.displayId == displayId;
}
+MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
+ *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
+ return arg.keyCode == keyCode;
+}
+
MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
const auto argX = arg.pointerCoords[0].getX();
const auto argY = arg.pointerCoords[0].getY();
@@ -62,9 +74,21 @@
return argPressure;
}
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+ const auto argToolType = arg.pointerProperties[0].toolType;
+ *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+ << motionToolTypeToString(argToolType);
+ return argToolType == toolType;
+}
+
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
*result_listener << "expected flags " << flags << ", but got " << arg.flags;
return arg.flags == flags;
}
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+ *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+ return arg.buttonState == buttons;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 9c93919..c4830dc 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -17,6 +17,7 @@
#include "UinputDevice.h"
#include <android-base/stringprintf.h>
+#include <cutils/memory.h>
#include <fcntl.h>
namespace android {
@@ -58,11 +59,11 @@
}
void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+ // uinput ignores the timestamp
struct input_event event = {};
event.type = type;
event.code = code;
event.value = value;
- event.time = {}; // uinput ignores the timestamp
if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
@@ -75,8 +76,8 @@
// --- UinputKeyboard ---
-UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
- : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+UinputKeyboard::UinputKeyboard(const char* name, std::initializer_list<int> keys)
+ : UinputDevice(name), mKeys(keys.begin(), keys.end()) {}
void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
// enable key press/release event
@@ -120,14 +121,19 @@
// --- UinputHomeKey ---
-UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+UinputHomeKey::UinputHomeKey() : UinputKeyboard("Test Uinput Home Key", {KEY_HOME}) {}
void UinputHomeKey::pressAndReleaseHomeKey() {
pressAndReleaseKey(KEY_HOME);
}
// --- UinputSteamController
-UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+UinputSteamController::UinputSteamController()
+ : UinputKeyboard("Test Uinput Steam Controller", {BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
+// --- UinputExternalStylus
+UinputExternalStylus::UinputExternalStylus()
+ : UinputKeyboard("Test Uinput External Stylus", {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
// --- UinputTouchScreen ---
UinputTouchScreen::UinputTouchScreen(const Rect* size)
@@ -146,6 +152,9 @@
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS3);
device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -157,6 +166,8 @@
device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+ device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
+ device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
}
void UinputTouchScreen::sendSlot(int32_t slot) {
@@ -195,6 +206,10 @@
injectEvent(EV_SYN, SYN_REPORT, 0);
}
+void UinputTouchScreen::sendKey(int32_t scanCode, int32_t value) {
+ injectEvent(EV_KEY, scanCode, value);
+}
+
// Get the center x, y base on the range definition.
const Point UinputTouchScreen::getCenterPoint() {
return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index e0ff8c3..53dcfd0 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -84,7 +84,7 @@
friend std::unique_ptr<D> createUinputDevice(Ts... args);
protected:
- UinputKeyboard(std::initializer_list<int> keys = {});
+ UinputKeyboard(const char* name, std::initializer_list<int> keys = {});
private:
void configureDevice(int fd, uinput_user_dev* device) override;
@@ -117,6 +117,16 @@
UinputSteamController();
};
+// A stylus that reports button presses.
+class UinputExternalStylus : public UinputKeyboard {
+public:
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+ UinputExternalStylus();
+};
+
// --- UinputTouchScreen ---
// A touch screen device with specific size.
class UinputTouchScreen : public UinputDevice {
@@ -142,6 +152,7 @@
void sendUp();
void sendToolType(int32_t toolType);
void sendSync();
+ void sendKey(int32_t scanCode, int32_t value);
const Point getCenterPoint();
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index a9f5a3a..2eed997 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -157,6 +157,10 @@
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
+ std::optional<std::string> getBluetoothAddress(int32_t deviceId) const {
+ return reader->getBluetoothAddress(deviceId);
+ }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
@@ -273,6 +277,7 @@
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
},
+ [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
})();
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bd81761..64316ba 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -315,6 +315,7 @@
return mTransform;
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+ void notifyStylusGestureStarted(int32_t, nsecs_t) {}
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -363,6 +364,7 @@
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+ void notifyStylusGestureStarted(int32_t, nsecs_t) {}
};
} // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 6fbba3f..b7de619 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -38,6 +38,8 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
"android.hardware.power-V3-cpp",
],
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 8c225d5..f89035f 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -33,16 +33,20 @@
// -------------------------------------------------------------------------------------------------
std::unique_ptr<HalWrapper> HalConnector::connect() {
- sp<IPower> halAidl = PowerHalLoader::loadAidl();
- if (halAidl) {
+ if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) {
return std::make_unique<AidlHalWrapper>(halAidl);
}
- sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
- sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
- if (halHidlV1_1) {
- return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
- }
- if (halHidlV1_0) {
+ // If V1_0 isn't defined, none of them are
+ if (sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0()) {
+ if (sp<V1_3::IPower> halHidlV1_3 = PowerHalLoader::loadHidlV1_3()) {
+ return std::make_unique<HidlHalWrapperV1_3>(halHidlV1_3);
+ }
+ if (sp<V1_2::IPower> halHidlV1_2 = PowerHalLoader::loadHidlV1_2()) {
+ return std::make_unique<HidlHalWrapperV1_2>(halHidlV1_2);
+ }
+ if (sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1()) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_1);
+ }
return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
}
return nullptr;
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 1f1b43a..6bd40f8 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "PowerHalLoader"
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
#include <binder/IServiceManager.h>
#include <hardware/power.h>
@@ -55,12 +57,16 @@
sp<IPower> PowerHalLoader::gHalAidl = nullptr;
sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
+sp<V1_3::IPower> PowerHalLoader::gHalHidlV1_3 = nullptr;
void PowerHalLoader::unloadAll() {
std::lock_guard<std::mutex> lock(gHalMutex);
gHalAidl = nullptr;
gHalHidlV1_0 = nullptr;
gHalHidlV1_1 = nullptr;
+ gHalHidlV1_2 = nullptr;
+ gHalHidlV1_3 = nullptr;
}
sp<IPower> PowerHalLoader::loadAidl() {
@@ -82,6 +88,20 @@
return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
}
+sp<V1_2::IPower> PowerHalLoader::loadHidlV1_2() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_2::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_2::IPower>(gHalExists, gHalHidlV1_2, loadFn, "HIDL v1.2");
+}
+
+sp<V1_3::IPower> PowerHalLoader::loadHidlV1_3() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_3::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_3::IPower>(gHalExists, gHalHidlV1_3, loadFn, "HIDL v1.3");
+}
+
sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
static bool gHalExists = true;
static auto loadFn = []() { return V1_0::IPower::getService(); };
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index d74bd23..9e7adf8 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -24,8 +24,6 @@
#include <cinttypes>
using namespace android::hardware::power;
-namespace V1_0 = android::hardware::power::V1_0;
-namespace V1_1 = android::hardware::power::V1_1;
namespace Aidl = android::hardware::power;
namespace android {
@@ -108,7 +106,7 @@
HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
if (boost == Boost::INTERACTION) {
- return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
} else {
ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
return HalResult<void>::unsupported();
@@ -119,13 +117,13 @@
uint32_t data = enabled ? 1 : 0;
switch (mode) {
case Mode::LAUNCH:
- return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ return sendPowerHint(V1_3::PowerHint::LAUNCH, data);
case Mode::LOW_POWER:
- return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ return sendPowerHint(V1_3::PowerHint::LOW_POWER, data);
case Mode::SUSTAINED_PERFORMANCE:
- return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data);
case Mode::VR:
- return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ return sendPowerHint(V1_3::PowerHint::VR_MODE, data);
case Mode::INTERACTIVE:
return setInteractive(enabled);
case Mode::DOUBLE_TAP_TO_WAKE:
@@ -137,8 +135,8 @@
}
}
-HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- auto ret = mHandleV1_0->powerHint(hintId, data);
+HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_0->powerHint(static_cast<V1_0::PowerHint>(hintId), data);
return HalResult<void>::fromReturn(ret);
}
@@ -152,7 +150,7 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
@@ -166,8 +164,59 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- auto ret = mHandleV1_1->powerHintAsync(hintId, data);
+HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_1::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync(static_cast<V1_0::PowerHint>(hintId), data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_2::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_2::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync_1_2(static_cast<V1_2::PowerHint>(hintId), data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) {
+ switch (boost) {
+ case Boost::CAMERA_SHOT:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs);
+ case Boost::CAMERA_LAUNCH:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs);
+ default:
+ return HidlHalWrapperV1_1::setBoost(boost, durationMs);
+ }
+}
+
+HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::CAMERA_STREAMING_SECURE:
+ case Mode::CAMERA_STREAMING_LOW:
+ case Mode::CAMERA_STREAMING_MID:
+ case Mode::CAMERA_STREAMING_HIGH:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data);
+ case Mode::AUDIO_STREAMING_LOW_LATENCY:
+ return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data);
+ default:
+ return HidlHalWrapperV1_1::setMode(mode, enabled);
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ if (mode == Mode::EXPENSIVE_RENDERING) {
+ return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data);
+ }
+ return HidlHalWrapperV1_2::setMode(mode, enabled);
+}
+
+HalResult<void> HidlHalWrapperV1_3::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_3::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync_1_3(hintId, data);
return HalResult<void>::fromReturn(ret);
}
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index fcb012f..0286a81 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,6 +38,8 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
"android.hardware.power-V3-cpp",
],
static_libs: [
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 962784c..eec6801 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -31,6 +31,8 @@
"PowerHalWrapperAidlTest.cpp",
"PowerHalWrapperHidlV1_0Test.cpp",
"PowerHalWrapperHidlV1_1Test.cpp",
+ "PowerHalWrapperHidlV1_2Test.cpp",
+ "PowerHalWrapperHidlV1_3Test.cpp",
"WorkSourceTest.cpp",
],
cflags: [
@@ -47,6 +49,8 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
"android.hardware.power-V3-cpp",
],
static_libs: [
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index 058e1b5..e36deed 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -26,6 +26,8 @@
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_2 = android::hardware::power::V1_2::IPower;
+using IPowerV1_3 = android::hardware::power::V1_3::IPower;
using IPowerAidl = android::hardware::power::IPower;
using namespace android;
@@ -52,6 +54,16 @@
return PowerHalLoader::loadHidlV1_1();
}
+template <>
+sp<IPowerV1_2> loadHal<IPowerV1_2>() {
+ return PowerHalLoader::loadHidlV1_2();
+}
+
+template <>
+sp<IPowerV1_3> loadHal<IPowerV1_3>() {
+ return PowerHalLoader::loadHidlV1_3();
+}
+
// -------------------------------------------------------------------------------------------------
template <typename T>
@@ -63,7 +75,7 @@
// -------------------------------------------------------------------------------------------------
-typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes;
TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index b54762c..0cd2e22 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -87,18 +87,28 @@
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(),
setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
@@ -131,6 +141,16 @@
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index d30e8d2..32f84e2 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -31,7 +31,6 @@
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using namespace android;
using namespace android::power;
@@ -40,15 +39,6 @@
// -------------------------------------------------------------------------------------------------
-class MockIPowerV1_0 : public IPowerV1_0 {
-public:
- MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
- MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
- MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
- MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
- (getPlatformLowPowerStats_cb _hidl_cb), (override));
-};
-
class MockIPowerV1_1 : public IPowerV1_1 {
public:
MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
@@ -69,23 +59,22 @@
protected:
std::unique_ptr<HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
- sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHal = nullptr;
};
// -------------------------------------------------------------------------------------------------
void PowerHalWrapperHidlV1_1Test::SetUp() {
- mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
- mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
- mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ mMockHal = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHal);
ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
}
// -------------------------------------------------------------------------------------------------
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
.Times(Exactly(1));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
@@ -93,7 +82,7 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
.Times(Exactly(1))
.WillRepeatedly([](PowerHint, int32_t) {
return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
@@ -104,24 +93,31 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
{
InSequence seq;
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(),
- powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_0.get(),
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
.Times(Exactly(1));
}
@@ -141,7 +137,7 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true)))
.Times(Exactly(1))
.WillRepeatedly([](PowerHint, int32_t) {
return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
@@ -152,6 +148,16 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..cf48409
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_2Test"
+
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
+using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
+
+using IPowerV1_2 = android::hardware::power::V1_2::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_2 : public IPowerV1_2 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_2Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_2>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_2>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_SHOT), Eq(500)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_LAUNCH), Eq(300)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_2, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LOW_POWER), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(true)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(false)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::AUDIO_LOW_LATENCY), Eq(true)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_2, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..2c48537
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_3Test"
+
+#include <android/hardware/power/1.3/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
+using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
+using PowerHintV1_3 = android::hardware::power::V1_3::PowerHint;
+
+using IPowerV1_3 = android::hardware::power::V1_3::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_3 : public IPowerV1_3 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_3, (PowerHintV1_3 hint, int32_t data),
+ (override));
+
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_3Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_3>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_3>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_SHOT), Eq(500)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_LAUNCH), Eq(300)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_3, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LOW_POWER), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(true)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(false)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::AUDIO_LOW_LATENCY), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::EXPENSIVE_RENDERING), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_3, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setMode(Mode::GAME, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 3de51a4..9e13849 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -69,6 +69,10 @@
case VendorAtomValue::repeatedIntValue: {
const std::optional<std::vector<int>>& repeatedIntValue =
atomValue.get<VendorAtomValue::repeatedIntValue>();
+ if (!repeatedIntValue) {
+ AStatsEvent_writeInt32Array(event, {}, 0);
+ break;
+ }
AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
repeatedIntValue->size());
break;
@@ -76,6 +80,10 @@
case VendorAtomValue::repeatedLongValue: {
const std::optional<std::vector<int64_t>>& repeatedLongValue =
atomValue.get<VendorAtomValue::repeatedLongValue>();
+ if (!repeatedLongValue) {
+ AStatsEvent_writeInt64Array(event, {}, 0);
+ break;
+ }
AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
repeatedLongValue->size());
break;
@@ -83,6 +91,10 @@
case VendorAtomValue::repeatedFloatValue: {
const std::optional<std::vector<float>>& repeatedFloatValue =
atomValue.get<VendorAtomValue::repeatedFloatValue>();
+ if (!repeatedFloatValue) {
+ AStatsEvent_writeFloatArray(event, {}, 0);
+ break;
+ }
AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
repeatedFloatValue->size());
break;
@@ -90,12 +102,18 @@
case VendorAtomValue::repeatedStringValue: {
const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
atomValue.get<VendorAtomValue::repeatedStringValue>();
+ if (!repeatedStringValue) {
+ AStatsEvent_writeStringArray(event, {}, 0);
+ break;
+ }
const std::vector<std::optional<std::string>>& repeatedStringVector =
*repeatedStringValue;
const char* cStringArray[repeatedStringVector.size()];
for (int i = 0; i < repeatedStringVector.size(); ++i) {
- cStringArray[i] = repeatedStringVector[i]->c_str();
+ cStringArray[i] = repeatedStringVector[i].has_value()
+ ? repeatedStringVector[i]->c_str()
+ : "";
}
AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
@@ -104,6 +122,10 @@
case VendorAtomValue::repeatedBoolValue: {
const std::optional<std::vector<bool>>& repeatedBoolValue =
atomValue.get<VendorAtomValue::repeatedBoolValue>();
+ if (!repeatedBoolValue) {
+ AStatsEvent_writeBoolArray(event, {}, 0);
+ break;
+ }
const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
bool boolArray[repeatedBoolValue->size()];
@@ -117,7 +139,10 @@
case VendorAtomValue::byteArrayValue: {
const std::optional<std::vector<uint8_t>>& byteArrayValue =
atomValue.get<VendorAtomValue::byteArrayValue>();
-
+ if (!byteArrayValue) {
+ AStatsEvent_writeByteArray(event, {}, 0);
+ break;
+ }
AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
break;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 8a76d7c..e76b191 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -155,6 +155,9 @@
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FrontEnd/LayerCreationArgs.cpp",
+ "FrontEnd/LayerHandle.cpp",
+ "FrontEnd/TransactionHandler.cpp",
"FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
@@ -163,6 +166,7 @@
"HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
+ "LayerFE.cpp",
"LayerProtoHelper.cpp",
"LayerRenderArea.cpp",
"LayerVector.cpp",
@@ -192,7 +196,6 @@
"Tracing/TransactionTracing.cpp",
"Tracing/TransactionProtoParser.cpp",
"TransactionCallbackInvoker.cpp",
- "TransactionHandler.cpp",
"TunnelModeEnabledReporter.cpp",
],
}
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 30b8759..7202bef 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -24,6 +24,7 @@
#include <gui/AidlStatusUtil.h>
#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
#include "SurfaceFlinger.h"
@@ -83,7 +84,8 @@
sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
static_cast<uint32_t>(flags), std::move(metadata));
- const status_t status = mFlinger->createLayer(args, parent, *outResult);
+ args.parentHandle = parent;
+ const status_t status = mFlinger->createLayer(args, *outResult);
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index b01932e..2bd8f32 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -59,16 +59,17 @@
return true;
}
-bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+base::expected<std::shared_ptr<renderengine::ExternalTexture>, ClientCache::AddError>
+ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
auto& [processToken, id] = cacheId;
if (processToken == nullptr) {
ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token");
- return false;
+ return base::unexpected(AddError::Unspecified);
}
if (!buffer) {
ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer");
- return false;
+ return base::unexpected(AddError::Unspecified);
}
std::lock_guard lock(mMutex);
@@ -81,7 +82,7 @@
token = processToken.promote();
if (!token) {
ALOGE_AND_TRACE("ClientCache::add - invalid token");
- return false;
+ return base::unexpected(AddError::Unspecified);
}
// Only call linkToDeath if not a local binder
@@ -89,7 +90,7 @@
status_t err = token->linkToDeath(mDeathRecipient);
if (err != NO_ERROR) {
ALOGE_AND_TRACE("ClientCache::add - could not link to death");
- return false;
+ return base::unexpected(AddError::Unspecified);
}
}
auto [itr, success] =
@@ -104,17 +105,17 @@
if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
ALOGE_AND_TRACE("ClientCache::add - cache is full");
- return false;
+ return base::unexpected(AddError::CacheFull);
}
LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
"Attempted to build the ClientCache before a RenderEngine instance was "
"ready!");
- processBuffers[id].buffer = std::make_shared<
- renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
- renderengine::impl::ExternalTexture::Usage::
- READABLE);
- return true;
+
+ return (processBuffers[id].buffer = std::make_shared<
+ renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
+ renderengine::impl::ExternalTexture::
+ Usage::READABLE));
}
void ClientCache::erase(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index a9b8177..cdeac2b 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -37,7 +37,10 @@
public:
ClientCache();
- bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+ enum class AddError { CacheFull, Unspecified };
+
+ base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
+ const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
void erase(const client_cache_t& cacheId);
std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index fe8cad5..608c53a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -118,6 +118,9 @@
// Requested white point of the layer in nits
const float whitePointNits;
+
+ // True if layers with 170M dataspace should be overridden to sRGB.
+ const bool treat170mAsSrgb;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5aec7c2..e309442 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -72,6 +72,7 @@
SolidColor = 1u << 16,
BackgroundBlurRadius = 1u << 17,
BlurRegions = 1u << 18,
+ HasProtectedContent = 1u << 19,
};
// clang-format on
@@ -245,9 +246,9 @@
ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
- bool isProtected() const {
- return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
- }
+ wp<GraphicBuffer> getBuffer() const { return mBuffer.get(); }
+
+ bool isProtected() const { return mIsProtected.get(); }
bool hasSolidColorCompositionType() const {
return getOutputLayer()->getLayerFE().getCompositionState()->compositionType ==
@@ -482,7 +483,11 @@
return hash;
}};
- static const constexpr size_t kNumNonUniqueFields = 17;
+ OutputLayerState<bool, LayerStateField::HasProtectedContent> mIsProtected{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 18;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -501,7 +506,7 @@
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
- &mFrameNumber,
+ &mFrameNumber, &mIsProtected,
};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 0b69d44..1c5cbed 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -248,12 +248,17 @@
return false;
}
- const TimePoint startTime = TimePoint::now();
-
// Get any composition changes requested by the HWC device, and apply them.
std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
const bool requiresClientComposition = anyLayersRequireClientComposition();
+
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ }
+
+ const TimePoint hwcValidateStartTime = TimePoint::now();
+
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
getState().earliestPresentTime,
@@ -266,8 +271,10 @@
}
if (isPowerHintSessionEnabled()) {
- mPowerAdvisor->setHwcValidateTiming(mId, startTime, TimePoint::now());
- mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+ }
}
return true;
@@ -432,13 +439,6 @@
}
impl::Output::finishFrame(refreshArgs, std::move(result));
-
- if (isPowerHintSessionEnabled()) {
- auto& hwc = getCompositionEngine().getHwComposer();
- if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
- mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
- }
- }
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e3f3680..0622534 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -900,6 +900,13 @@
ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
*outHdrDataSpace = ui::Dataspace::UNKNOWN;
+ // An Output's layers may be stale when it is disabled. As a consequence, the layers returned by
+ // getOutputLayersOrderedByZ may not be in a valid state and it is not safe to access their
+ // properties. Return a default dataspace value in this case.
+ if (!getState().isEnabled) {
+ return ui::Dataspace::V0_SRGB;
+ }
+
for (const auto* layer : getOutputLayersOrderedByZ()) {
switch (layer->getLayerFE().getCompositionState()->dataspace) {
case ui::Dataspace::V0_SCRGB:
@@ -1420,7 +1427,8 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.blurSetting = blurSetting,
- .whitePointNits = layerState.whitePointNits};
+ .whitePointNits = layerState.whitePointNits,
+ .treat170mAsSrgb = outputState.treat170mAsSrgb};
if (auto clientCompositionSettings =
layerFE.prepareClientComposition(targetSettings)) {
clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 0731d48..ed9a88d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -411,8 +411,8 @@
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
- if (auto* buffer = mLayers[0].getBuffer().get()) {
- base::StringAppendF(&result, " Buffer %p", buffer);
+ if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) {
+ base::StringAppendF(&result, " Buffer %p", buffer.get());
base::StringAppendF(&result, " Format %s",
decodePixelFormat(buffer->getPixelFormat()).c_str());
}
@@ -422,8 +422,8 @@
result.append(" Cached set of:\n");
for (const Layer& layer : mLayers) {
base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
- if (auto* buffer = layer.getBuffer().get()) {
- base::StringAppendF(&result, " Buffer %p", buffer);
+ if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) {
+ base::StringAppendF(&result, " Buffer %p", buffer.get());
base::StringAppendF(&result, " Format[%s]",
decodePixelFormat(buffer->getPixelFormat()).c_str());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9102139..758b346 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -139,6 +139,8 @@
MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
(const, override));
MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
+ MOCK_METHOD(status_t, getOverlaySupport,
+ (aidl::android::hardware::graphics::composer3::OverlayProperties*));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index eb209e9..514a8ff 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2081,6 +2081,7 @@
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ mOutput.editState().isEnabled = true;
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
.WillRepeatedly(Return(&mLayer1.mOutputLayer));
@@ -4464,6 +4465,7 @@
true /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4476,6 +4478,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -4516,6 +4519,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
@@ -4528,6 +4532,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
@@ -4540,6 +4545,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4570,6 +4576,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4582,6 +4589,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4594,6 +4602,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4624,6 +4633,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4636,6 +4646,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4648,6 +4659,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4677,6 +4689,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4689,6 +4702,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4701,6 +4715,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4728,6 +4743,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4740,6 +4756,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4752,6 +4769,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4936,6 +4954,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4954,6 +4973,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4987,6 +5007,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
LayerFE::LayerSettings mShadowSettings;
@@ -5029,6 +5050,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
diff --git a/services/surfaceflinger/Display/DisplayMap.h b/services/surfaceflinger/Display/DisplayMap.h
index baf0da9..0d59706 100644
--- a/services/surfaceflinger/Display/DisplayMap.h
+++ b/services/surfaceflinger/Display/DisplayMap.h
@@ -17,6 +17,7 @@
#pragma once
#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
namespace android::display {
@@ -28,4 +29,7 @@
template <typename Key, typename Value>
using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+template <typename T>
+using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
+
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
new file mode 100644
index 0000000..ac25fe0
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/non_null.h>
+
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::display {
+
+struct DisplayModeRequest {
+ ftl::NonNull<DisplayModePtr> modePtr;
+
+ // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
+ bool emitEvent = false;
+};
+
+inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
+ return lhs.modePtr == rhs.modePtr && lhs.emitEvent == rhs.emitEvent;
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 06a812b..7abb94b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -41,6 +41,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include "Display/DisplayModeRequest.h"
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
@@ -190,9 +191,20 @@
/* ------------------------------------------------------------------------
* Display mode management.
*/
+
+ // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest.
struct ActiveModeInfo {
+ using Event = scheduler::DisplayModeEvent;
+
+ ActiveModeInfo() = default;
+ ActiveModeInfo(DisplayModePtr mode, Event event) : mode(std::move(mode)), event(event) {}
+
+ explicit ActiveModeInfo(display::DisplayModeRequest&& request)
+ : ActiveModeInfo(std::move(request.modePtr).take(),
+ request.emitEvent ? Event::Changed : Event::None) {}
+
DisplayModePtr mode;
- scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
+ Event event = Event::None;
bool operator!=(const ActiveModeInfo& other) const {
return mode != other.mode || event != other.event;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3651231..0e41962 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -55,6 +55,7 @@
using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
@@ -503,6 +504,11 @@
return Error::NONE;
}
+Error AidlComposer::getOverlaySupport(AidlOverlayProperties* /*outProperties*/) {
+ // TODO(b/242588489): implement details
+ return Error::NONE;
+}
+
Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) {
auto fences = mReader.takeReleaseFences(translate<int64_t>(display));
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 18d2242..f2a59a5 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -48,6 +48,7 @@
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
using aidl::android::hardware::graphics::composer3::ComposerClientReader;
using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
class AidlIComposerCallbackWrapper;
@@ -103,6 +104,7 @@
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
+ Error getOverlaySupport(OverlayProperties* outProperties) override;
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index d266d94..b02f867 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -38,6 +38,7 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/common/Transform.h>
#include <optional>
@@ -281,6 +282,7 @@
virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0;
virtual Error getPhysicalDisplayOrientation(Display displayId,
AidlTransform* outDisplayOrientation) = 0;
+ virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 16d3984..a9337d8 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -40,6 +40,7 @@
using aidl::android::hardware::graphics::composer3::Composition;
using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -333,6 +334,11 @@
return Error::NONE;
}
+Error Display::getOverlaySupport(OverlayProperties* /*outProperties*/) const {
+ // TODO(b/242588489): implement details
+ return Error::NONE;
+}
+
Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
Dataspace* outDataspace,
uint8_t* outComponentMask) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 96399e2..4971d19 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -43,6 +43,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -117,6 +118,9 @@
[[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
[[nodiscard]] virtual hal::Error getHdrCapabilities(
android::HdrCapabilities* outCapabilities) const = 0;
+ [[nodiscard]] virtual hal::Error getOverlaySupport(
+ aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties)
+ const = 0;
[[nodiscard]] virtual hal::Error getDisplayedContentSamplingAttributes(
hal::PixelFormat* outFormat, hal::Dataspace* outDataspace,
uint8_t* outComponentMask) const = 0;
@@ -204,6 +208,8 @@
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+ hal::Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+ outProperties) const override;
hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
hal::Dataspace* outDataspace,
uint8_t* outComponentMask) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0a4ad97..168e2dd 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -652,6 +652,11 @@
return NO_ERROR;
}
+status_t HWComposer::getOverlaySupport(
+ aidl::android::hardware::graphics::composer3::OverlayProperties* /*outProperties*/) {
+ return NO_ERROR;
+}
+
int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 6c43d8b..8235b88 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -48,6 +48,7 @@
#include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -178,6 +179,9 @@
// Fetches the HDR capabilities of the given display
virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
+ virtual status_t getOverlaySupport(
+ aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties) = 0;
+
virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
// Returns the available RenderIntent of the given display.
@@ -362,6 +366,9 @@
// Fetches the HDR capabilities of the given display
status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
+ status_t getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+ outProperties) override;
+
int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
// Returns the available RenderIntent of the given display.
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 2597ae6..a664d2c 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -40,6 +40,7 @@
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using aidl::android::hardware::graphics::composer3::DimmingStage;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -540,6 +541,10 @@
return error;
}
+Error HidlComposer::getOverlaySupport(OverlayProperties* /*outProperties*/) {
+ return Error::NONE;
+}
+
Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) {
mReader.takeReleaseFences(display, outLayers, outReleaseFences);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d0d3c2e..b436408 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -211,6 +211,8 @@
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
+ Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+ outProperties) override;
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
new file mode 100644
index 0000000..6d492c0
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerCreationArgs.h"
+#include <binder/IPCThreadState.h>
+#include <private/android_filesystem_config.h>
+#include "Client.h"
+#include "gui/LayerMetadata.h"
+
+namespace android::surfaceflinger {
+
+std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+ uint32_t flags, gui::LayerMetadata metadataArg,
+ std::optional<uint32_t> id)
+ : flinger(flinger),
+ client(std::move(client)),
+ name(std::move(name)),
+ flags(flags),
+ metadata(std::move(metadataArg)) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ ownerPid = ipc->getCallingPid();
+ uid_t callingUid = ipc->getCallingUid();
+ metadata.setInt32(gui::METADATA_CALLING_UID, static_cast<int32_t>(callingUid));
+ ownerUid = callingUid;
+ if (ownerUid == AID_GRAPHICS || ownerUid == AID_SYSTEM) {
+ // System can override the calling UID/PID since it can create layers on behalf of apps.
+ ownerPid = metadata.getInt32(gui::METADATA_OWNER_PID, ownerPid);
+ ownerUid = static_cast<uid_t>(
+ metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid)));
+ }
+
+ if (id) {
+ sequence = *id;
+ sSequence = *id + 1;
+ } else {
+ sequence = sSequence++;
+ if (sequence == UNASSIGNED_LAYER_ID) {
+ ALOGW("Layer sequence id rolled over.");
+ sequence = sSequence++;
+ }
+ }
+}
+
+LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args)
+ : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
new file mode 100644
index 0000000..7b5a157
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Binder.h>
+#include <gui/LayerMetadata.h>
+#include <utils/StrongPointer.h>
+#include <cstdint>
+#include <limits>
+#include <optional>
+constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
+
+namespace android {
+class SurfaceFlinger;
+class Client;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+struct LayerCreationArgs {
+ static std::atomic<uint32_t> sSequence;
+
+ LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
+ uint32_t flags, gui::LayerMetadata,
+ std::optional<uint32_t> id = std::nullopt);
+ LayerCreationArgs(const LayerCreationArgs&);
+
+ android::SurfaceFlinger* flinger;
+ sp<android::Client> client;
+ std::string name;
+ uint32_t flags; // ISurfaceComposerClient flags
+ gui::LayerMetadata metadata;
+ pid_t ownerPid;
+ uid_t ownerUid;
+ uint32_t textureName;
+ uint32_t sequence;
+ bool addToRoot = true;
+ wp<IBinder> parentHandle = nullptr;
+ wp<IBinder> mirrorLayerHandle = nullptr;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
new file mode 100644
index 0000000..75e4e3a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerHandle.h"
+#include <cstdint>
+#include "Layer.h"
+#include "LayerCreationArgs.h"
+#include "SurfaceFlinger.h"
+
+namespace android::surfaceflinger {
+
+LayerHandle::LayerHandle(const sp<android::SurfaceFlinger>& flinger,
+ const sp<android::Layer>& layer)
+ : mFlinger(flinger), mLayer(layer), mLayerId(static_cast<uint32_t>(layer->getSequence())) {}
+
+LayerHandle::~LayerHandle() {
+ if (mFlinger) {
+ mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+ }
+}
+
+const String16 LayerHandle::kDescriptor = String16("android.Layer.LayerHandle");
+
+sp<LayerHandle> LayerHandle::fromIBinder(const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ return nullptr;
+ }
+
+ BBinder* b = binder->localBinder();
+ if (b == nullptr || b->getInterfaceDescriptor() != LayerHandle::kDescriptor) {
+ ALOGD("handle does not have a valid descriptor");
+ return nullptr;
+ }
+
+ // We can safely cast this binder since its local and we verified its interface descriptor.
+ return sp<LayerHandle>::cast(binder);
+}
+
+sp<android::Layer> LayerHandle::getLayer(const sp<IBinder>& binder) {
+ sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+ return handle ? handle->mLayer : nullptr;
+}
+
+uint32_t LayerHandle::getLayerId(const sp<IBinder>& binder) {
+ sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+ return handle ? handle->mLayerId : UNASSIGNED_LAYER_ID;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.h b/services/surfaceflinger/FrontEnd/LayerHandle.h
new file mode 100644
index 0000000..5d0f783
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Binder.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+class SurfaceFlinger;
+class Layer;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+/*
+ * The layer handle is just a BBinder object passed to the client
+ * (remote process) -- we don't keep any reference on our side such that
+ * the dtor is called when the remote side let go of its reference.
+ *
+ * ~LayerHandle ensures that mFlinger->onLayerDestroyed() is called for
+ * this layer when the handle is destroyed.
+ */
+class LayerHandle : public BBinder {
+public:
+ LayerHandle(const sp<android::SurfaceFlinger>& flinger, const sp<android::Layer>& layer);
+ // for testing
+ LayerHandle(uint32_t layerId) : mFlinger(nullptr), mLayer(nullptr), mLayerId(layerId) {}
+ ~LayerHandle();
+
+ // Static functions to access the layer and layer id safely from an incoming binder.
+ static sp<LayerHandle> fromIBinder(const sp<IBinder>& handle);
+ static sp<android::Layer> getLayer(const sp<IBinder>& handle);
+ static uint32_t getLayerId(const sp<IBinder>& handle);
+ static const String16 kDescriptor;
+
+ const String16& getInterfaceDescriptor() const override { return kDescriptor; }
+
+private:
+ sp<android::SurfaceFlinger> mFlinger;
+ sp<android::Layer> mLayer;
+ const uint32_t mLayerId;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
similarity index 95%
rename from services/surfaceflinger/TransactionHandler.cpp
rename to services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 6c6a487..95961fe 100644
--- a/services/surfaceflinger/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -168,15 +168,16 @@
return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
}
-void TransactionHandler::onTransactionQueueStalled(const TransactionState& transaction,
- sp<ITransactionCompletedListener>& listener) {
- if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
+void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
+ sp<ITransactionCompletedListener>& listener,
+ const std::string& reason) {
+ if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
mStalledTransactions.end()) {
return;
}
- mStalledTransactions.push_back(transaction.id);
- listener->onTransactionQueueStalled();
+ mStalledTransactions.push_back(transactionId);
+ listener->onTransactionQueueStalled(String8(reason.c_str()));
}
void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
@@ -185,4 +186,4 @@
mStalledTransactions.erase(it);
}
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
similarity index 93%
rename from services/surfaceflinger/TransactionHandler.h
rename to services/surfaceflinger/FrontEnd/TransactionHandler.h
index 237b48d..2b6f07d 100644
--- a/services/surfaceflinger/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -54,7 +54,8 @@
std::vector<TransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
void queueTransaction(TransactionState&&);
- void onTransactionQueueStalled(const TransactionState&, sp<ITransactionCompletedListener>&);
+ void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
+ const std::string& reason);
void removeFromStalledTransactions(uint64_t transactionId);
private:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9bb9305..9777092 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,7 +40,6 @@
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/TraceUtils.h>
@@ -63,12 +62,15 @@
#include <algorithm>
#include <mutex>
+#include <optional>
#include <sstream>
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "LayerProtoHelper.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
@@ -81,28 +83,8 @@
namespace {
constexpr int kDumpTableRowLength = 159;
-static constexpr float defaultMaxLuminance = 1000.0;
-
const ui::Transform kIdentityTransform;
-constexpr mat4 inverseOrientation(uint32_t transform) {
- const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
- const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- mat4 tr;
-
- if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- tr = tr * rot90;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- tr = tr * flipH;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- tr = tr * flipV;
- }
- return inverse(tr);
-}
-
bool assignTransform(ui::Transform* dst, ui::Transform& from) {
if (*dst == from) {
return false;
@@ -152,10 +134,8 @@
using PresentState = frametimeline::SurfaceFrame::PresentState;
-std::atomic<int32_t> Layer::sSequence{1};
-
Layer::Layer(const LayerCreationArgs& args)
- : sequence(args.sequence.value_or(sSequence++)),
+ : sequence(args.sequence),
mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
mClientRef(args.client),
@@ -164,7 +144,8 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
+ mHwcSlotGenerator(sp<HwcSlotGenerator>::make()),
+ mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
uint32_t layerFlags = 0;
@@ -173,9 +154,6 @@
if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
layerFlags |= layer_state_t::eLayerSkipScreenshot;
- if (args.sequence) {
- sSequence = *args.sequence + 1;
- }
mDrawingState.flags = layerFlags;
mDrawingState.crop.makeInvalid();
mDrawingState.z = 0;
@@ -220,18 +198,8 @@
mFrameTracker.setDisplayRefreshPeriod(
args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs());
- mCallingPid = args.callingPid;
- mCallingUid = args.callingUid;
-
- if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
- // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
- mOwnerUid = args.metadata.getInt32(gui::METADATA_OWNER_UID, mCallingUid);
- mOwnerPid = args.metadata.getInt32(gui::METADATA_OWNER_PID, mCallingPid);
- } else {
- // A create layer request from a non system request cannot specify the owner uid
- mOwnerUid = mCallingUid;
- mOwnerPid = mCallingPid;
- }
+ mOwnerUid = args.ownerUid;
+ mOwnerPid = args.ownerPid;
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -288,18 +256,6 @@
}
}
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
- uint32_t flags, LayerMetadata metadata)
- : flinger(flinger),
- client(std::move(client)),
- name(std::move(name)),
- flags(flags),
- metadata(std::move(metadata)) {
- IPCThreadState* ipc = IPCThreadState::self();
- callingPid = ipc->getCallingPid();
- callingUid = ipc->getCallingUid();
-}
-
// ---------------------------------------------------------------------------
// callbacks
// ---------------------------------------------------------------------------
@@ -377,7 +333,7 @@
return nullptr;
}
mGetHandleCalled = true;
- return sp<Handle>::make(mFlinger, sp<Layer>::fromExisting(this));
+ return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this));
}
// ---------------------------------------------------------------------------
@@ -652,12 +608,6 @@
snapshot->cursorFrame = frame;
}
-sp<compositionengine::LayerFE> Layer::asLayerFE() const {
- compositionengine::LayerFE* layerFE = const_cast<compositionengine::LayerFE*>(
- static_cast<const compositionengine::LayerFE*>(this));
- return sp<compositionengine::LayerFE>::fromExisting(layerFE);
-}
-
const char* Layer::getDebugName() const {
return mName.c_str();
}
@@ -666,237 +616,6 @@
// drawing...
// ---------------------------------------------------------------------------
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
- prepareClientCompositionInternal(targetSettings);
- // Nothing to render.
- if (!layerSettings) {
- return {};
- }
-
- // HWC requests to clear this layer.
- if (targetSettings.clearContent) {
- prepareClearClientComposition(*layerSettings, false /* blackout */);
- return layerSettings;
- }
-
- // set the shadow for the layer if needed
- prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
-
- return layerSettings;
-}
-
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionInternal(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
-
- const auto* snapshot = getLayerSnapshot();
- if (!snapshot) {
- return {};
- }
-
- compositionengine::LayerFE::LayerSettings layerSettings;
- layerSettings.geometry.boundaries =
- reduce(snapshot->geomLayerBounds, snapshot->transparentRegionHint);
- layerSettings.geometry.positionTransform = snapshot->geomLayerTransform.asMatrix4();
-
- // skip drawing content if the targetSettings indicate the content will be occluded
- const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
- layerSettings.skipContentDraw = !drawContent;
-
- if (hasColorTransform()) {
- layerSettings.colorTransform = snapshot->colorTransform;
- }
-
- const auto& roundedCornerState = snapshot->roundedCorner;
- layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
- layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
-
- layerSettings.alpha = snapshot->alpha;
- layerSettings.sourceDataspace = snapshot->dataspace;
-
- // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
- // We do this here instead of in buffer info so that dumpsys can still report layers that are
- // using the 170M transfer.
- if (mFlinger->mTreat170mAsSrgb &&
- (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
- HAL_DATASPACE_TRANSFER_SMPTE_170M) {
- layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
- (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
- (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
- HAL_DATASPACE_TRANSFER_SRGB);
- }
-
- layerSettings.whitePointNits = targetSettings.whitePointNits;
- switch (targetSettings.blurSetting) {
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
- layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
- layerSettings.blurRegions = snapshot->blurRegions;
- layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
- layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
- layerSettings.blurRegions = snapshot->blurRegions;
- layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
- default:
- break;
- }
- layerSettings.stretchEffect = snapshot->stretchEffect;
- // Record the name of the layer for debugging further down the stack.
- layerSettings.name = snapshot->name;
-
- if (hasEffect() && !hasBufferOrSidebandStream()) {
- prepareEffectsClientComposition(layerSettings, targetSettings);
- return layerSettings;
- }
-
- prepareBufferStateClientComposition(layerSettings, targetSettings);
- return layerSettings;
-}
-
-void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
- bool blackout) const {
- layerSettings.source.buffer.buffer = nullptr;
- layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
- layerSettings.disableBlending = true;
- layerSettings.bufferId = 0;
- layerSettings.frameNumber = 0;
-
- // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
- layerSettings.alpha = blackout ? 1.0f : 0.0f;
- layerSettings.name = getLayerSnapshot()->name;
-}
-
-void Layer::prepareEffectsClientComposition(
- compositionengine::LayerFE::LayerSettings& layerSettings,
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- // If fill bounds are occluded or the fill color is invalid skip the fill settings.
- if (targetSettings.realContentIsVisible && fillsColor()) {
- // Set color for color fill settings.
- layerSettings.source.solidColor = getColor().rgb;
- } else if (hasBlur() || drawShadows()) {
- layerSettings.skipContentDraw = true;
- }
-}
-
-void Layer::prepareBufferStateClientComposition(
- compositionengine::LayerFE::LayerSettings& layerSettings,
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
- const auto* snapshot = getLayerSnapshot();
- if (CC_UNLIKELY(!snapshot->externalTexture)) {
- // If there is no buffer for the layer or we have sidebandstream where there is no
- // activeBuffer, then we need to return LayerSettings.
- return;
- }
- const bool blackOutLayer =
- (snapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
- ((snapshot->isSecure || snapshot->hasProtectedContent) && !targetSettings.isSecure);
- const bool bufferCanBeUsedAsHwTexture =
- snapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
- if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
- ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
- snapshot->name.c_str());
- prepareClearClientComposition(layerSettings, true /* blackout */);
- return;
- }
-
- layerSettings.source.buffer.buffer = snapshot->externalTexture;
- layerSettings.source.buffer.isOpaque = snapshot->contentOpaque;
- layerSettings.source.buffer.fence = snapshot->acquireFence;
- layerSettings.source.buffer.textureName = snapshot->textureName;
- layerSettings.source.buffer.usePremultipliedAlpha = snapshot->premultipliedAlpha;
- layerSettings.source.buffer.isY410BT2020 = snapshot->isHdrY410;
- bool hasSmpte2086 = snapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
- bool hasCta861_3 = snapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
- float maxLuminance = 0.f;
- if (hasSmpte2086 && hasCta861_3) {
- maxLuminance = std::min(snapshot->hdrMetadata.smpte2086.maxLuminance,
- snapshot->hdrMetadata.cta8613.maxContentLightLevel);
- } else if (hasSmpte2086) {
- maxLuminance = snapshot->hdrMetadata.smpte2086.maxLuminance;
- } else if (hasCta861_3) {
- maxLuminance = snapshot->hdrMetadata.cta8613.maxContentLightLevel;
- } else {
- switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // Behavior-match previous releases for HDR content
- maxLuminance = defaultMaxLuminance;
- break;
- }
- }
- layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
- layerSettings.frameNumber = snapshot->frameNumber;
- layerSettings.bufferId = snapshot->externalTexture->getId();
-
- const bool useFiltering = targetSettings.needsFiltering ||
- snapshot->geomLayerTransform.needsBilinearFiltering() || snapshot->bufferNeedsFiltering;
-
- // Query the texture matrix given our current filtering mode.
- float textureMatrix[16];
- getDrawingTransformMatrix(useFiltering, textureMatrix);
-
- if (snapshot->geomBufferUsesDisplayInverseTransform) {
- /*
- * the code below applies the primary display's inverse transform to
- * the texture transform
- */
- uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
- mat4 tr = inverseOrientation(transform);
-
- /**
- * TODO(b/36727915): This is basically a hack.
- *
- * Ensure that regardless of the parent transformation,
- * this buffer is always transformed from native display
- * orientation to display orientation. For example, in the case
- * of a camera where the buffer remains in native orientation,
- * we want the pixels to always be upright.
- */
- const auto parentTransform = snapshot->transform;
- tr = tr * inverseOrientation(parentTransform.getOrientation());
-
- // and finally apply it to the original texture matrix
- const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
- memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
- }
-
- const Rect win{layerSettings.geometry.boundaries};
- float bufferWidth = snapshot->bufferSize.getWidth();
- float bufferHeight = snapshot->bufferSize.getHeight();
-
- // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
- // been set and there is no parent layer bounds. In that case, the scale is meaningless so
- // ignore them.
- if (!snapshot->bufferSize.isValid()) {
- bufferWidth = float(win.right) - float(win.left);
- bufferHeight = float(win.bottom) - float(win.top);
- }
-
- const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
- const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
- const float translateY = float(win.top) / bufferHeight;
- const float translateX = float(win.left) / bufferWidth;
-
- // Flip y-coordinates because GLConsumer expects OpenGL convention.
- mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
- mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
- mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
- mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
-
- layerSettings.source.buffer.useTextureFiltering = useFiltering;
- layerSettings.source.buffer.textureTransform =
- mat4(static_cast<const float*>(textureMatrix)) * tr;
-
- return;
-}
-
aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
@@ -1056,7 +775,7 @@
}
bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- sp<Layer> relative = fromHandle(relativeToHandle).promote();
+ sp<Layer> relative = LayerHandle::getLayer(relativeToHandle);
if (relative == nullptr) {
return false;
}
@@ -1296,8 +1015,10 @@
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
-ui::LayerStack Layer::getLayerStack() const {
- if (const auto parent = mDrawingParent.promote()) {
+ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const {
+ bool useDrawing = state == LayerVector::StateSet::Drawing;
+ const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+ if (parent) {
return parent->getLayerStack();
}
return getDrawingState().layerStack;
@@ -1551,7 +1272,6 @@
if (fps) {
surfaceFrame->setRenderRate(*fps);
}
- // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well.
onSurfaceFrameCreated(surfaceFrame);
return surfaceFrame;
}
@@ -1613,6 +1333,10 @@
return usage;
}
+void Layer::skipReportingTransformHint() {
+ mSkipReportingTransformHint = true;
+}
+
void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
transformHint = ui::Transform::ROT_0;
@@ -1755,8 +1479,8 @@
}
void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
- getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
+ StringAppendF(&result, "Layer %s (%s) ownerPid:%d ownerUid:%d\n", getName().c_str(), getType(),
+ mOwnerPid, mOwnerUid);
}
void Layer::onDisconnect() {
@@ -1822,7 +1546,7 @@
bool Layer::reparent(const sp<IBinder>& newParentHandle) {
sp<Layer> newParent;
if (newParentHandle != nullptr) {
- newParent = fromHandle(newParentHandle).promote();
+ newParent = LayerHandle::getLayer(newParentHandle);
if (newParent == nullptr) {
ALOGE("Unable to promote Layer handle");
return false;
@@ -2139,7 +1863,7 @@
return regionsCopy;
}
-Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+RoundedCornerState Layer::getRoundedCornerState() const {
// Get parent settings
RoundedCornerState parentSettings;
const auto& parent = mDrawingParent.promote();
@@ -2180,21 +1904,6 @@
return {};
}
-void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
- const Rect& layerStackRect) const {
- const auto* snapshot = getLayerSnapshot();
- renderengine::ShadowSettings state = snapshot->shadowSettings;
- if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
- return;
- }
-
- // Shift the spot light x-position to the middle of the display and then
- // offset it by casting layer's screen pos.
- state.lightPos.x = (layerStackRect.width() / 2.f) - snapshot->transformedBounds.left;
- state.lightPos.y -= snapshot->transformedBounds.top;
- caster.shadow = state;
-}
-
bool Layer::findInHierarchy(const sp<Layer>& l) {
if (l == this) {
return true;
@@ -2230,7 +1939,8 @@
void Layer::setInputInfo(const WindowInfo& info) {
mDrawingState.inputInfo = info;
- mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
+ mDrawingState.touchableRegionCrop =
+ LayerHandle::getLayer(info.touchableRegionCropHandle.promote());
mDrawingState.modified = true;
mFlinger->mUpdateInputInfo = true;
setTransactionFlags(eTransactionNeeded);
@@ -2877,23 +2587,6 @@
mFlinger->mNumClones++;
}
-const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
-
-wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
- if (handleBinder == nullptr) {
- return nullptr;
- }
-
- BBinder* b = handleBinder->localBinder();
- if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
- return nullptr;
- }
-
- // We can safely cast this binder since its local and we verified its interface descriptor.
- sp<Handle> handle = sp<Handle>::cast(handleBinder);
- return handle->owner;
-}
-
bool Layer::setDropInputMode(gui::DropInputMode mode) {
if (mDrawingState.dropInputMode == mode) {
return false;
@@ -2973,6 +2666,10 @@
void Layer::onSurfaceFrameCreated(
const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ if (!hasBufferOrSidebandStreamInDrawing()) {
+ return;
+ }
+
while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
// Too many SurfaceFrames pending classification. The front of the deque is probably not
// tracked by FrameTimeline and will never be presented. This will only result in a memory
@@ -2988,7 +2685,9 @@
void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
- handle->transformHint = mTransformHint;
+ handle->transformHint = mSkipReportingTransformHint
+ ? std::nullopt
+ : std::make_optional<uint32_t>(mTransformHint);
handle->dequeueReadyTime = dequeueReadyTime;
handle->currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
@@ -3377,7 +3076,7 @@
return fenceSignaled;
}
-bool Layer::onPreComposition(nsecs_t refreshStartTime, bool /* updatingOutputGeometryThisFrame */) {
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->refreshStartTime = refreshStartTime;
}
@@ -3788,22 +3487,19 @@
mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
}
-sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
// There's no need to get a CE Layer if the layer isn't going to draw anything.
- if (hasSomethingToDraw()) {
- return asLayerFE();
- } else {
- return nullptr;
- }
+ return hasSomethingToDraw() ? mLayerFE : nullptr;
}
-const Layer::LayerSnapshot* Layer::getLayerSnapshot() const {
+const LayerSnapshot* Layer::getLayerSnapshot() const {
return mSnapshot.get();
}
-Layer::LayerSnapshot* Layer::editLayerSnapshot() {
+LayerSnapshot* Layer::editLayerSnapshot() {
return mSnapshot.get();
}
+
const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
return mSnapshot.get();
}
@@ -4154,22 +3850,12 @@
return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
-void Layer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const {
- sp<GraphicBuffer> buffer = getBuffer();
- if (!buffer) {
- ALOGE("Buffer should not be null!");
- return;
- }
- GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
- buffer->getPixelFormat(), mBufferInfo.mCrop,
- mBufferInfo.mTransform, filteringEnabled);
-}
-
void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
mTransformHint = getFixedTransformHint();
if (mTransformHint == ui::Transform::ROT_INVALID) {
mTransformHint = displayTransformHint;
}
+ mSkipReportingTransformHint = false;
}
const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
@@ -4240,9 +3926,17 @@
}
snapshot->bufferSize = getBufferSize(mDrawingState);
snapshot->externalTexture = mBufferInfo.mBuffer;
+ snapshot->hasReadyFrame = hasReadyFrame();
preparePerFrameCompositionState();
}
+void Layer::updateChildrenSnapshots(bool updateGeometry) {
+ for (const sp<Layer>& child : mDrawingChildren) {
+ child->updateSnapshot(updateGeometry);
+ child->updateChildrenSnapshots(updateGeometry);
+ }
+}
+
void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
mSnapshot->layerMetadata = parentMetadata;
mSnapshot->layerMetadata.merge(mDrawingState.metadata);
@@ -4275,6 +3969,28 @@
}
}
+LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
+ if (mLayer) {
+ mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
+ }
+}
+
+LayerSnapshotGuard::~LayerSnapshotGuard() {
+ if (mLayer) {
+ mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot);
+ }
+}
+
+LayerSnapshotGuard::LayerSnapshotGuard(LayerSnapshotGuard&& other) : mLayer(other.mLayer) {
+ other.mLayer = nullptr;
+}
+
+LayerSnapshotGuard& LayerSnapshotGuard::operator=(LayerSnapshotGuard&& other) {
+ mLayer = other.mLayer;
+ other.mLayer = nullptr;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8ace812..a3c4e59 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -52,6 +52,7 @@
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
#include "HwcSlotGenerator.h"
+#include "LayerFE.h"
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
@@ -81,24 +82,7 @@
class SurfaceFrame;
} // namespace frametimeline
-struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
-
- SurfaceFlinger* flinger;
- const sp<Client> client;
- std::string name;
- uint32_t flags;
- LayerMetadata metadata;
-
- pid_t callingPid;
- uid_t callingUid;
- uint32_t textureName;
- std::optional<uint32_t> sequence = std::nullopt;
- bool addToRoot = true;
-};
-
-class Layer : public virtual RefBase, compositionengine::LayerFE {
- static std::atomic<int32_t> sSequence;
+class Layer : public virtual RefBase {
// The following constants represent priority of the window. SF uses this information when
// deciding which window has a priority when deciding about the refresh rate of the screen.
// Priority 0 is considered the highest priority. -1 means that the priority is unset.
@@ -129,43 +113,6 @@
inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
};
- struct RoundedCornerState {
- RoundedCornerState() = default;
- RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
- : cropRect(cropRect), radius(radius) {}
-
- // Rounded rectangle in local layer coordinate space.
- FloatRect cropRect = FloatRect();
- // Radius of the rounded rectangle.
- vec2 radius;
- bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
- };
-
- // LayerSnapshot stores Layer state used by Composition Engine and Render Engine. Composition
- // Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
- // passed to Render Engine are created using properties stored on this struct.
- //
- // TODO(b/238781169) Implement LayerFE as a separate subclass. Migrate LayerSnapshot to that
- // LayerFE subclass.
- struct LayerSnapshot : public compositionengine::LayerFECompositionState {
- int32_t sequence;
- std::string name;
- uint32_t textureName;
- bool contentOpaque;
- RoundedCornerState roundedCorner;
- StretchEffect stretchEffect;
- FloatRect transformedBounds;
- renderengine::ShadowSettings shadowSettings;
- bool premultipliedAlpha;
- bool isHdrY410;
- bool bufferNeedsFiltering;
- ui::Transform transform;
- Rect bufferSize;
- std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- LayerMetadata layerMetadata;
- LayerMetadata relativeLayerMetadata;
- };
-
using FrameRate = scheduler::LayerInfo::FrameRate;
using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
@@ -279,46 +226,6 @@
bool dimmingEnabled = true;
};
- /*
- * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
- * is called.
- */
- class LayerCleaner {
- sp<SurfaceFlinger> mFlinger;
- sp<Layer> mLayer;
- BBinder* mHandle;
-
- protected:
- ~LayerCleaner() {
- // destroy client resources
- mFlinger->onHandleDestroyed(mHandle, mLayer);
- }
-
- public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
- : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
- };
-
- /*
- * The layer handle is just a BBinder object passed to the client
- * (remote process) -- we don't keep any reference on our side such that
- * the dtor is called when the remote side let go of its reference.
- *
- * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
- * this layer when the handle is destroyed.
- */
- class Handle : public BBinder, public LayerCleaner {
- public:
- Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : LayerCleaner(flinger, layer, this), owner(layer) {}
- const String16& getInterfaceDescriptor() const override { return kDescriptor; }
-
- static const String16 kDescriptor;
- wp<Layer> owner;
- };
-
- static wp<Layer> fromHandle(const sp<IBinder>& handle);
-
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
@@ -373,7 +280,9 @@
virtual bool setTrustedOverlay(bool);
virtual bool setFlags(uint32_t flags, uint32_t mask);
virtual bool setLayerStack(ui::LayerStack);
- virtual ui::LayerStack getLayerStack() const;
+ virtual ui::LayerStack getLayerStack(
+ LayerVector::StateSet state = LayerVector::StateSet::Drawing) const;
+
virtual bool setMetadata(const LayerMetadata& data);
virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -413,7 +322,7 @@
ui::Dataspace getDataSpace() const;
ui::Dataspace getRequestedDataSpace() const;
- virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
+ virtual sp<LayerFE> getCompositionEngineLayerFE() const;
const LayerSnapshot* getLayerSnapshot() const;
LayerSnapshot* editLayerSnapshot();
@@ -557,7 +466,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
+ bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); }
PixelFormat getPixelFormat() const;
/**
@@ -598,24 +507,15 @@
// implements compositionengine::LayerFE
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
- // Called before composition. updatingOutputGeometryThisFrame is used by ARC++'s Layer subclass.
- bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame);
- std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const override;
+ bool onPreComposition(nsecs_t refreshStartTime);
void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
- void setWasClientComposed(const sp<Fence>& fence) override {
+ void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
mClearClientCompositionFenceOnLayerDisplayed = false;
}
- const LayerMetadata* getMetadata() const override { return &mSnapshot->layerMetadata; }
-
- const LayerMetadata* getRelativeMetadata() const override {
- return &mSnapshot->relativeLayerMetadata;
- }
-
- const char* getDebugName() const override;
+ const char* getDebugName() const;
bool setShadowRadius(float shadowRadius);
@@ -640,7 +540,7 @@
// Compute bounds for the layer and cache the results.
void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
- int32_t getSequence() const override { return sequence; }
+ int32_t getSequence() const { return sequence; }
// For tracing.
// TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -723,7 +623,7 @@
* Sets display transform hint on BufferLayerConsumer.
*/
void updateTransformHint(ui::Transform::RotationFlags);
-
+ void skipReportingTransformHint();
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
@@ -902,7 +802,7 @@
// Updates the LayerSnapshot. This must be called prior to sending layer data to
// CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
- // Layer::prepareClientComposition).
+ // LayerFE::prepareClientComposition).
//
// TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
// CompositionEngine to create a single path for composing layers.
@@ -928,7 +828,6 @@
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
- sp<compositionengine::LayerFE> asLayerFE() const;
sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
bool isClone() { return mClonedFrom != nullptr; }
bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
@@ -941,12 +840,6 @@
void addChildToDrawing(const sp<Layer>&);
void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
- // the settings clears the content with a solid black fill.
- void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
- void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
- const Rect& layerStackRect) const;
-
void prepareBasicGeometryCompositionState();
void prepareGeometryCompositionState();
void prepareCursorCompositionState();
@@ -1102,10 +995,6 @@
// Fills in the frame and transform info for the gui::WindowInfo.
void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
- // Computes the transform matrix using the setFilteringEnabled to determine whether the
- // transform matrix should be computed for use with bilinear filtering.
- void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const;
-
inline void tracePendingBufferCount(int32_t pendingBuffers);
// Latch sideband stream and returns true if the dirty region should be updated.
@@ -1129,8 +1018,6 @@
const sp<Fence>& releaseFence,
uint32_t currentMaxAcquiredBufferCount);
- std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
// Returns true if there is a valid color to fill.
bool fillsColor() const;
// Returns true if this layer has a blur value.
@@ -1140,13 +1027,13 @@
return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
}
+ bool hasBufferOrSidebandStreamInDrawing() const {
+ return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr));
+ }
+
bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
- void prepareBufferStateClientComposition(
- compositionengine::LayerFE::LayerSettings&,
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
- void prepareEffectsClientComposition(
- compositionengine::LayerFE::LayerSettings&,
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
+ void updateChildrenSnapshots(bool updateGeometry);
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
@@ -1166,11 +1053,6 @@
bool mGetHandleCalled = false;
- // Tracks the process and user id of the caller when creating this layer
- // to help debugging.
- pid_t mCallingPid;
- uid_t mCallingUid;
-
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1205,6 +1087,7 @@
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+ bool mSkipReportingTransformHint = true;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
@@ -1219,7 +1102,7 @@
std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
// An upper bound on the number of SurfaceFrames in the pending classifications deque.
- static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
+ static constexpr int kPendingClassificationMaxSurfaceFrames = 50;
const std::string mBlastTransactionName{"BufferTX - " + mName};
// This integer is incremented everytime a buffer arrives at the server for this layer,
@@ -1239,7 +1122,33 @@
sp<HwcSlotGenerator> mHwcSlotGenerator;
+ sp<LayerFE> mLayerFE;
std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
+
+ friend class LayerSnapshotGuard;
+};
+
+// LayerSnapshotGuard manages the movement of LayerSnapshot between a Layer and its corresponding
+// LayerFE. This class must be used whenever LayerFEs are passed to CompositionEngine. Instances of
+// LayerSnapshotGuard should only be constructed on the main thread and should not be moved outside
+// the main thread.
+//
+// Moving the snapshot instead of sharing common state prevents use of LayerFE outside the main
+// thread by making errors obvious (i.e. use outside the main thread results in SEGFAULTs due to
+// nullptr dereference).
+class LayerSnapshotGuard {
+public:
+ LayerSnapshotGuard(Layer* layer) REQUIRES(kMainThreadContext);
+ ~LayerSnapshotGuard() REQUIRES(kMainThreadContext);
+
+ LayerSnapshotGuard(const LayerSnapshotGuard&) = delete;
+ LayerSnapshotGuard& operator=(const LayerSnapshotGuard&) = delete;
+
+ LayerSnapshotGuard(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
+ LayerSnapshotGuard& operator=(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
+
+private:
+ Layer* mLayer;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
new file mode 100644
index 0000000..3bdb521
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "LayerFE"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/GLConsumer.h>
+#include <gui/TraceUtils.h>
+#include <math/vec3.h>
+#include <system/window.h>
+#include <utils/Log.h>
+
+#include "DisplayDevice.h"
+#include "LayerFE.h"
+
+namespace android {
+
+namespace {
+constexpr float defaultMaxLuminance = 1000.0;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+ const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ mat4 tr;
+
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ tr = tr * rot90;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ tr = tr * flipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ tr = tr * flipV;
+ }
+ return inverse(tr);
+}
+
+FloatRect reduce(const FloatRect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ // Convert through Rect (by rounding) for lack of FloatRegion
+ return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
+// Computes the transform matrix using the setFilteringEnabled to determine whether the
+// transform matrix should be computed for use with bilinear filtering.
+void getDrawingTransformMatrix(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ Rect bufferCrop, uint32_t bufferTransform, bool filteringEnabled,
+ float outMatrix[16]) {
+ if (!buffer) {
+ ALOGE("Buffer should not be null!");
+ return;
+ }
+ GLConsumer::computeTransformMatrix(outMatrix, static_cast<float>(buffer->getWidth()),
+ static_cast<float>(buffer->getHeight()),
+ buffer->getPixelFormat(), bufferCrop, bufferTransform,
+ filteringEnabled);
+}
+
+} // namespace
+
+LayerFE::LayerFE(const std::string& name) : mName(name) {}
+
+const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const {
+ return mSnapshot.get();
+}
+
+bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) {
+ mCompositionResult.refreshStartTime = refreshStartTime;
+ return mSnapshot->hasReadyFrame;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+ prepareClientCompositionInternal(targetSettings);
+ // Nothing to render.
+ if (!layerSettings) {
+ return {};
+ }
+
+ // HWC requests to clear this layer.
+ if (targetSettings.clearContent) {
+ prepareClearClientComposition(*layerSettings, false /* blackout */);
+ return layerSettings;
+ }
+
+ // set the shadow for the layer if needed
+ prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+
+ return layerSettings;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ ATRACE_CALL();
+ compositionengine::LayerFE::LayerSettings layerSettings;
+ layerSettings.geometry.boundaries =
+ reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint);
+ layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4();
+
+ // skip drawing content if the targetSettings indicate the content will be occluded
+ const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
+ layerSettings.skipContentDraw = !drawContent;
+
+ if (!mSnapshot->colorTransformIsIdentity) {
+ layerSettings.colorTransform = mSnapshot->colorTransform;
+ }
+
+ const auto& roundedCornerState = mSnapshot->roundedCorner;
+ layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
+ layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+ layerSettings.alpha = mSnapshot->alpha;
+ layerSettings.sourceDataspace = mSnapshot->dataspace;
+
+ // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
+ // We do this here instead of in buffer info so that dumpsys can still report layers that are
+ // using the 170M transfer.
+ if (targetSettings.treat170mAsSrgb &&
+ (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
+ HAL_DATASPACE_TRANSFER_SMPTE_170M) {
+ layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
+ (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
+ (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
+ HAL_DATASPACE_TRANSFER_SRGB);
+ }
+
+ layerSettings.whitePointNits = targetSettings.whitePointNits;
+ switch (targetSettings.blurSetting) {
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
+ layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+ layerSettings.blurRegions = mSnapshot->blurRegions;
+ layerSettings.blurRegionTransform = mSnapshot->geomInverseLayerTransform.asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
+ layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
+ layerSettings.blurRegions = mSnapshot->blurRegions;
+ layerSettings.blurRegionTransform = mSnapshot->geomInverseLayerTransform.asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
+ default:
+ break;
+ }
+ layerSettings.stretchEffect = mSnapshot->stretchEffect;
+ // Record the name of the layer for debugging further down the stack.
+ layerSettings.name = mSnapshot->name;
+
+ if (hasEffect() && !hasBufferOrSidebandStream()) {
+ prepareEffectsClientComposition(layerSettings, targetSettings);
+ return layerSettings;
+ }
+
+ prepareBufferStateClientComposition(layerSettings, targetSettings);
+ return layerSettings;
+}
+
+void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
+ bool blackout) const {
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ layerSettings.disableBlending = true;
+ layerSettings.bufferId = 0;
+ layerSettings.frameNumber = 0;
+
+ // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
+ layerSettings.alpha = blackout ? 1.0f : 0.0f;
+ layerSettings.name = mSnapshot->name;
+}
+
+void LayerFE::prepareEffectsClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+ if (targetSettings.realContentIsVisible && fillsColor()) {
+ // Set color for color fill settings.
+ layerSettings.source.solidColor = mSnapshot->color.rgb;
+ } else if (hasBlur() || drawShadows()) {
+ layerSettings.skipContentDraw = true;
+ }
+}
+
+void LayerFE::prepareBufferStateClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ ATRACE_CALL();
+ if (CC_UNLIKELY(!mSnapshot->externalTexture)) {
+ // If there is no buffer for the layer or we have sidebandstream where there is no
+ // activeBuffer, then we need to return LayerSettings.
+ return;
+ }
+ const bool blackOutLayer =
+ (mSnapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
+ ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mSnapshot->name.c_str());
+ prepareClearClientComposition(layerSettings, true /* blackout */);
+ return;
+ }
+
+ layerSettings.source.buffer.buffer = mSnapshot->externalTexture;
+ layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque;
+ layerSettings.source.buffer.fence = mSnapshot->acquireFence;
+ layerSettings.source.buffer.textureName = mSnapshot->textureName;
+ layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha;
+ layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410;
+ bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ float maxLuminance = 0.f;
+ if (hasSmpte2086 && hasCta861_3) {
+ maxLuminance = std::min(mSnapshot->hdrMetadata.smpte2086.maxLuminance,
+ mSnapshot->hdrMetadata.cta8613.maxContentLightLevel);
+ } else if (hasSmpte2086) {
+ maxLuminance = mSnapshot->hdrMetadata.smpte2086.maxLuminance;
+ } else if (hasCta861_3) {
+ maxLuminance = mSnapshot->hdrMetadata.cta8613.maxContentLightLevel;
+ } else {
+ switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Behavior-match previous releases for HDR content
+ maxLuminance = defaultMaxLuminance;
+ break;
+ }
+ }
+ layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
+ layerSettings.frameNumber = mSnapshot->frameNumber;
+ layerSettings.bufferId = mSnapshot->externalTexture->getId();
+
+ const bool useFiltering = targetSettings.needsFiltering ||
+ mSnapshot->geomLayerTransform.needsBilinearFiltering() ||
+ mSnapshot->bufferNeedsFiltering;
+
+ // Query the texture matrix given our current filtering mode.
+ float textureMatrix[16];
+ getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop,
+ mSnapshot->geomBufferTransform, useFiltering, textureMatrix);
+
+ if (mSnapshot->geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to
+ * the texture transform
+ */
+ uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ mat4 tr = inverseOrientation(transform);
+
+ /**
+ * TODO(b/36727915): This is basically a hack.
+ *
+ * Ensure that regardless of the parent transformation,
+ * this buffer is always transformed from native display
+ * orientation to display orientation. For example, in the case
+ * of a camera where the buffer remains in native orientation,
+ * we want the pixels to always be upright.
+ */
+ const auto parentTransform = mSnapshot->transform;
+ tr = tr * inverseOrientation(parentTransform.getOrientation());
+
+ // and finally apply it to the original texture matrix
+ const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+ memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+ }
+
+ const Rect win{layerSettings.geometry.boundaries};
+ float bufferWidth = static_cast<float>(mSnapshot->bufferSize.getWidth());
+ float bufferHeight = static_cast<float>(mSnapshot->bufferSize.getHeight());
+
+ // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+ // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+ // ignore them.
+ if (!mSnapshot->bufferSize.isValid()) {
+ bufferWidth = float(win.right) - float(win.left);
+ bufferHeight = float(win.bottom) - float(win.top);
+ }
+
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+ mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+ mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+ layerSettings.source.buffer.useTextureFiltering = useFiltering;
+ layerSettings.source.buffer.textureTransform =
+ mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+ return;
+}
+
+void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+ const Rect& layerStackRect) const {
+ renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+ if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
+ return;
+ }
+
+ // Shift the spot light x-position to the middle of the display and then
+ // offset it by casting layer's screen pos.
+ state.lightPos.x =
+ (static_cast<float>(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left;
+ state.lightPos.y -= mSnapshot->transformedBounds.top;
+ caster.shadow = state;
+}
+
+void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+ mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult));
+}
+
+CompositionResult&& LayerFE::stealCompositionResult() {
+ return std::move(mCompositionResult);
+}
+
+const char* LayerFE::getDebugName() const {
+ return mName.c_str();
+}
+
+const LayerMetadata* LayerFE::getMetadata() const {
+ return &mSnapshot->layerMetadata;
+}
+
+const LayerMetadata* LayerFE::getRelativeMetadata() const {
+ return &mSnapshot->relativeLayerMetadata;
+}
+
+int32_t LayerFE::getSequence() const {
+ return mSnapshot->sequence;
+}
+
+bool LayerFE::hasRoundedCorners() const {
+ return mSnapshot->roundedCorner.hasRoundedCorners();
+}
+
+void LayerFE::setWasClientComposed(const sp<Fence>& fence) {
+ mCompositionResult.lastClientCompositionFence = fence;
+}
+
+bool LayerFE::hasBufferOrSidebandStream() const {
+ return mSnapshot->externalTexture || mSnapshot->sidebandStream;
+}
+
+bool LayerFE::fillsColor() const {
+ return mSnapshot->color.r >= 0.0_hf && mSnapshot->color.g >= 0.0_hf &&
+ mSnapshot->color.b >= 0.0_hf;
+}
+
+bool LayerFE::hasBlur() const {
+ return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0;
+}
+
+bool LayerFE::drawShadows() const {
+ return mSnapshot->shadowSettings.length > 0.f &&
+ (mSnapshot->shadowSettings.ambientColor.a > 0 ||
+ mSnapshot->shadowSettings.spotColor.a > 0);
+};
+
+const sp<GraphicBuffer> LayerFE::getBuffer() const {
+ return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
new file mode 100644
index 0000000..e4f6889
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/LayerMetadata.h>
+
+#include "compositionengine/LayerFE.h"
+#include "compositionengine/LayerFECompositionState.h"
+#include "renderengine/LayerSettings.h"
+
+namespace android {
+struct RoundedCornerState {
+ RoundedCornerState() = default;
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
+ : cropRect(cropRect), radius(radius) {}
+
+ // Rounded rectangle in local layer coordinate space.
+ FloatRect cropRect = FloatRect();
+ // Radius of the rounded rectangle.
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
+};
+
+// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
+// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
+// passed to Render Engine are created using properties stored on this struct.
+struct LayerSnapshot : public compositionengine::LayerFECompositionState {
+ int32_t sequence;
+ std::string name;
+ uint32_t textureName;
+ bool contentOpaque;
+ RoundedCornerState roundedCorner;
+ StretchEffect stretchEffect;
+ FloatRect transformedBounds;
+ renderengine::ShadowSettings shadowSettings;
+ bool premultipliedAlpha;
+ bool isHdrY410;
+ bool bufferNeedsFiltering;
+ ui::Transform transform;
+ Rect bufferSize;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ gui::LayerMetadata layerMetadata;
+ gui::LayerMetadata relativeLayerMetadata;
+ bool contentDirty;
+ bool hasReadyFrame;
+};
+
+struct CompositionResult {
+ // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
+ // and remove this field.
+ nsecs_t refreshStartTime = 0;
+ std::vector<ftl::SharedFuture<FenceResult>> releaseFences;
+ sp<Fence> lastClientCompositionFence = nullptr;
+};
+
+class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
+public:
+ LayerFE(const std::string& name);
+
+ // compositionengine::LayerFE overrides
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+ const char* getDebugName() const override;
+ int32_t getSequence() const override;
+ bool hasRoundedCorners() const override;
+ void setWasClientComposed(const sp<Fence>&) override;
+ const gui::LayerMetadata* getMetadata() const override;
+ const gui::LayerMetadata* getRelativeMetadata() const override;
+ std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ CompositionResult&& stealCompositionResult();
+
+ std::unique_ptr<LayerSnapshot> mSnapshot;
+
+private:
+ std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+ // the settings clears the content with a solid black fill.
+ void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+ void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+ const Rect& layerStackRect) const;
+ void prepareBufferStateClientComposition(
+ compositionengine::LayerFE::LayerSettings&,
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ void prepareEffectsClientComposition(
+ compositionengine::LayerFE::LayerSettings&,
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
+ bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+ bool hasBufferOrSidebandStream() const;
+
+ bool fillsColor() const;
+ bool hasBlur() const;
+ bool drawShadows() const;
+
+ const sp<GraphicBuffer> getBuffer() const;
+
+ CompositionResult mCompositionResult;
+ std::string mName;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 6bc7dc1..554fae4 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -18,6 +18,7 @@
#include <ui/Transform.h>
#include "DisplayDevice.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
#include "LayerRenderArea.h"
#include "SurfaceFlinger.h"
@@ -30,6 +31,7 @@
// Compute and cache the bounds for the new parent layer.
newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
0.f /* shadowRadius */);
+ newParent->updateSnapshot(true /* updateGeometry */);
oldParent->setChildrenDrawingParent(newParent);
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 30483a2..39850c7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -23,6 +23,7 @@
#include <chrono>
#include <cmath>
+#include <deque>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -143,8 +144,7 @@
ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
- constexpr float kEpsilon = 0.0001f;
- if (std::abs(overallScore - rhs.overallScore) > kEpsilon) {
+ if (!ScoredRefreshRate::scoresEqual(overallScore, rhs.overallScore)) {
return overallScore > rhs.overallScore;
}
@@ -288,8 +288,7 @@
}
auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+ GlobalSignals signals) const -> RankedRefreshRates {
std::lock_guard lock(mLock);
if (mGetRankedRefreshRatesCache &&
@@ -304,7 +303,7 @@
auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+ -> RankedRefreshRates {
using namespace fps_approx_ops;
ATRACE_CALL();
ALOGV("%s: %zu layers", __func__, layers.size());
@@ -314,8 +313,7 @@
// Keep the display at max refresh rate for the duration of powering on the display.
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending,
- /*preferredDisplayModeOpt*/ std::nullopt),
+ return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Descending),
GlobalSignals{.powerOnImminent = true}};
}
@@ -375,8 +373,7 @@
// selected a refresh rate to see if we should apply touch boost.
if (signals.touch && !hasExplicitVoteLayers) {
ALOGV("Touch Boost");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
- /*preferredDisplayModeOpt*/ std::nullopt),
+ return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending),
GlobalSignals{.touch = true}};
}
@@ -388,24 +385,19 @@
if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
ALOGV("Idle");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
- /*preferredDisplayModeOpt*/ std::nullopt),
+ return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
GlobalSignals{.idle = true}};
}
if (layers.empty() || noVoteLayers == layers.size()) {
ALOGV("No layers with votes");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
- /*preferredDisplayModeOpt*/ std::nullopt),
- kNoSignals};
+ return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
}
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("All layers Min");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
- /*preferredDisplayModeOpt*/ std::nullopt),
- kNoSignals};
+ return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
}
// Find the best refresh rate based on score
@@ -557,12 +549,13 @@
maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
std::sort(scores.begin(), scores.end(),
RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
- std::vector<RefreshRateRanking> rankedRefreshRates;
- rankedRefreshRates.reserve(scores.size());
- std::transform(scores.begin(), scores.end(), back_inserter(rankedRefreshRates),
+ RefreshRateRanking ranking;
+ ranking.reserve(scores.size());
+
+ std::transform(scores.begin(), scores.end(), back_inserter(ranking),
[](const RefreshRateScore& score) {
- return RefreshRateRanking{score.modeIt->second, score.overallScore};
+ return ScoredRefreshRate{score.modeIt->second, score.overallScore};
});
const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
@@ -574,11 +567,9 @@
// range instead of picking a random score from the app range.
if (noLayerScore) {
ALOGV("Layers not scored");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
- /*preferredDisplayModeOpt*/ std::nullopt),
- kNoSignals};
+ return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
} else {
- return {rankedRefreshRates, kNoSignals};
+ return {ranking, kNoSignals};
}
}
@@ -596,14 +587,12 @@
}
}();
- const auto& touchRefreshRates =
- getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
- /*preferredDisplayModeOpt*/ std::nullopt);
+ const auto touchRefreshRates = rankRefreshRates(anchorGroup, RefreshRateOrder::Descending);
+
using fps_approx_ops::operator<;
if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- scores.front().modeIt->second->getFps() <
- touchRefreshRates.front().displayModePtr->getFps()) {
+ scores.front().modeIt->second->getFps() < touchRefreshRates.front().modePtr->getFps()) {
ALOGV("Touch Boost");
return {touchRefreshRates, GlobalSignals{.touch = true}};
}
@@ -612,12 +601,11 @@
// current config
if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
const auto preferredDisplayMode = activeMode.getId();
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Ascending,
- preferredDisplayMode),
+ return {rankRefreshRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
kNoSignals};
}
- return {rankedRefreshRates, kNoSignals};
+ return {ranking, kNoSignals};
}
std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -783,11 +771,12 @@
return mPrimaryRefreshRates.back()->second;
}
-std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
+auto RefreshRateConfigs::rankRefreshRates(
std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const {
- std::deque<RefreshRateRanking> rankings;
- const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
+ std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking {
+ std::deque<ScoredRefreshRate> ranking;
+
+ const auto rankRefreshRate = [&](DisplayModeIterator it) REQUIRES(mLock) {
const auto& mode = it->second;
if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
return;
@@ -800,31 +789,32 @@
}
if (preferredDisplayModeOpt) {
if (*preferredDisplayModeOpt == mode->getId()) {
- rankings.push_front(RefreshRateRanking{mode, /*score*/ 1.0f});
+ constexpr float kScore = std::numeric_limits<float>::max();
+ ranking.push_front(ScoredRefreshRate{mode, kScore});
return;
}
constexpr float kNonPreferredModePenalty = 0.95f;
score *= kNonPreferredModePenalty;
}
- rankings.push_back(RefreshRateRanking{mode, score});
+ ranking.push_back(ScoredRefreshRate{mode, score});
};
if (refreshRateOrder == RefreshRateOrder::Ascending) {
- std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), makeRanking);
+ std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), rankRefreshRate);
} else {
- std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), makeRanking);
+ std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), rankRefreshRate);
}
- if (!rankings.empty() || !anchorGroupOpt) {
- return {rankings.begin(), rankings.end()};
+ if (!ranking.empty() || !anchorGroupOpt) {
+ return {ranking.begin(), ranking.end()};
}
ALOGW("Can't find %s refresh rate by policy with the same mode group"
" as the mode group %d",
refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
- return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder,
- preferredDisplayModeOpt);
+ constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
+ return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
}
DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 7219584..99f81aa 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -23,6 +23,7 @@
#include <utility>
#include <variant>
+#include <ftl/concat.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
@@ -46,15 +47,6 @@
return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
-struct RefreshRateRanking {
- DisplayModePtr displayModePtr;
- float score = 0.0f;
-
- bool operator==(const RefreshRateRanking& ranking) const {
- return displayModePtr == ranking.displayModePtr && score == ranking.score;
- }
-};
-
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
/**
@@ -208,12 +200,46 @@
return touch == other.touch && idle == other.idle &&
powerOnImminent == other.powerOnImminent;
}
+
+ auto toString() const {
+ return ftl::Concat("{touch=", touch, ", idle=", idle,
+ ", powerOnImminent=", powerOnImminent, '}');
+ }
};
- // Returns the list in the descending order of refresh rates desired
- // based on their overall score, and the GlobalSignals that were considered.
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRates(
- const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
+ struct ScoredRefreshRate {
+ DisplayModePtr modePtr;
+ float score = 0.0f;
+
+ bool operator==(const ScoredRefreshRate& other) const {
+ return modePtr == other.modePtr && score == other.score;
+ }
+
+ static bool scoresEqual(float lhs, float rhs) {
+ constexpr float kEpsilon = 0.0001f;
+ return std::abs(lhs - rhs) <= kEpsilon;
+ }
+
+ struct DescendingScore {
+ bool operator()(const ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const {
+ return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
+ }
+ };
+ };
+
+ using RefreshRateRanking = std::vector<ScoredRefreshRate>;
+
+ struct RankedRefreshRates {
+ RefreshRateRanking ranking; // Ordered by descending score.
+ GlobalSignals consideredSignals;
+
+ bool operator==(const RankedRefreshRates& other) const {
+ return ranking == other.ranking && consideredSignals == other.consideredSignals;
+ }
+ };
+
+ RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&,
+ GlobalSignals) const EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
@@ -354,8 +380,8 @@
// See mActiveModeIt for thread safety.
DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRatesLocked(
- const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
+ RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&,
+ GlobalSignals) const REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -373,11 +399,10 @@
enum class RefreshRateOrder { Ascending, Descending };
- // Returns the rankings in RefreshRateOrder. May change at runtime.
// Only uses the primary range, not the app request range.
- std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(
- std::optional<int> anchorGroupOpt, RefreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const REQUIRES(mLock);
+ RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt =
+ std::nullopt) const REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
@@ -436,7 +461,7 @@
struct GetRankedRefreshRatesCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> result;
+ RankedRefreshRates result;
};
mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 12949d6..be3ebb7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -26,6 +26,7 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <ftl/fake_guard.h>
+#include <ftl/small_map.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <utils/Timers.h>
@@ -41,6 +42,7 @@
#include "../Layer.h"
#include "DispSyncSource.h"
+#include "Display/DisplayMap.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "OneShotTimer.h"
@@ -56,39 +58,6 @@
} \
} while (false)
-namespace {
-
-using android::Fps;
-using android::FpsApproxEqual;
-using android::FpsHash;
-using android::scheduler::AggregatedFpsScore;
-using android::scheduler::RefreshRateRankingsAndSignals;
-
-// Returns the aggregated score per Fps for the RefreshRateRankingsAndSignals sourced.
-auto getAggregatedScoresPerFps(
- const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
- -> std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> {
- std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps;
-
- for (const auto& refreshRateRankingsAndSignal : refreshRateRankingsAndSignalsPerDisplay) {
- const auto& refreshRateRankings = refreshRateRankingsAndSignal.refreshRateRankings;
-
- std::for_each(refreshRateRankings.begin(), refreshRateRankings.end(), [&](const auto& it) {
- const auto [score, result] =
- aggregatedScoresPerFps.try_emplace(it.displayModePtr->getFps(),
- AggregatedFpsScore{it.score,
- /* numDisplays */ 1});
- if (!result) { // update
- score->second.totalScore += it.score;
- score->second.numDisplays++;
- }
- });
- }
- return aggregatedScoresPerFps;
-}
-
-} // namespace
-
namespace android::scheduler {
Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
@@ -157,6 +126,23 @@
mRefreshRateConfigs->startIdleTimer();
}
+void Scheduler::registerDisplay(sp<const DisplayDevice> display) {
+ if (display->isPrimary()) {
+ mLeaderDisplayId = display->getPhysicalId();
+ }
+
+ const bool ok = mDisplays.try_emplace(display->getPhysicalId(), std::move(display)).second;
+ ALOGE_IF(!ok, "%s: Duplicate display", __func__);
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+ if (mLeaderDisplayId == displayId) {
+ mLeaderDisplayId.reset();
+ }
+
+ mDisplays.erase(displayId);
+}
+
void Scheduler::run() {
while (true) {
waitMessage();
@@ -653,9 +639,8 @@
template <typename S, typename T>
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
- DisplayModePtr newMode;
+ std::vector<display::DisplayModeRequest> modeRequests;
GlobalSignals consideredSignals;
- std::vector<DisplayModeConfig> displayModeConfigs;
bool refreshRateChanged = false;
bool frameRateOverridesChanged;
@@ -668,42 +653,41 @@
if (currentState == newState) return {};
currentState = std::forward<T>(newState);
- displayModeConfigs = getBestDisplayModeConfigs();
+ auto modeChoices = chooseDisplayModes();
- // mPolicy holds the current mode, using the current mode we find out
- // what display is currently being tracked through the policy and
- // then find the DisplayModeConfig for that display. So that
- // later we check if the policy mode has changed for the same display in policy.
- // If mPolicy mode isn't available then we take the first display from the best display
- // modes as the candidate for policy changes and frame rate overrides.
- // TODO(b/240743786) Update the single display based assumptions and make mode changes
- // and mPolicy per display.
- const DisplayModeConfig& displayModeConfigForCurrentPolicy = mPolicy.mode
- ? *std::find_if(displayModeConfigs.begin(), displayModeConfigs.end(),
- [&](const auto& displayModeConfig) REQUIRES(mPolicyLock) {
- return displayModeConfig.displayModePtr
- ->getPhysicalDisplayId() ==
- mPolicy.mode->getPhysicalDisplayId();
- })
- : displayModeConfigs.front();
+ // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go
+ // through. Fix this by tracking per-display Scheduler::Policy and timers.
+ DisplayModePtr modePtr;
+ std::tie(modePtr, consideredSignals) =
+ modeChoices.get(*mLeaderDisplayId)
+ .transform([](const DisplayModeChoice& choice) {
+ return std::make_pair(choice.modePtr, choice.consideredSignals);
+ })
+ .value();
- newMode = displayModeConfigForCurrentPolicy.displayModePtr;
- consideredSignals = displayModeConfigForCurrentPolicy.signals;
- frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
+ modeRequests.reserve(modeChoices.size());
+ for (auto& [id, choice] : modeChoices) {
+ modeRequests.emplace_back(
+ display::DisplayModeRequest{.modePtr =
+ ftl::as_non_null(std::move(choice.modePtr)),
+ .emitEvent = !choice.consideredSignals.idle});
+ }
- if (mPolicy.mode == newMode) {
+ frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modePtr->getFps());
+
+ if (mPolicy.mode != modePtr) {
+ mPolicy.mode = modePtr;
+ refreshRateChanged = true;
+ } else {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed if previously considered idle.
if (!consideredSignals.idle) {
dispatchCachedReportedMode();
}
- } else {
- mPolicy.mode = newMode;
- refreshRateChanged = true;
}
}
if (refreshRateChanged) {
- mSchedulerCallback.requestDisplayModes(std::move(displayModeConfigs));
+ mSchedulerCallback.requestDisplayModes(std::move(modeRequests));
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -711,67 +695,88 @@
return consideredSignals;
}
-void Scheduler::registerDisplay(const sp<const DisplayDevice>& display) {
- const bool ok = mDisplays.try_emplace(display->getPhysicalId(), display).second;
- ALOGE_IF(!ok, "Duplicate display registered");
-}
-
-void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
- mDisplays.erase(displayId);
-}
-
-std::vector<DisplayModeConfig> Scheduler::getBestDisplayModeConfigs() const {
+auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- std::vector<RefreshRateRankingsAndSignals> refreshRateRankingsAndSignalsPerDisplay;
- refreshRateRankingsAndSignalsPerDisplay.reserve(mDisplays.size());
+ using RankedRefreshRates = RefreshRateConfigs::RankedRefreshRates;
+ display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
+
+ // Tallies the score of a refresh rate across `displayCount` displays.
+ struct RefreshRateTally {
+ explicit RefreshRateTally(float score) : score(score) {}
+
+ float score;
+ size_t displayCount = 1;
+ };
+
+ // Chosen to exceed a typical number of refresh rates across displays.
+ constexpr size_t kStaticCapacity = 8;
+ ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies;
+
+ const auto globalSignals = makeGlobalSignals();
for (const auto& [id, display] : mDisplays) {
- const auto [rankings, signals] =
+ auto rankedRefreshRates =
display->holdRefreshRateConfigs()
- ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals());
+ ->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals);
- refreshRateRankingsAndSignalsPerDisplay.emplace_back(
- RefreshRateRankingsAndSignals{rankings, signals});
+ for (const auto& [modePtr, score] : rankedRefreshRates.ranking) {
+ const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score);
+
+ if (!inserted) {
+ auto& tally = it->second;
+ tally.score += score;
+ tally.displayCount++;
+ }
+ }
+
+ perDisplayRanking.push_back(std::move(rankedRefreshRates));
}
- // FPS and their Aggregated score.
- std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps =
- getAggregatedScoresPerFps(refreshRateRankingsAndSignalsPerDisplay);
+ auto maxScoreIt = refreshRateTallies.cbegin();
- auto maxScoreIt = aggregatedScoresPerFps.cbegin();
- // Selects the max Fps that is present on all the displays.
- for (auto it = aggregatedScoresPerFps.cbegin(); it != aggregatedScoresPerFps.cend(); ++it) {
- const auto [fps, aggregatedScore] = *it;
- if (aggregatedScore.numDisplays == mDisplays.size() &&
- aggregatedScore.totalScore >= maxScoreIt->second.totalScore) {
- maxScoreIt = it;
+ // Find the first refresh rate common to all displays.
+ while (maxScoreIt != refreshRateTallies.cend() &&
+ maxScoreIt->second.displayCount != mDisplays.size()) {
+ ++maxScoreIt;
+ }
+
+ if (maxScoreIt != refreshRateTallies.cend()) {
+ // Choose the highest refresh rate common to all displays, if any.
+ for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
+ const auto [fps, tally] = *it;
+
+ if (tally.displayCount == mDisplays.size() && tally.score > maxScoreIt->second.score) {
+ maxScoreIt = it;
+ }
}
}
- return getDisplayModeConfigsForTheChosenFps(maxScoreIt->first,
- refreshRateRankingsAndSignalsPerDisplay);
-}
-std::vector<DisplayModeConfig> Scheduler::getDisplayModeConfigsForTheChosenFps(
- Fps chosenFps,
- const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
- const {
- std::vector<DisplayModeConfig> displayModeConfigs;
- displayModeConfigs.reserve(mDisplays.size());
+ const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend()
+ ? std::make_optional(maxScoreIt->first)
+ : std::nullopt;
+
+ DisplayModeChoiceMap modeChoices;
+
using fps_approx_ops::operator==;
- std::for_each(refreshRateRankingsAndSignalsPerDisplay.begin(),
- refreshRateRankingsAndSignalsPerDisplay.end(),
- [&](const auto& refreshRateRankingsAndSignal) {
- for (const auto& ranking : refreshRateRankingsAndSignal.refreshRateRankings) {
- if (ranking.displayModePtr->getFps() == chosenFps) {
- displayModeConfigs.emplace_back(
- DisplayModeConfig{refreshRateRankingsAndSignal.globalSignals,
- ranking.displayModePtr});
- break;
- }
- }
- });
- return displayModeConfigs;
+
+ for (auto& [ranking, signals] : perDisplayRanking) {
+ if (!chosenFps) {
+ auto& [modePtr, _] = ranking.front();
+ modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{std::move(modePtr), signals});
+ continue;
+ }
+
+ for (auto& [modePtr, _] : ranking) {
+ if (modePtr->getFps() == *chosenFps) {
+ modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{std::move(modePtr), signals});
+ break;
+ }
+ }
+ }
+ return modeChoices;
}
GlobalSignals Scheduler::makeGlobalSignals() const {
@@ -789,11 +794,11 @@
// Make sure the stored mode is up to date.
if (mPolicy.mode) {
const auto configs = holdRefreshRateConfigs();
- const auto rankings =
+ const auto ranking =
configs->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
- .first;
+ .ranking;
- mPolicy.mode = rankings.front().displayModePtr;
+ mPolicy.mode = ranking.front().modePtr;
}
return mPolicy.mode;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 25fa714..33f6126 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -33,11 +33,13 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-#include <DisplayDevice.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
+#include <ui/DisplayId.h>
#include "Display/DisplayMap.h"
+#include "Display/DisplayModeRequest.h"
+#include "DisplayDevice.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "LayerHistory.h"
@@ -87,20 +89,9 @@
using GlobalSignals = RefreshRateConfigs::GlobalSignals;
-// Config representing the DisplayMode and considered signals for the Display.
-struct DisplayModeConfig {
- const GlobalSignals signals;
- const DisplayModePtr displayModePtr;
-
- DisplayModeConfig(GlobalSignals signals, DisplayModePtr displayModePtr)
- : signals(signals), displayModePtr(std::move(displayModePtr)) {}
-};
-
struct ISchedulerCallback {
- using DisplayModeEvent = scheduler::DisplayModeEvent;
-
virtual void setVsyncEnabled(bool) = 0;
- virtual void requestDisplayModes(std::vector<DisplayModeConfig>) = 0;
+ virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
@@ -108,19 +99,6 @@
~ISchedulerCallback() = default;
};
-// Holds the total score of the FPS and
-// number of displays the FPS is found in.
-struct AggregatedFpsScore {
- float totalScore;
- size_t numDisplays;
-};
-
-// Represents the RefreshRateRankings and GlobalSignals for the selected RefreshRateRankings.
-struct RefreshRateRankingsAndSignals {
- std::vector<RefreshRateRanking> refreshRateRankings;
- GlobalSignals globalSignals;
-};
-
class Scheduler : android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
@@ -132,6 +110,9 @@
void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>)
EXCLUDES(mRefreshRateConfigsLock);
+ void registerDisplay(sp<const DisplayDevice>);
+ void unregisterDisplay(PhysicalDisplayId);
+
void run();
void createVsyncSchedule(FeatureFlags);
@@ -257,9 +238,6 @@
return mLayerHistory.getLayerFramerate(now, id);
}
- void registerDisplay(const sp<const DisplayDevice>&);
- void unregisterDisplay(PhysicalDisplayId);
-
private:
friend class TestableScheduler;
@@ -290,12 +268,26 @@
template <typename S, typename T>
GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
- // Returns the best display mode per display.
- std::vector<DisplayModeConfig> getBestDisplayModeConfigs() const REQUIRES(mPolicyLock);
+ struct DisplayModeChoice {
+ DisplayModeChoice(DisplayModePtr modePtr, GlobalSignals consideredSignals)
+ : modePtr(std::move(modePtr)), consideredSignals(consideredSignals) {}
- // Returns the list of DisplayModeConfigs per display for the chosenFps.
- std::vector<DisplayModeConfig> getDisplayModeConfigsForTheChosenFps(
- Fps chosenFps, const std::vector<RefreshRateRankingsAndSignals>&) const;
+ DisplayModePtr modePtr;
+ GlobalSignals consideredSignals;
+
+ bool operator==(const DisplayModeChoice& other) const {
+ return modePtr == other.modePtr && consideredSignals == other.consideredSignals;
+ }
+
+ // For tests.
+ friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) {
+ return stream << '{' << to_string(*choice.modePtr) << " considering "
+ << choice.consideredSignals.toString().c_str() << '}';
+ }
+ };
+
+ using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
+ DisplayModeChoiceMap chooseDisplayModes() const REQUIRES(mPolicyLock);
GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
@@ -344,9 +336,8 @@
mutable std::mutex mPolicyLock;
- // Holds the Physical displays registered through the SurfaceFlinger, used for making
- // the refresh rate selections.
- display::PhysicalDisplayMap<PhysicalDisplayId, const sp<const DisplayDevice>> mDisplays;
+ display::PhysicalDisplayMap<PhysicalDisplayId, sp<const DisplayDevice>> mDisplays;
+ std::optional<PhysicalDisplayId> mLeaderDisplayId;
struct Policy {
// Policy for choosing the display mode.
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 2c77142..bd4f409 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -138,10 +138,6 @@
bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};
-struct FpsHash {
- size_t operator()(Fps fps) const { return std::hash<nsecs_t>()(fps.getPeriodNsecs()); }
-};
-
inline std::string to_string(Fps fps) {
return base::StringPrintf("%.2f Hz", fps.getValue());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e4fee1c..cfebec7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -84,6 +84,7 @@
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
+#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
#include <utils/StopWatch.h>
@@ -122,6 +123,8 @@
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
@@ -1062,30 +1065,28 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
ATRACE_CALL();
- if (!info.mode) {
- ALOGW("requested display mode is null");
- return;
- }
- auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
+ auto display = getDisplayDeviceLocked(request.modePtr->getPhysicalDisplayId());
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
return;
}
- if (display->setDesiredActiveMode(info)) {
+ const Fps refreshRate = request.modePtr->getFps();
+
+ if (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
// switch.
- mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
+ mScheduler->resyncToHardwareVsync(true, refreshRate);
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(info.mode->getFps());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setModeChangePending(true);
}
}
@@ -1177,7 +1178,7 @@
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
- if (upcomingModeInfo.event != DisplayModeEvent::None) {
+ if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
}
}
@@ -1383,6 +1384,10 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* /*outProperties*/) const {
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId) {
const char* const whence = __func__;
@@ -1573,7 +1578,10 @@
return BAD_VALUE;
}
- const wp<Layer> stopLayer = fromHandle(stopLayerHandle);
+ // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy
+ // the layer here since the caller has a strong ref to the layer's handle.
+ // TODO (b/238781169): replace layer with layer id
+ const wp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
mRegionSamplingThread->addListener(samplingArea, stopLayer, listener);
return NO_ERROR;
}
@@ -2172,10 +2180,13 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
- layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
+ std::vector<Layer*> layers;
+
+ mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) {
if (auto layerFE = layer->getCompositionEngineLayerFE()) {
+ layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
refreshArgs.layers.push_back(layerFE);
+ layers.push_back(layer);
}
});
refreshArgs.blursAreExpensive = mBlursAreExpensive;
@@ -2205,7 +2216,25 @@
// the scheduler.
const auto presentTime = systemTime();
- mCompositionEngine->present(refreshArgs);
+ {
+ std::vector<LayerSnapshotGuard> layerSnapshotGuards;
+ for (Layer* layer : layers) {
+ layerSnapshotGuards.emplace_back(layer);
+ }
+ mCompositionEngine->present(refreshArgs);
+ }
+
+ for (auto& layer : layers) {
+ CompositionResult compositionResult{
+ layer->getCompositionEngineLayerFE()->stealCompositionResult()};
+ layer->onPreComposition(compositionResult.refreshStartTime);
+ for (auto releaseFence : compositionResult.releaseFences) {
+ layer->onLayerDisplayed(releaseFence);
+ }
+ if (compositionResult.lastClientCompositionFence) {
+ layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+ }
+ }
mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
@@ -3117,20 +3146,21 @@
}
}
- if (!hintDisplay && mDisplays.size() > 0) {
+ if (!hintDisplay) {
// NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
// redraw after transform hint changes. See bug 8508397.
-
// could be null when this layer is using a layerStack
// that is not visible on any display. Also can occur at
// screen off/on times.
- hintDisplay = getDefaultDisplayDeviceLocked();
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
+ // U Update: Don't provide stale hints to the clients. For
+ // special cases where we want the app to draw its
+ // first frame before the display is available, we rely
+ // on WMS and DMS to provide the right information
+ // so the client can calculate the hint.
+ ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName());
+ layer->skipReportingTransformHint();
} else {
- ALOGW("Ignoring transform hint update for %s", layer->getDebugName());
+ layer->updateTransformHint(hintDisplay->getTransformHint());
}
});
}
@@ -3291,37 +3321,45 @@
}
}
+ std::vector<LayerSnapshotGuard> layerSnapshotGuards;
+ mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) {
+ if (layer->getLayerSnapshot()->compositionType ==
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
+ layer->updateSnapshot(false /* updateGeometry */);
+ layerSnapshotGuards.emplace_back(layer);
+ }
+ });
+
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::requestDisplayModes(
- std::vector<scheduler::DisplayModeConfig> displayModeConfigs) {
+void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
if (mBootStage != BootStage::FINISHED) {
ALOGV("Currently in the boot stage, skipping display mode changes");
return;
}
ATRACE_CALL();
+
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- std::for_each(displayModeConfigs.begin(), displayModeConfigs.end(),
- [&](const auto& config) REQUIRES(mStateLock) {
- const auto& displayModePtr = config.displayModePtr;
- if (const auto display =
- getDisplayDeviceLocked(displayModePtr->getPhysicalDisplayId());
- display->refreshRateConfigs().isModeAllowed(displayModePtr->getId())) {
- const auto event = config.signals.idle ? DisplayModeEvent::None
- : DisplayModeEvent::Changed;
- setDesiredActiveMode({displayModePtr, event});
- } else {
- ALOGV("Skipping disallowed mode %d for display %" PRId64,
- displayModePtr->getId().value(), display->getPhysicalId().value);
- }
- });
+ for (auto& request : modeRequests) {
+ const auto& modePtr = request.modePtr;
+ const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
+
+ if (!display) continue;
+
+ if (display->refreshRateConfigs().isModeAllowed(modePtr->getId())) {
+ setDesiredActiveMode(std::move(request));
+ } else {
+ ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
+ to_string(display->getId()).c_str());
+ }
+ }
}
void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3566,9 +3604,9 @@
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
-status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
+status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
- bool addToRoot, uint32_t* outTransformHint) {
+ uint32_t* outTransformHint) {
if (mNumLayers >= MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
@@ -3599,12 +3637,12 @@
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- mCreatedLayers.emplace_back(layer, parent, addToRoot);
+ mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
}
// attach this layer to the client
- if (client != nullptr) {
- client->attachLayer(handle, layer);
+ if (args.client != nullptr) {
+ args.client->attachLayer(handle, layer);
}
setTransactionFlags(eTransactionNeeded);
@@ -3664,7 +3702,7 @@
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
- sp<Layer> layer = Layer::fromHandle(s.surface).promote();
+ sp<Layer> layer = LayerHandle::getLayer(s.surface);
const auto& transaction = *flushState.transaction;
// check for barrier frames
if (s.bufferData->hasBarrier &&
@@ -3709,7 +3747,10 @@
if (listener &&
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
- mTransactionHandler.onTransactionQueueStalled(transaction, listener);
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id, listener,
+ "Buffer processing hung up due to stuck "
+ "fence. Indicates GPU hang");
}
return false;
}
@@ -3925,10 +3966,11 @@
uint32_t clientStateFlags = 0;
for (int i = 0; i < states.size(); i++) {
ComposerState& state = states.editItemAt(i);
- clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
- isAutoTimestamp, postTime, permissions);
+ clientStateFlags |=
+ setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
+ postTime, permissions, transactionId);
if ((flags & eAnimation) && state.state.surface) {
- if (const auto layer = fromHandle(state.state.surface).promote()) {
+ if (const auto layer = LayerHandle::getLayer(state.state.surface)) {
using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
mScheduler->recordLayerHistory(layer.get(),
isAutoTimestamp ? 0 : desiredPresentTime,
@@ -4042,7 +4084,8 @@
uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
ComposerState& composerState,
int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint32_t permissions) {
+ int64_t postTime, uint32_t permissions,
+ uint64_t transactionId) {
layer_state_t& s = composerState.state;
s.sanitize(permissions);
@@ -4068,7 +4111,7 @@
uint32_t flags = 0;
sp<Layer> layer = nullptr;
if (s.surface) {
- layer = fromHandle(s.surface).promote();
+ layer = LayerHandle::getLayer(s.surface);
} else {
// The client may provide us a null handle. Treat it as if the layer was removed.
ALOGW("Attempt to set client state with a null layer handle");
@@ -4081,6 +4124,8 @@
return 0;
}
+ ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+
// Only set by BLAST adapter layers
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
@@ -4333,7 +4378,8 @@
if (what & layer_state_t::eBufferChanged) {
std::shared_ptr<renderengine::ExternalTexture> buffer =
- getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName());
+ getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName(),
+ transactionId);
if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
dequeueBufferTimestamp, frameTimelineInfo)) {
flags |= eTraversalNeeded;
@@ -4345,6 +4391,13 @@
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
+
+ // if the layer has been parented on to a new display, update its transform hint.
+ if (((flags & eTransformHintUpdateNeeded) == 0) &&
+ oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) {
+ flags |= eTransformHintUpdateNeeded;
+ }
+
return flags;
}
@@ -4362,14 +4415,16 @@
sp<Layer> mirrorLayer;
sp<Layer> mirrorFrom;
+ LayerCreationArgs mirrorArgs(args);
{
Mutex::Autolock _l(mStateLock);
- mirrorFrom = fromHandle(mirrorFromHandle).promote();
+ mirrorFrom = LayerHandle::getLayer(mirrorFromHandle);
if (!mirrorFrom) {
return NAME_NOT_FOUND;
}
- LayerCreationArgs mirrorArgs = args;
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
+ mirrorArgs.mirrorLayerHandle = mirrorFromHandle;
+ mirrorArgs.addToRoot = false;
status_t result = createEffectLayer(mirrorArgs, &outResult.handle, &mirrorLayer);
if (result != NO_ERROR) {
return result;
@@ -4385,9 +4440,8 @@
mirrorLayer->sequence, args.name,
mirrorFrom->sequence);
}
- return addClientLayer(args.client, outResult.handle, mirrorLayer /* layer */,
- nullptr /* parent */, false /* addToRoot */,
- nullptr /* outTransformHint */);
+ return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */,
+ nullptr /* parent */, nullptr /* outTransformHint */);
}
status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
@@ -4412,14 +4466,14 @@
}
layerStack = display->getLayerStack();
- LayerCreationArgs mirrorArgs = args;
+ LayerCreationArgs mirrorArgs(args);
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
+ mirrorArgs.addToRoot = true;
result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
outResult.layerId = rootMirrorLayer->sequence;
outResult.layerName = String16(rootMirrorLayer->getDebugName());
- result |= addClientLayer(args.client, outResult.handle, rootMirrorLayer /* layer */,
- nullptr /* parent */, true /* addToRoot */,
- nullptr /* outTransformHint */);
+ result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
+ nullptr /* parent */, nullptr /* outTransformHint */);
}
if (result != NO_ERROR) {
@@ -4440,8 +4494,7 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
- gui::CreateSurfaceResult& outResult) {
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
status_t result = NO_ERROR;
sp<Layer> layer;
@@ -4470,28 +4523,23 @@
return result;
}
- bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
- wp<Layer> parent = fromHandle(parentHandle);
- if (parentHandle != nullptr && parent == nullptr) {
- ALOGE("Invalid parent handle %p.", parentHandle.get());
- addToRoot = false;
+ args.addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
+ // We can safely promote the parent layer in binder thread because we have a strong reference
+ // to the layer's handle inside this scope.
+ sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote());
+ if (args.parentHandle != nullptr && parent == nullptr) {
+ ALOGE("Invalid parent handle %p", args.parentHandle.promote().get());
+ args.addToRoot = false;
}
- int parentId = -1;
- // We can safely promote the layer in binder thread because we have a strong reference
- // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
- sp<Layer> parentSp = parent.promote();
- if (parentSp != nullptr) {
- parentId = parentSp->getSequence();
- }
+ const int parentId = parent ? parent->getSequence() : -1;
if (mTransactionTracing) {
mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence,
args.name, args.flags, parentId);
}
uint32_t outTransformHint;
- result = addClientLayer(args.client, outResult.handle, layer, parent, addToRoot,
- &outTransformHint);
+ result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
if (result != NO_ERROR) {
return result;
}
@@ -4523,7 +4571,7 @@
setTransactionFlags(eTransactionNeeded);
}
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t /* layerId */) {
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
@@ -4781,13 +4829,6 @@
}
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
- if (asProto) {
- mLayerTracing.writeToFile();
- if (mTransactionTracing) {
- mTransactionTracing->writeToFile();
- }
- }
-
return doDump(fd, DumpArgs(), asProto);
}
@@ -6135,7 +6176,7 @@
{
Mutex::Autolock lock(mStateLock);
- parent = fromHandle(args.layerHandle).promote();
+ parent = LayerHandle::getLayer(args.layerHandle);
if (parent == nullptr) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
@@ -6166,7 +6207,7 @@
reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
for (const auto& handle : args.excludeHandles) {
- sp<Layer> excludeLayer = fromHandle(handle).promote();
+ sp<Layer> excludeLayer = LayerHandle::getLayer(handle);
if (excludeLayer != nullptr) {
excludeLayers.emplace(excludeLayer);
} else {
@@ -6294,37 +6335,38 @@
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
- auto future = mScheduler->schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable
- -> ftl::SharedFuture<FenceResult> {
- ScreenCaptureResults captureResults;
- std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
- captureListener->onScreenCaptureCompleted(captureResults);
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
+ auto future = mScheduler->schedule(
+ [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
+ kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+ ScreenCaptureResults captureResults;
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+ }
- ftl::SharedFuture<FenceResult> renderFuture;
- renderArea->render([&] {
- renderFuture =
- renderScreenImpl(*renderArea, traverseLayers, buffer, canCaptureBlackoutContent,
- regionSampling, grayscale, captureResults);
- });
+ ftl::SharedFuture<FenceResult> renderFuture;
+ renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ renderFuture = renderScreenImpl(*renderArea, traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling,
+ grayscale, captureResults);
+ });
- if (captureListener) {
- // Defer blocking on renderFuture back to the Binder thread.
- return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
- captureResults.fenceResult = std::move(fenceResult);
- captureListener->onScreenCaptureCompleted(captureResults);
- return base::unexpected(NO_ERROR);
- })
- .share();
- }
- return renderFuture;
- });
+ if (captureListener) {
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ captureResults.fenceResult = std::move(fenceResult);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
+ }
+ return renderFuture;
+ });
// Flatten nested futures.
auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
@@ -6418,7 +6460,11 @@
const auto display = renderArea.getDisplayDevice();
std::vector<Layer*> renderedLayers;
bool disableBlurs = false;
- traverseLayers([&](Layer* layer) {
+ traverseLayers([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
+ auto layerFE = layer->getCompositionEngineLayerFE();
+ if (!layerFE) {
+ return;
+ }
// Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
// LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
// generated with the layer's current buffer and geometry.
@@ -6444,8 +6490,12 @@
isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits,
};
- std::optional<compositionengine::LayerFE::LayerSettings> settings =
- layer->prepareClientComposition(targetSettings);
+ std::optional<compositionengine::LayerFE::LayerSettings> settings;
+ {
+ LayerSnapshotGuard layerSnapshotGuard(layer);
+ settings = layerFE->prepareClientComposition(targetSettings);
+ }
+
if (!settings) {
return;
}
@@ -6531,18 +6581,19 @@
}
}
-std::optional<DisplayModePtr> SurfaceFlinger::getPreferredDisplayMode(
+std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
- return schedulerMode;
+ return ftl::as_non_null(schedulerMode);
}
return mPhysicalDisplays.get(displayId)
.transform(&PhysicalDisplay::snapshotRef)
.and_then([&](const display::DisplaySnapshot& snapshot) {
return snapshot.displayModes().get(defaultModeId);
- });
+ })
+ .transform(&ftl::as_non_null<const DisplayModePtr&>);
}
status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
@@ -6598,7 +6649,7 @@
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), DisplayModeEvent::Changed});
+ setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
return NO_ERROR;
}
@@ -6672,10 +6723,6 @@
return NO_ERROR;
}
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
- return Layer::fromHandle(handle);
-}
-
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
if (!layer->isRemovedFromCurrentState()) {
@@ -6840,7 +6887,20 @@
parent->addChild(layer);
}
- layer->updateTransformHint(mActiveDisplayTransformHint);
+ ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+ sp<const DisplayDevice> hintDisplay;
+ // Find the display that includes the layer.
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getLayerStack() == layerStack) {
+ hintDisplay = display;
+ break;
+ }
+ }
+
+ if (hintDisplay) {
+ layer->updateTransformHint(hintDisplay->getTransformHint());
+ }
+
if (mTransactionTracing) {
mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
}
@@ -6896,34 +6956,44 @@
}
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* layerName) const {
- bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
- bool bufferSizeExceedsLimit = false;
- std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
- if (cacheIdChanged && bufferData.buffer != nullptr) {
- bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
- bufferData.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
- buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
- }
- } else if (cacheIdChanged) {
- buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
- } else if (bufferData.buffer != nullptr) {
- bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
- bufferData.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- buffer = std::make_shared<
- renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
+ BufferData& bufferData, const char* layerName, uint64_t transactionId) {
+ if (bufferData.buffer &&
+ exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), bufferData.buffer->getHeight())) {
+ ALOGE("Attempted to create an ExternalTexture for layer %s that exceeds render target "
+ "size limit.",
+ layerName);
+ return nullptr;
}
- ALOGE_IF(bufferSizeExceedsLimit,
- "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
- "limit.",
- layerName);
- return buffer;
+
+ bool cachedBufferChanged =
+ bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+ if (cachedBufferChanged && bufferData.buffer) {
+ auto result = ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+ if (result.ok()) {
+ return result.value();
+ }
+
+ if (result.error() == ClientCache::AddError::CacheFull) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transactionId, bufferData.releaseBufferListener,
+ "Buffer processing hung due to full buffer cache");
+ }
+
+ return nullptr;
+ }
+
+ if (cachedBufferChanged) {
+ return ClientCache::getInstance().get(bufferData.cachedBuffer);
+ }
+
+ if (bufferData.buffer) {
+ return std::make_shared<
+ renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::Usage::
+ READABLE);
+ }
+
+ return nullptr;
}
bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) {
@@ -6941,7 +7011,7 @@
for (const auto& mirrorDisplay : mirrorDisplays) {
// Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
// accidentally.
- sp<Layer> rootMirrorLayer = Layer::fromHandle(mirrorDisplay.rootHandle).promote();
+ sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
for (const auto& layer : mDrawingState.layersSortedByZ) {
if (layer->getLayerStack() != mirrorDisplay.layerStack ||
@@ -7272,6 +7342,14 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getOverlaySupport(gui::OverlayProperties* outProperties) {
+ status_t status = checkAccessPermission();
+ if (status == OK) {
+ status = mFlinger->getOverlaySupport(outProperties);
+ }
+ return binderStatusFromStatusT(status);
+}
+
binder::Status SurfaceComposerAIDL::getBootDisplayModeSupport(bool* outMode) {
status_t status = checkAccessPermission();
if (status == OK) {
@@ -7719,7 +7797,7 @@
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) &&
+ if ((uid != AID_GRAPHICS) && (uid != AID_SYSTEM) &&
!PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) {
ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c4c5b56..85c194b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,6 +29,7 @@
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/future.h>
+#include <ftl/non_null.h>
#include <gui/BufferQueue.h>
#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
@@ -65,6 +66,8 @@
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FlagManager.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
@@ -75,7 +78,6 @@
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
-#include "TransactionHandler.h"
#include "TransactionState.h"
#include <atomic>
@@ -271,6 +273,11 @@
void removeHierarchyFromOffscreenLayers(Layer* layer);
void removeFromOffscreenLayers(Layer* layer);
+ // Called when all clients have released all their references to
+ // this layer. The layer may still be kept alive by its parents but
+ // the client can no longer modify this layer directly.
+ void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
+
// TODO: Remove atomic if move dtor to main thread CL lands
std::atomic<uint32_t> mNumClones;
@@ -278,12 +285,6 @@
return mTransactionCallbackInvoker;
}
- // Converts from a binder handle to a Layer
- // Returns nullptr if the handle does not point to an existing layer.
- // Otherwise, returns a weak reference so that callers off the main-thread
- // won't accidentally hold onto the last strong reference.
- wp<Layer> fromHandle(const sp<IBinder>& handle) const;
-
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
@@ -312,7 +313,7 @@
REQUIRES(mStateLock);
virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* layerName) const;
+ BufferData& bufferData, const char* layerName, uint64_t transactionId);
// Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
template <typename Predicate>
@@ -429,10 +430,6 @@
mCounterByLayerHandle GUARDED_BY(mLock);
};
- using ActiveModeInfo = DisplayDevice::ActiveModeInfo;
- using KernelIdleTimerController =
- ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController;
-
enum class BootStage {
BOOTLOADER,
BOOTANIMATION,
@@ -523,6 +520,7 @@
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
status_t getBootDisplayModeSupport(bool* outSupport) const;
status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
+ status_t getOverlaySupport(gui::OverlayProperties* outProperties) const;
status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
void setGameContentType(const sp<IBinder>& displayToken, bool on);
@@ -626,16 +624,17 @@
// ISchedulerCallback overrides:
// Toggles hardware VSYNC by calling into HWC.
+ // TODO(b/241286146): Rename for self-explanatory API.
void setVsyncEnabled(bool) override;
- // Sets the desired display mode per display if allowed by policy .
- void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override;
- // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
- // Called when the frame rate override list changed to trigger an event.
void triggerOnFrameRateOverridesChanged() override;
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
+
+ using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+
// Get the controller and timeout that will help decide how the kernel idle timer will be
// configured and what value to use as the timeout.
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
@@ -650,8 +649,8 @@
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
- // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
- void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+ void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
// Sets the active mode and a new refresh rate in SF.
void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
@@ -670,9 +669,8 @@
// Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
// display. Falls back to the display's defaultModeId otherwise.
- std::optional<DisplayModePtr> getPreferredDisplayMode(PhysicalDisplayId,
- DisplayModeId defaultModeId) const
- REQUIRES(mStateLock);
+ std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode(
+ PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
status_t setDesiredDisplayModeSpecsInternal(const sp<DisplayDevice>&,
const scheduler::RefreshRateConfigs::PolicyVariant&)
@@ -727,7 +725,8 @@
uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
+ int64_t postTime, uint32_t permissions, uint64_t transactionId)
+ REQUIRES(mStateLock);
uint32_t getTransactionFlags() const;
@@ -754,8 +753,7 @@
/*
* Layer management
*/
- status_t createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
- gui::CreateSurfaceResult& outResult);
+ status_t createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult);
status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
sp<Layer>* outLayer);
@@ -769,15 +767,11 @@
status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
gui::CreateSurfaceResult& outResult);
- // called when all clients have released all their references to
- // this layer meaning it is entirely safe to destroy all
- // resources associated to this layer.
- void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
void markLayerPendingRemovalLocked(const sp<Layer>& layer);
// add a layer to SurfaceFlinger
- status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+ status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+ const sp<Layer>& layer, const wp<Layer>& parentLayer,
uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
@@ -797,7 +791,8 @@
ftl::SharedFuture<FenceResult> renderScreenImpl(
const RenderArea&, TraverseLayersFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
- bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock);
+ bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
+ REQUIRES(kMainThreadContext);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
@@ -1410,6 +1405,7 @@
binder::Status setBootDisplayMode(const sp<IBinder>& display, int displayModeId) override;
binder::Status clearBootDisplayMode(const sp<IBinder>& display) override;
binder::Status getBootDisplayModeSupport(bool* outMode) override;
+ binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override;
binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override;
binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
binder::Status captureDisplay(const DisplayCaptureArgs&,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 2f1f263..7e6894d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -91,6 +91,10 @@
return sp<Layer>::make(args);
}
+sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName) {
+ return sp<LayerFE>::make(layerName);
+}
+
std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
return std::make_unique<FrameTracer>();
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 447a02f..2c6de0e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -42,6 +42,7 @@
std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
+ sp<LayerFE> createLayerFE(const std::string& layerName) override;
std::unique_ptr<FrameTracer> createFrameTracer() override;
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 9c4d5c8..41edd22 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -38,12 +38,12 @@
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class Layer;
+class LayerFE;
class StartPropertySetThread;
class SurfaceFlinger;
class TimeStats;
struct DisplayDeviceCreationArgs;
-struct LayerCreationArgs;
namespace compositionengine {
class CompositionEngine;
@@ -61,6 +61,7 @@
namespace surfaceflinger {
+struct LayerCreationArgs;
class NativeWindowSurface;
// The interface that SurfaceFlinger uses to create all of the implementations
@@ -88,6 +89,7 @@
virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
+ virtual sp<LayerFE> createLayerFE(const std::string& layerName) = 0;
virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index d0364f2..25fdd26 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -82,6 +82,8 @@
sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
+ sp<LayerFE> createLayerFE(const std::string& layerName) { return sp<LayerFE>::make(layerName); }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
}
@@ -98,7 +100,8 @@
MockSurfaceFlinger(Factory& factory)
: SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* /* layerName */) const override {
+ BufferData& bufferData, const char* /* layerName */,
+ uint64_t /* transactionId */) override {
return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(),
bufferData.getHeight(),
bufferData.getId(),
@@ -211,8 +214,8 @@
gui::CreateSurfaceResult outResult;
LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
- tracingArgs.flags, LayerMetadata());
- args.sequence = std::make_optional<int32_t>(tracingArgs.layerId);
+ tracingArgs.flags, LayerMetadata(),
+ std::make_optional<int32_t>(tracingArgs.layerId));
if (tracingArgs.mirrorFromId == -1) {
sp<IBinder> parentHandle = nullptr;
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index f3cf42d..9f9ae48 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -52,6 +52,10 @@
;
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
+
+ // sink any log spam from the stubbed surfaceflinger
+ __android_log_set_logger([](const struct __android_log_message* /* log_message */) {});
+
if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 23ea7a5..61ff9bc 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -19,6 +19,7 @@
#include <condition_variable>
#include <deque>
#include <mutex>
+#include <optional>
#include <queue>
#include <thread>
#include <unordered_map>
@@ -48,7 +49,7 @@
std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
nsecs_t latchTime = -1;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = std::nullopt;
uint32_t currentMaxAcquiredBufferCount = 0;
std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
CompositorTiming compositorTiming;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index a3da4dc..ce4d18f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -150,7 +150,7 @@
sp<IBinder> handle = defaultServiceManager()->checkService(
String16(mFdp.ConsumeRandomLengthString().c_str()));
- mFlinger->fromHandle(handle);
+ LayerHandle::getLayer(handle);
mFlinger->disableExpensiveRendering();
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index ee48345..502b55a 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -34,6 +34,7 @@
#include "DisplayHardware/ComposerHal.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/EventThread.h"
@@ -354,6 +355,10 @@
return sp<Layer>::make(args);
}
+ sp<LayerFE> createLayerFE(const std::string &layerName) override {
+ return sp<LayerFE>::make(layerName);
+ }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<android::mock::FrameTracer>();
}
@@ -767,7 +772,7 @@
auto& mutableDisplays() { return mFlinger->mDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
- auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
+ auto fromHandle(const sp<IBinder> &handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
mutableDisplays().clear();
@@ -781,7 +786,7 @@
private:
void setVsyncEnabled(bool) override {}
- void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 0a142c3..acfc1d4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -146,8 +146,7 @@
layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
layer->fenceHasSignaled();
- layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>(),
- false /*updatingOutputGeometryThisFrame*/);
+ layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
const std::vector<sp<CallbackHandle>> callbacks;
layer->setTransactionCompletedListeners(callbacks);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 8b91c67..e0b508a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -120,51 +120,6 @@
});
}
-sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
- std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
- constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
- constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
- constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
-
- // The DisplayDevice is required to have a framebuffer (behind the
- // ANativeWindow interface) which uses the actual hardware display
- // size.
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
- compositionengine::DisplayCreationArgsBuilder()
- .setId(DEFAULT_DISPLAY_ID)
- .setPixels({DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT})
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
-
- constexpr bool kIsPrimary = true;
- auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
-
- injector.setNativeWindow(mNativeWindow);
- if (injectExtra) {
- injectExtra(injector);
- }
-
- auto displayDevice = injector.inject();
-
- Mock::VerifyAndClear(mNativeWindow.get());
-
- return displayDevice;
-}
-
bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
const auto& map = mFlinger.hwcPhysicalDisplayIdMap();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 9cceb5e..19c7d5c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -42,6 +42,7 @@
#include <renderengine/mock/RenderEngine.h>
#include <ui/DebugUtils.h>
+#include "FakeDisplayInjector.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -88,8 +89,11 @@
void injectMockComposer(int virtualDisplayCount);
void injectFakeBufferQueueFactory();
void injectFakeNativeWindowSurfaceFactory();
+
sp<DisplayDevice> injectDefaultInternalDisplay(
- std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) {
+ return mFakeDisplayInjector.injectInternalDisplay(injectExtra);
+ }
// --------------------------------------------------------------------
// Postcondition helpers
@@ -114,6 +118,8 @@
sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make();
Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
+
// These mocks are created by the test, but are destroyed by SurfaceFlinger
// by virtue of being stored into a std::unique_ptr. However we still need
// to keep a reference to them for use in setting up call expectations.
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 81b420c..6e4bf2b 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -27,49 +27,54 @@
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
using android::hardware::graphics::composer::hal::HWDisplayId;
using android::Hwc2::mock::PowerAdvisor;
-using testing::_;
-using testing::AnyNumber;
-using testing::DoAll;
-using testing::Mock;
-using testing::ResultOf;
-using testing::Return;
-using testing::SetArgPointee;
+
+struct FakeDisplayInjectorArgs {
+ PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+ HWDisplayId hwcDisplayId = 0;
+ bool isPrimary = true;
+};
class FakeDisplayInjector {
public:
- sp<DisplayDevice> injectDefaultInternalDisplay(
- const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
- TestableSurfaceFlinger& flinger, uint8_t port = 255u) const {
- constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
- constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
- constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
+ FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+ sp<mock::NativeWindow> nativeWindow)
+ : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
- const PhysicalDisplayId physicalDisplayId = PhysicalDisplayId::fromPort(port);
+ sp<DisplayDevice> injectInternalDisplay(
+ const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+ FakeDisplayInjectorArgs args = {}) {
+ using testing::_;
+ using testing::AnyNumber;
+ using testing::DoAll;
+ using testing::Mock;
+ using testing::Return;
+ using testing::SetArgPointee;
+
+ constexpr ui::Size kResolution = {1080, 1920};
// The DisplayDevice is required to have a framebuffer (behind the
// ANativeWindow interface) which uses the actual hardware display
// size.
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getWidth()), Return(0)));
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getHeight()), Return(0)));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
auto compositionDisplay = compositionengine::impl::
- createDisplay(flinger.getCompositionEngine(),
+ createDisplay(mFlinger.getCompositionEngine(),
compositionengine::DisplayCreationArgsBuilder()
- .setId(physicalDisplayId)
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setPowerAdvisor(mPowerAdvisor)
+ .setId(args.displayId)
+ .setPixels(kResolution)
+ .setPowerAdvisor(&mPowerAdvisor)
.build());
- constexpr bool kIsPrimary = true;
- auto injector = FakeDisplayDeviceInjector(flinger, compositionDisplay,
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
ui::DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
+ args.hwcDisplayId, args.isPrimary);
injector.setNativeWindow(mNativeWindow);
if (injectExtra) {
@@ -83,8 +88,9 @@
return displayDevice;
}
- sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
- PowerAdvisor* mPowerAdvisor = new PowerAdvisor();
+ TestableSurfaceFlinger& mFlinger;
+ Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+ sp<mock::NativeWindow> mNativeWindow;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 620825f..924c5be 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -17,6 +17,9 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
+#include <algorithm>
+#include <array>
+
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
@@ -34,15 +37,17 @@
namespace hal = android::hardware::graphics::composer::hal;
-using SetPolicyResult = RefreshRateConfigs::SetPolicyResult;
-using LayerVoteType = RefreshRateConfigs::LayerVoteType;
using LayerRequirement = RefreshRateConfigs::LayerRequirement;
+using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+using SetPolicyResult = RefreshRateConfigs::SetPolicyResult;
using mock::createDisplayMode;
struct TestableRefreshRateConfigs : RefreshRateConfigs {
- using RefreshRateConfigs::RefreshRateConfigs;
using RefreshRateConfigs::RefreshRateOrder;
+ using RefreshRateConfigs::RefreshRateRanking;
+
+ using RefreshRateConfigs::RefreshRateConfigs;
void setActiveModeId(DisplayModeId modeId) {
ftl::FakeGuard guard(kMainThreadContext);
@@ -74,12 +79,10 @@
return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
}
- std::vector<RefreshRateRanking> getRefreshRatesByPolicy(
- std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
+ RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder) const {
std::lock_guard lock(mLock);
- return RefreshRateConfigs::
- getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder,
- /*preferredDisplayModeOpt*/ std::nullopt);
+ return RefreshRateConfigs::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
}
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
@@ -87,14 +90,25 @@
using RefreshRateConfigs::GetRankedRefreshRatesCache;
auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
- auto getRankedRefreshRatesAndSignals(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const {
- return RefreshRateConfigs::getRankedRefreshRates(layers, signals);
+ auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto result = RefreshRateConfigs::getRankedRefreshRates(layers, signals);
+
+ EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
+ ScoredRefreshRate::DescendingScore{}));
+
+ return result;
+ }
+
+ auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto [ranking, consideredSignals] = getRankedRefreshRates(layers, signals);
+ return std::make_pair(ranking, consideredSignals);
}
DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
GlobalSignals signals = {}) const {
- return getRankedRefreshRatesAndSignals(layers, signals).first.front().displayModePtr;
+ return getRankedRefreshRates(layers, signals).ranking.front().modePtr;
}
SetPolicyResult setPolicy(const PolicyVariant& policy) {
@@ -109,6 +123,8 @@
class RefreshRateConfigsTest : public testing::Test {
protected:
+ using RefreshRateOrder = TestableRefreshRateConfigs::RefreshRateOrder;
+
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
@@ -1050,20 +1066,17 @@
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode30}};
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
- TestableRefreshRateConfigs::RefreshRateOrder::
- Descending);
+ const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(),
+ RefreshRateOrder::Descending);
+ const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
}
@@ -1071,20 +1084,17 @@
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode90}};
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
- TestableRefreshRateConfigs::RefreshRateOrder::
- Ascending);
+ const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(),
+ RefreshRateOrder::Ascending);
+ const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
}
@@ -1092,23 +1102,20 @@
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode90}};
EXPECT_EQ(SetPolicyResult::Changed,
configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
- TestableRefreshRateConfigs::RefreshRateOrder::
- Ascending);
+ const auto refreshRates =
+ configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
+ const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
}
@@ -1116,47 +1123,48 @@
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode30}};
EXPECT_EQ(SetPolicyResult::Changed,
configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
- TestableRefreshRateConfigs::RefreshRateOrder::
- Descending);
+ const auto refreshRates =
+ configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Descending);
+ const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
}
TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
- RefreshRateConfigs configs(kModes_60_90, kModeId60);
- std::vector<RefreshRateRanking> expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60}};
+ TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
+
+ std::array expectedRefreshRates = {kMode90, kMode60};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
- std::tie(refreshRates, signals) = configs.getRankedRefreshRates({}, {.powerOnImminent = true});
+ std::tie(refreshRates, signals) =
+ configs.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
EXPECT_TRUE(signals.powerOnImminent);
+
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1166,34 +1174,38 @@
lr1.name = "60Hz ExplicitExactOrMultiple";
std::tie(refreshRates, signals) =
- configs.getRankedRefreshRates(layers, {.powerOnImminent = true});
+ configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
EXPECT_TRUE(signals.powerOnImminent);
+
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
- expectedRefreshRates = {RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90}};
std::tie(refreshRates, signals) =
- configs.getRankedRefreshRates(layers, {.powerOnImminent = false});
+ configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
EXPECT_FALSE(signals.powerOnImminent);
+
+ expectedRefreshRates = {kMode60, kMode90};
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
+ << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
+ << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
}
}
TEST_F(RefreshRateConfigsTest, touchConsidered) {
- RefreshRateConfigs configs(kModes_60_90, kModeId60);
+ TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
auto [_, signals] = configs.getRankedRefreshRates({}, {});
EXPECT_FALSE(signals.touch);
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates({}, {.touch = true});
+ std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair({}, {.touch = true});
EXPECT_TRUE(signals.touch);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
@@ -1206,7 +1218,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
+ std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
EXPECT_TRUE(signals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -1215,7 +1227,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
+ std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
EXPECT_FALSE(signals.touch);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1224,7 +1236,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
+ std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
EXPECT_TRUE(signals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -1233,7 +1245,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
+ std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
EXPECT_FALSE(signals.touch);
}
@@ -1352,7 +1364,7 @@
const auto [mode, signals] =
configs.getRankedRefreshRates(layers, {.touch = true, .idle = true});
- EXPECT_EQ(mode.begin()->displayModePtr, kMode60);
+ EXPECT_EQ(mode.begin()->modePtr, kMode60);
EXPECT_FALSE(signals.touch);
}
@@ -1407,18 +1419,15 @@
lr5.name = "30Hz";
lr5.focused = true;
- std::vector<RefreshRateRanking> expectedRankings = {
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
- RefreshRateRanking{kMode60}, RefreshRateRanking{kMode30},
- };
+ std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
+ auto actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
- std::vector<RefreshRateRanking> actualOrder =
- configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
+ << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
+ << actualRanking[i].modePtr->getFps().getIntValue();
}
lr1.vote = LayerVoteType::Max;
@@ -1436,18 +1445,15 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz";
- expectedRankings = {
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
- RefreshRateRanking{kMode60}, RefreshRateRanking{kMode30},
- };
+ expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
+ actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
+ << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
+ << actualRanking[i].modePtr->getFps().getIntValue();
}
lr1.vote = LayerVoteType::Heuristic;
@@ -1463,17 +1469,15 @@
lr5.desiredRefreshRate = 72_Hz;
lr5.name = "72Hz";
- expectedRankings = {
- RefreshRateRanking{kMode30}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode72},
- };
+ expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72};
+ actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
+ << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
+ << actualRanking[i].modePtr->getFps().getIntValue();
}
lr1.desiredRefreshRate = 120_Hz;
@@ -1492,17 +1496,15 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz-2";
- expectedRankings = {
- RefreshRateRanking{kMode90}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode120},
- RefreshRateRanking{kMode72}, RefreshRateRanking{kMode30},
- };
+ expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30};
+ actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
+ << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
+ << actualRanking[i].modePtr->getFps().getIntValue();
}
}
@@ -1513,8 +1515,8 @@
EXPECT_EQ(SetPolicyResult::Changed,
configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
- const auto [mode, signals] = configs.getRankedRefreshRatesAndSignals({}, {});
- EXPECT_EQ(mode.front().displayModePtr, kMode90);
+ const auto [ranking, signals] = configs.getRankedRefreshRates({}, {});
+ EXPECT_EQ(ranking.front().modePtr, kMode90);
EXPECT_FALSE(signals.touch);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1892,13 +1894,12 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
- const auto [refreshRate, signals] =
- configs.getRankedRefreshRatesAndSignals(layers,
- {.touch = touchActive, .idle = true});
+ const auto [ranking, signals] =
+ configs.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
EXPECT_EQ(!touchActive, signals.idle);
- return refreshRate.front().displayModePtr->getId();
+ return ranking.front().modePtr->getId();
};
EXPECT_EQ(SetPolicyResult::Changed,
@@ -2059,12 +2060,13 @@
const auto args = std::make_pair(std::vector<LayerRequirement>{},
GlobalSignals{.touch = true, .idle = true});
- const auto result = std::make_pair(std::vector<RefreshRateRanking>{RefreshRateRanking{kMode90}},
- GlobalSignals{.touch = true});
+ const RefreshRateConfigs::RankedRefreshRates result = {{RefreshRateConfigs::ScoredRefreshRate{
+ kMode90}},
+ {.touch = true}};
configs.mutableGetRankedRefreshRatesCache() = {args, result};
- EXPECT_EQ(result, configs.getRankedRefreshRatesAndSignals(args.first, args.second));
+ EXPECT_EQ(result, configs.getRankedRefreshRates(args.first, args.second));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
@@ -2075,7 +2077,7 @@
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
- const auto result = configs.getRankedRefreshRatesAndSignals(layers, globalSignals);
+ const auto result = configs.getRankedRefreshRates(layers, globalSignals);
const auto& cache = configs.mutableGetRankedRefreshRatesCache();
ASSERT_TRUE(cache);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 406d2bc..147433b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -43,8 +43,6 @@
using MockLayer = android::mock::MockLayer;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
-
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -61,14 +59,28 @@
SchedulerTest();
- static inline const DisplayModePtr kMode60_1 = createDisplayMode(DisplayModeId(0), 60_Hz);
- static inline const DisplayModePtr kMode120_1 = createDisplayMode(DisplayModeId(1), 120_Hz);
- static inline const DisplayModePtr kMode60_2 = createDisplayMode(DisplayModeId(2), 60_Hz);
- static inline const DisplayModePtr kMode120_2 = createDisplayMode(DisplayModeId(3), 120_Hz);
- static inline const DisplayModePtr kMode60_3 = createDisplayMode(DisplayModeId(4), 60_Hz);
+ static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
+ static inline const DisplayModePtr kDisplay1Mode60 =
+ createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz);
+ static inline const DisplayModePtr kDisplay1Mode120 =
+ createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz);
+ static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
+
+ static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
+ static inline const DisplayModePtr kDisplay2Mode60 =
+ createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz);
+ static inline const DisplayModePtr kDisplay2Mode120 =
+ createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz);
+ static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
+
+ static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
+ static inline const DisplayModePtr kDisplay3Mode60 =
+ createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz);
+ static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
std::shared_ptr<RefreshRateConfigs> mConfigs =
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1), kMode60_1->getId());
+ std::make_shared<RefreshRateConfigs>(makeModes(kDisplay1Mode60),
+ kDisplay1Mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
@@ -76,9 +88,12 @@
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- FakeDisplayInjector mFakeDisplayInjector;
TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ sp<android::mock::NativeWindow> mNativeWindow = sp<android::mock::NativeWindow>::make();
+
+ FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
};
SchedulerTest::SchedulerTest() {
@@ -111,7 +126,7 @@
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(handle, kDisplayId1, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
mScheduler->onScreenAcquired(handle);
@@ -135,8 +150,8 @@
ASSERT_EQ(mEventThreadConnection, connection);
EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
- EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
+ EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
+ mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
mScheduler->onScreenAcquired(mConnectionHandle);
@@ -182,8 +197,7 @@
ASSERT_EQ(1u, mScheduler->layerHistorySize());
mScheduler->setRefreshRateConfigs(
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1, kMode120_1),
- kMode60_1->getId()));
+ std::make_shared<RefreshRateConfigs>(kDisplay1Modes, kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -200,7 +214,7 @@
TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setId(DisplayModeId(111))
- .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID)
+ .setPhysicalDisplayId(kDisplayId1)
.setVsyncPeriod(111111)
.build();
@@ -222,15 +236,15 @@
}
MATCHER(Is120Hz, "") {
- return isApproxEqual(arg.front().displayModePtr->getFps(), 120_Hz);
+ return isApproxEqual(arg.front().modePtr->getFps(), 120_Hz);
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
- auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ const auto display = mFakeDisplayInjector.injectInternalDisplay(
[&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId());
},
- mFlinger);
+ {.displayId = kDisplayId1});
mScheduler->registerDisplay(display);
mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
@@ -254,12 +268,13 @@
mScheduler->chooseRefreshRateForContent();
}
-TEST_F(SchedulerTest, getBestDisplayMode_singleDisplay) {
- auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+ const auto display = mFakeDisplayInjector.injectInternalDisplay(
[&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId());
},
- mFlinger);
+ {.displayId = kDisplayId1});
+
mScheduler->registerDisplay(display);
std::vector<RefreshRateConfigs::LayerRequirement> layers =
@@ -268,114 +283,125 @@
GlobalSignals globalSignals = {.idle = true};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(1ul, displayModeConfigs.size());
- EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode60_1);
- EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+ using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+
+ auto modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ auto choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode60, globalSignals));
globalSignals = {.idle = false};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(1ul, displayModeConfigs.size());
- EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
- EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+ modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
globalSignals = {.touch = true};
mScheduler->replaceTouchTimer(10);
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(1ul, displayModeConfigs.size());
- EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
- EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
- mScheduler->unregisterDisplay(display->getPhysicalId());
+ modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+
+ mScheduler->unregisterDisplay(kDisplayId1);
EXPECT_TRUE(mScheduler->mutableDisplays().empty());
}
-TEST_F(SchedulerTest, getBestDisplayModes_multipleDisplays) {
- auto display1 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
+ const auto display1 = mFakeDisplayInjector.injectInternalDisplay(
[&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId());
},
- mFlinger);
- auto display2 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ {.displayId = kDisplayId1, .hwcDisplayId = 42, .isPrimary = true});
+ const auto display2 = mFakeDisplayInjector.injectInternalDisplay(
[&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(makeModes(kMode60_2, kMode120_2), kMode60_2->getId());
+ injector.setDisplayModes(kDisplay2Modes, kDisplay2Mode60->getId());
},
- mFlinger, /* port */ 253u);
+ {.displayId = kDisplayId2, .hwcDisplayId = 41, .isPrimary = false});
+
mScheduler->registerDisplay(display1);
mScheduler->registerDisplay(display2);
- std::vector<sp<DisplayDevice>> expectedDisplays = {display1, display2};
- std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- GlobalSignals globalSignals = {.idle = true};
- std::vector<DisplayModeConfig> expectedConfigs = {DisplayModeConfig{globalSignals, kMode60_1},
- DisplayModeConfig{globalSignals, kMode60_2}};
+ using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+ TestableScheduler::DisplayModeChoiceMap expectedChoices;
- mScheduler->setContentRequirements(layers);
- mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(displayModeConfigs.size(), expectedConfigs.size());
- for (size_t i = 0; i < expectedConfigs.size(); ++i) {
- EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
- << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
- << " Actual fps "
- << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
- EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ {
+ const GlobalSignals globalSignals = {.idle = true};
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
+ globalSignals)(kDisplayId2, kDisplay2Mode60,
+ globalSignals);
+
+ std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f},
+ {.weight = 1.f}};
+ mScheduler->setContentRequirements(layers);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
}
+ {
+ const GlobalSignals globalSignals = {.idle = false};
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
+ globalSignals)(kDisplayId2, kDisplay2Mode120,
+ globalSignals);
- expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode120_1},
- DisplayModeConfig{globalSignals, kMode120_2}};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- globalSignals = {.idle = false};
- mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
- for (size_t i = 0; i < expectedConfigs.size(); ++i) {
- EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
- << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
- << " Actual fps "
- << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
- EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
}
+ {
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- globalSignals = {.touch = true};
- mScheduler->replaceTouchTimer(10);
- mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
- for (size_t i = 0; i < expectedConfigs.size(); ++i) {
- EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
- << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
- << " Actual fps "
- << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
- EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
+ globalSignals)(kDisplayId2, kDisplay2Mode120,
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
}
+ {
+ // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal.
+ const auto display3 = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(kDisplay3Modes, kDisplay3Mode60->getId());
+ },
+ {.displayId = kDisplayId3, .hwcDisplayId = 40, .isPrimary = false});
- // Filters out the 120Hz as it's not present on the display3, even with touch active
- // we select 60Hz here.
- auto display3 = mFakeDisplayInjector.injectDefaultInternalDisplay(
- [&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(makeModes(kMode60_3), kMode60_3->getId());
- },
- mFlinger, /* port */ 252u);
- mScheduler->registerDisplay(display3);
+ mScheduler->registerDisplay(display3);
- expectedDisplays = {display1, display2, display3};
- globalSignals = {.touch = true};
- mScheduler->replaceTouchTimer(10);
- expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode60_1},
- DisplayModeConfig{globalSignals, kMode60_2},
- DisplayModeConfig{globalSignals, kMode60_3}};
- mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
- ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
- for (size_t i = 0; i < expectedConfigs.size(); ++i) {
- EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
- << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
- << " Actual fps "
- << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
- EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
+ globalSignals)(kDisplayId2, kDisplay2Mode60,
+ globalSignals)(kDisplayId3,
+ kDisplay3Mode60,
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
}
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
index 0cf3bdf..fed6a1a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -44,9 +44,11 @@
std::move(eventThread), std::move(sfEventThread));
}
- sp<Layer> createLayer(const char* name, LayerMetadata layerMetadata) {
- return sp<Layer>::make(
- LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, layerMetadata});
+ sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
+ LayerCreationArgs args =
+ LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
+ inOutlayerMetadata = args.metadata;
+ return sp<Layer>::make(args);
}
TestableSurfaceFlinger mFlinger;
@@ -90,7 +92,7 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+ EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
}
// Test that snapshot layer metadata is set by merging the child's metadata on top of its
@@ -109,10 +111,10 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
auto expectedChildMetadata =
LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
- ASSERT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+ EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
}
// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
@@ -129,8 +131,8 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+ EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
}
// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
@@ -154,7 +156,8 @@
.build();
auto layerB = createLayer("layer-b", layerBMetadata);
auto layerBHandle = layerB->getHandle();
- auto layerC = createLayer("layer-c", {});
+ LayerMetadata layerCMetadata;
+ auto layerC = createLayer("layer-c", layerCMetadata);
auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
auto layerD = createLayer("layer-d", layerDMetadata);
auto layerDHandle = layerD->getHandle();
@@ -168,14 +171,18 @@
mFlinger.updateLayerMetadataSnapshot();
- auto expectedLayerDRelativeMetadata = LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- .build();
- ASSERT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+ auto expectedLayerDRelativeMetadata =
+ LayerMetadataBuilder()
+ // From layer A, parent of relative parent
+ .setInt32(METADATA_OWNER_UID, 1)
+ // From layer B, relative parent
+ .setInt32(METADATA_TASK_ID, 2)
+ .setInt32(METADATA_OWNER_PID, 3)
+ // added by layer creation args
+ .setInt32(gui::METADATA_CALLING_UID,
+ layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
+ .build();
+ EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
auto expectedLayerCRelativeMetadata =
LayerMetadataBuilder()
// From layer A, parent of relative parent
@@ -184,8 +191,11 @@
.setInt32(METADATA_OWNER_PID, 3)
// From layer D, relative parent
.setInt32(METADATA_TASK_ID, 4)
+ // added by layer creation args
+ .setInt32(gui::METADATA_CALLING_UID,
+ layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
.build();
- ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+ EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
}
TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
@@ -193,8 +203,10 @@
auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
auto layerA = createLayer("layer-a", layerAMetadata);
auto layerAHandle = layerA->getHandle();
- auto layerB = createLayer("layer-b", {});
- auto layerC = createLayer("layer-c", {});
+ LayerMetadata layerBMetadata;
+ auto layerB = createLayer("layer-b", layerBMetadata);
+ LayerMetadata layerCMetadata;
+ auto layerC = createLayer("layer-c", layerCMetadata);
layerB->setRelativeLayer(layerAHandle, 1);
layerC->setRelativeLayer(layerAHandle, 2);
layerA->commitChildList();
@@ -204,9 +216,9 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
- ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+ EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 68df987..26b2b67 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -107,9 +107,12 @@
mPolicy.contentRequirements = std::move(layers);
}
- std::vector<DisplayModeConfig> getBestDisplayModeConfigs() {
+ using Scheduler::DisplayModeChoice;
+ using Scheduler::DisplayModeChoiceMap;
+
+ DisplayModeChoiceMap chooseDisplayModes() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- return Scheduler::getBestDisplayModeConfigs();
+ return Scheduler::chooseDisplayModes();
}
void dispatchCachedReportedMode() {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 29ff5ee..7f471bc 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -33,6 +33,8 @@
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -118,6 +120,10 @@
sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
+ sp<LayerFE> createLayerFE(const std::string& layerName) override {
+ return sp<LayerFE>::make(layerName);
+ }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<mock::FrameTracer>();
}
@@ -397,9 +403,10 @@
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool forSystem, bool regionSampling) {
ScreenCaptureResults captureResults;
- return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem,
- regionSampling, false /* grayscale */,
- captureResults);
+ return FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer,
+ forSystem, regionSampling,
+ false /* grayscale */, captureResults));
}
auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
@@ -465,7 +472,8 @@
auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
gui::CreateSurfaceResult& outResult) {
- return mFlinger->createLayer(args, parentHandle, outResult);
+ args.parentHandle = parentHandle;
+ return mFlinger->createLayer(args, outResult);
}
auto mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
@@ -523,9 +531,7 @@
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
- auto fromHandle(const sp<IBinder>& handle) {
- return mFlinger->fromHandle(handle);
- }
+ auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index db438b7..9888f00 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -30,8 +30,8 @@
#include <vector>
#include <binder/Binder.h>
+#include "FrontEnd/TransactionHandler.h"
#include "TestableSurfaceFlinger.h"
-#include "TransactionHandler.h"
#include "mock/MockEventThread.h"
#include "mock/MockVsyncController.h"
@@ -294,7 +294,7 @@
TEST_F(TransactionApplicationTest, FromHandle) {
sp<IBinder> badHandle;
auto ret = mFlinger.fromHandle(badHandle);
- EXPECT_EQ(nullptr, ret.promote().get());
+ EXPECT_EQ(nullptr, ret.get());
}
class LatchUnsignaledTest : public TransactionApplicationTest {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index aa8b521..3808487 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -164,6 +164,8 @@
MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds));
MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*));
MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*));
+ MOCK_METHOD1(getOverlaySupport,
+ Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index a83ecbc..c78b6bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -33,4 +33,9 @@
.build();
}
+inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps refreshRate) {
+ return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 07cd15d..40f59b8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -105,6 +105,9 @@
MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override));
MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *),
(const override));
+ MOCK_METHOD(hal::Error, getOverlaySupport,
+ (aidl::android::hardware::graphics::composer3::OverlayProperties *),
+ (const override));
};
class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 8af2dfa..7d4b159 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -24,14 +24,14 @@
struct SchedulerCallback final : ISchedulerCallback {
MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
- MOCK_METHOD(void, requestDisplayModes, (std::vector<scheduler::DisplayModeConfig>), (override));
+ MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
void setVsyncEnabled(bool) override {}
- void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
};