Merge "SF: correct 90hz debug flags"
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index bb089e6..768cb4f 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -44,17 +44,18 @@
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
-static binder::Status error(uint32_t code, const std::string& msg) {
- MYLOGE("%s (%d) ", msg.c_str(), code);
- return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
-}
-
-// Takes ownership of data.
-static void* callAndNotify(void* data) {
+// 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) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
- MYLOGD("Finished Run()\n");
- return nullptr;
+ MYLOGD("Finished taking a bugreport. Exiting.\n");
+ exit(0);
+}
+
+[[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) {
+ listener->onError(error_code);
+ exit(0);
}
class DumpstateToken : public BnDumpstateToken {};
@@ -120,6 +121,25 @@
const sp<IDumpstateListener>& listener) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
+ // This is the bugreporting API flow, so 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.");
+ if (listener != nullptr) {
+ listener->onError(IDumpstateListener::BUGREPORT_ERROR_CONCURRENT_BUGREPORTS_FORBIDDEN);
+ }
+ return exception(binder::Status::EX_SERVICE_SPECIFIC,
+ "There is already a bugreport in progress");
+ }
+
+ // From here on, all conditions that indicate we are done with this incoming request should
+ // result in exiting the service to free it up for next invocation.
+ if (listener == nullptr) {
+ MYLOGE("Invalid input: no listener");
+ exit(0);
+ }
+
if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_REMOTE &&
@@ -127,30 +147,23 @@
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) {
- return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
- StringPrintf("Invalid bugreport mode: %d", bugreport_mode));
+ MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode);
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
}
if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) {
- return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Invalid file descriptor");
+ // TODO(b/111441001): screenshot fd should be optional
+ MYLOGE("Invalid filedescriptor");
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
}
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
screenshot_fd);
- // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
- // time.
- std::lock_guard<std::mutex> lock(lock_);
- if (ds_ != nullptr) {
- return exception(binder::Status::EX_SERVICE_SPECIFIC,
- "There is already a bugreport in progress");
- }
ds_ = &(Dumpstate::GetInstance());
ds_->SetOptions(std::move(options));
- if (listener != nullptr) {
- ds_->listener_ = listener;
- }
+ ds_->listener_ = listener;
DumpstateInfo* ds_info = new DumpstateInfo();
ds_info->ds = ds_;
@@ -158,11 +171,12 @@
ds_info->calling_package = calling_package;
pthread_t thread;
- status_t err = pthread_create(&thread, nullptr, callAndNotify, ds_info);
+ status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
if (err != 0) {
delete ds_info;
ds_info = nullptr;
- return error(err, "Could not create a background thread.");
+ MYLOGE("Could not create a thread");
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
}
return binder::Status::ok();
}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index b1005d3..37ff442 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -36,6 +36,9 @@
IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
boolean getSectionDetails);
+ // NOTE: If you add to or change these modes, please also change the corresponding enums
+ // in system server, in BugreportParams.java.
+
// These modes encapsulate a set of run time options for generating bugreports.
// Takes a bugreport without user interference.
const int BUGREPORT_MODE_FULL = 0;
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 907a67c..42858e0 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -19,6 +19,11 @@
/**
* Listener for dumpstate events.
*
+ * <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called.
+ *
+ * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler
+ * to handle.
+ *
* {@hide}
*/
interface IDumpstateListener {
@@ -27,7 +32,10 @@
*
* @param progress the progress in [0, 100]
*/
- oneway void onProgress(int progress);
+ void onProgress(int progress);
+
+ // NOTE: If you add to or change these error codes, please also change the corresponding enums
+ // in system server, in BugreportManager.java.
/* Options specified are invalid or incompatible */
const int BUGREPORT_ERROR_INVALID_INPUT = 1;
@@ -41,15 +49,18 @@
/* The request to get user consent timed out */
const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+ /* There is currently a bugreport running. The caller should try again later. */
+ const int BUGREPORT_ERROR_CONCURRENT_BUGREPORTS_FORBIDDEN = 5;
+
/**
* Called on an error condition with one of the error codes listed above.
*/
- oneway void onError(int errorCode);
+ void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
*/
- oneway void onFinished();
+ void onFinished();
// TODO(b/111441001): Remove old methods when not used anymore.
void onProgressUpdated(int progress);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 27abee2..60a7de2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -158,7 +158,7 @@
}
static bool CopyFileToFd(const std::string& input_file, int out_fd) {
- MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd);
+ MYLOGD("Going to copy file (%s) to %d\n", input_file.c_str(), out_fd);
// Obtain a handle to the source file.
android::base::unique_fd in_fd(OpenForRead(input_file));
@@ -166,14 +166,14 @@
if (CopyFile(in_fd.get(), out_fd)) {
return true;
}
- MYLOGE("Failed to copy zip file: %s\n", strerror(errno));
+ MYLOGE("Failed to copy file: %s\n", strerror(errno));
}
return false;
}
static bool UnlinkAndLogOnError(const std::string& file) {
- if (unlink(file.c_str()) != -1) {
- MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
+ if (unlink(file.c_str())) {
+ MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno));
return false;
}
return true;
@@ -461,9 +461,7 @@
if (!ds.AddZipEntry("anrd_trace.txt", path)) {
MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
} else {
- if (remove(path)) {
- MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
- }
+ android::os::UnlinkAndLogOnError(path);
return true;
}
} else {
@@ -1331,6 +1329,7 @@
DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
+ RunDumpsys("WINSCOPE TRACE", {"window", "trace"});
/* Add window and surface trace files. */
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(WMTRACE_DATA_DIR, false);
@@ -1592,13 +1591,8 @@
for (int i = 0; i < NUM_OF_DUMPS; i++) {
paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(),
kDumpstateBoardFiles[i].c_str()));
- remover.emplace_back(android::base::make_scope_guard(std::bind(
- [](std::string path) {
- if (remove(path.c_str()) != 0 && errno != ENOENT) {
- MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
- }
- },
- paths[i])));
+ remover.emplace_back(android::base::make_scope_guard(
+ std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
}
sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
@@ -1759,9 +1753,7 @@
ds.zip_file.reset(nullptr);
MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
- if (remove(tmp_path_.c_str()) != 0) {
- MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
- }
+ android::os::UnlinkAndLogOnError(tmp_path_);
return true;
}
@@ -2345,6 +2337,7 @@
register_sig_handler();
+ // TODO(b/111441001): maybe skip if already started?
if (options_->do_start_service) {
MYLOGI("Starting 'dumpstate' service\n");
android::status_t ret;
@@ -2491,15 +2484,24 @@
// Do an early return if there were errors. We make an exception for consent
// timing out because it's possible the user got distracted. In this case the
// bugreport is not shared but made available for manual retrieval.
+ MYLOGI("User denied consent. Returning\n");
return status;
}
- if (options_->screenshot_fd.get() != -1) {
+ if (options_->do_fb && options_->screenshot_fd.get() != -1) {
bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
options_->screenshot_fd.get());
if (copy_succeeded) {
android::os::UnlinkAndLogOnError(screenshot_path_);
}
}
+ if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+ MYLOGI(
+ "Did not receive user consent yet."
+ " Will not copy the bugreport artifacts to caller.\n");
+ // TODO(b/111441001):
+ // 1. cancel outstanding requests
+ // 2. check for result more frequently
+ }
}
/* vibrate a few but shortly times to let user know it's finished */
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 9f99114..555badd 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -21,6 +21,10 @@
#include <libgen.h>
#include <android-base/file.h>
+#include <android/os/BnDumpstate.h>
+#include <android/os/BnDumpstateListener.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <ziparchive/zip_archive.h>
@@ -34,6 +38,24 @@
using ::testing::Test;
using ::std::literals::chrono_literals::operator""s;
+using android::base::unique_fd;
+
+class DumpstateListener;
+
+namespace {
+
+sp<IDumpstate> GetDumpstateService() {
+ return android::interface_cast<IDumpstate>(
+ android::defaultServiceManager()->getService(String16("dumpstate")));
+}
+
+int OpenForWrite(const std::string& filename) {
+ return TEMP_FAILURE_RETRY(open(filename.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+}
+
+} // namespace
struct SectionInfo {
std::string name;
@@ -46,41 +68,71 @@
* Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
* section details generated by dumpstate are added to a vector to be used by Tests later.
*/
-class DumpstateListener : public IDumpstateListener {
+class DumpstateListener : public BnDumpstateListener {
public:
- int outFd_, max_progress_;
- std::shared_ptr<std::vector<SectionInfo>> sections_;
DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
- : outFd_(fd), max_progress_(5000), sections_(sections) {
+ : out_fd_(fd), sections_(sections) {
}
+
+ DumpstateListener(int fd) : out_fd_(fd) {
+ }
+
binder::Status onProgress(int32_t progress) override {
- dprintf(outFd_, "\rIn progress %d", progress);
+ dprintf(out_fd_, "\rIn progress %d", progress);
return binder::Status::ok();
}
+
binder::Status onError(int32_t error_code) override {
- dprintf(outFd_, "\rError %d", error_code);
+ std::lock_guard<std::mutex> lock(lock_);
+ error_code_ = error_code;
+ dprintf(out_fd_, "\rError code %d", error_code);
return binder::Status::ok();
}
+
binder::Status onFinished() override {
- dprintf(outFd_, "\rFinished");
+ std::lock_guard<std::mutex> lock(lock_);
+ is_finished_ = true;
+ dprintf(out_fd_, "\rFinished");
return binder::Status::ok();
}
+
binder::Status onProgressUpdated(int32_t progress) override {
- dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
+ dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_);
return binder::Status::ok();
}
+
binder::Status onMaxProgressUpdated(int32_t max_progress) override {
+ std::lock_guard<std::mutex> lock(lock_);
max_progress_ = max_progress;
return binder::Status::ok();
}
+
binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
int32_t duration_ms) override {
- sections_->push_back({name, status, size_bytes, duration_ms});
+ std::lock_guard<std::mutex> lock(lock_);
+ if (sections_.get() != nullptr) {
+ sections_->push_back({name, status, size_bytes, duration_ms});
+ }
return binder::Status::ok();
}
- IBinder* onAsBinder() override {
- return nullptr;
+
+ bool getIsFinished() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return is_finished_;
}
+
+ int getErrorCode() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return error_code_;
+ }
+
+ private:
+ int out_fd_;
+ int max_progress_ = 5000;
+ int error_code_ = -1;
+ bool is_finished_ = false;
+ std::shared_ptr<std::vector<SectionInfo>> sections_;
+ std::mutex lock_;
};
/**
@@ -293,6 +345,148 @@
SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
}
+class DumpstateBinderTest : public Test {
+ protected:
+ void SetUp() override {
+ // In case there is a stray service, stop it first.
+ property_set("ctl.stop", "bugreportd");
+ // dry_run results in a faster bugreport.
+ property_set("dumpstate.dry_run", "true");
+ // We need to receive some async calls later. Ensure we have binder threads.
+ ProcessState::self()->startThreadPool();
+ }
+
+ void TearDown() override {
+ property_set("ctl.stop", "bugreportd");
+ property_set("dumpstate.dry_run", "");
+
+ unlink("/data/local/tmp/tmp.zip");
+ unlink("/data/local/tmp/tmp.png");
+ }
+
+ // Waits until listener gets the callbacks.
+ void WaitTillExecutionComplete(DumpstateListener* listener) {
+ // Wait till one of finished, error or timeout.
+ static const int kBugreportTimeoutSeconds = 120;
+ int i = 0;
+ while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
+ i < kBugreportTimeoutSeconds) {
+ sleep(1);
+ i++;
+ }
+ }
+};
+
+TEST_F(DumpstateBinderTest, Baseline) {
+ // In the beginning dumpstate binder service is not running.
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_EQ(ds_binder, nullptr);
+
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("dumpstate.dry_run", "true");
+ property_set("ctl.start", "bugreportd");
+
+ // Now we are able to retrieve dumpstate binder service.
+ ds_binder = GetDumpstateService();
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
+
+ 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", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
+ // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
+ // gets expected callbacks.
+ EXPECT_TRUE(status.isOk());
+ WaitTillExecutionComplete(listener.get());
+
+ // Bugreport generation requires user consent, which we cannot get in a test set up,
+ // so instead of getting is_finished_, we are more likely to get a consent error.
+ EXPECT_TRUE(
+ listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
+ listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+
+ // The service should have died on its own, freeing itself up for a new invocation.
+ sleep(2);
+ ds_binder = GetDumpstateService();
+ EXPECT_EQ(ds_binder, nullptr);
+}
+
+TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("ctl.start", "bugreportd");
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+
+ EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(screenshot_fd.get(), -1);
+
+ // Call startBugreport with bad arguments.
+ sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
+ android::binder::Status status =
+ ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ 2000, // invalid bugreport mode
+ listener);
+ EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+
+ // The service should have died, freeing itself up for a new invocation.
+ sleep(2);
+ ds_binder = GetDumpstateService();
+ EXPECT_EQ(ds_binder, nullptr);
+}
+
+TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("dumpstate.dry_run", "true");
+ property_set("ctl.start", "bugreportd");
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+
+ EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(screenshot_fd.get(), -1);
+
+ sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
+ android::binder::Status status =
+ ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
+ 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", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
+ EXPECT_FALSE(status.isOk());
+ WaitTillExecutionComplete(listener2.get());
+ EXPECT_EQ(listener2->getErrorCode(),
+ IDumpstateListener::BUGREPORT_ERROR_CONCURRENT_BUGREPORTS_FORBIDDEN);
+
+ // Meanwhile the first call works as expected. Service should not die in this case.
+ WaitTillExecutionComplete(listener1.get());
+
+ // Bugreport generation requires user consent, which we cannot get in a test set up,
+ // so instead of getting is_finished_, we are more likely to get a consent error.
+ EXPECT_TRUE(
+ listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
+ listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+}
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index 3de5c79..287f2d9 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -14,6 +14,15 @@
},
{
"name": "installd_utils_test"
+ },
+ {
+ "name": "CtsUsesLibraryHostTestCases"
+ },
+ {
+ "name": "CtsClassloaderSplitsHostTestCases"
+ },
+ {
+ "name": "CtsCompilationTestCases"
}
]
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 3421800..acc0647 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -471,7 +471,7 @@
if (disable_cdex) {
AddArg(kDisableCompactDexFlag);
}
- AddArg(target_sdk_version_arg);
+ AddRuntimeArg(target_sdk_version_arg);
if (enable_hidden_api_checks) {
AddRuntimeArg("-Xhidden-api-checks");
}
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 8b66f69..92da10c 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -25,15 +25,6 @@
namespace android {
-enum {
- /* Device id of the built in keyboard. */
- DEVICE_ID_BUILT_IN_KEYBOARD = 0,
-
- /* Device id of a generic virtual keyboard with a full layout that can be used
- * to synthesize key events. */
- DEVICE_ID_VIRTUAL_KEYBOARD = -1,
-};
-
class KeyLayoutMap;
class KeyCharacterMap;
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index 566c334..b49c623 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/time.h>
+#include <ui/DisplayInfo.h>
#include <vector>
namespace android {
@@ -55,11 +56,23 @@
*/
const struct timeval& getTimestamp() const;
+ /**
+ * Rotate the video frame.
+ * The rotation value is an enum from ui/DisplayInfo.h
+ */
+ void rotate(int32_t orientation);
+
private:
uint32_t mHeight;
uint32_t mWidth;
std::vector<int16_t> mData;
struct timeval mTimestamp;
+
+ /**
+ * Common method for 90 degree and 270 degree rotation
+ */
+ void rotateQuarterTurn(bool clockwise);
+ void rotate180();
};
} // namespace android
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 35cb4a7..8a4298a 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -39,4 +39,62 @@
const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
+void TouchVideoFrame::rotate(int32_t orientation) {
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ rotateQuarterTurn(true /*clockwise*/);
+ break;
+ case DISPLAY_ORIENTATION_180:
+ rotate180();
+ break;
+ case DISPLAY_ORIENTATION_270:
+ rotateQuarterTurn(false /*clockwise*/);
+ break;
+ }
+}
+
+/**
+ * Rotate once clockwise by a quarter turn === rotate 90 degrees
+ * Rotate once counterclockwise by a quarter turn === rotate 270 degrees
+ * For a clockwise rotation:
+ * An element at position (i, j) is rotated to (j, height - i - 1)
+ * For a counterclockwise rotation:
+ * An element at position (i, j) is rotated to (width - j - 1, i)
+ */
+void TouchVideoFrame::rotateQuarterTurn(bool clockwise) {
+ std::vector<int16_t> rotated(mData.size());
+ for (size_t i = 0; i < mHeight; i++) {
+ for (size_t j = 0; j < mWidth; j++) {
+ size_t iRotated, jRotated;
+ if (clockwise) {
+ iRotated = j;
+ jRotated = mHeight - i - 1;
+ } else {
+ iRotated = mWidth - j - 1;
+ jRotated = i;
+ }
+ size_t indexRotated = iRotated * mHeight + jRotated;
+ rotated[indexRotated] = mData[i * mWidth + j];
+ }
+ }
+ mData = std::move(rotated);
+ std::swap(mHeight, mWidth);
+}
+
+/**
+ * An element at position (i, j) is rotated to (height - i - 1, width - j - 1)
+ * This is equivalent to moving element [i] to position [height * width - i - 1]
+ * Since element at [height * width - i - 1] would move to position [i],
+ * we can just swap elements [i] and [height * width - i - 1].
+ */
+void TouchVideoFrame::rotate180() {
+ if (mData.size() == 0) {
+ return;
+ }
+ // Just need to swap elements i and (height * width - 1 - i)
+ for (size_t i = 0; i < mData.size() / 2; i++) {
+ std::swap(mData[i], mData[mHeight * mWidth - 1 - i]);
+ }
+}
+
} // namespace android
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 3c1c7f3..815424e 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -67,5 +67,130 @@
ASSERT_FALSE(frame == changedTimestampFrame);
}
+// --- Rotate 90 degrees ---
+
+TEST(TouchVideoFrame, Rotate90_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_3x2_4times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
+// --- Rotate 180 degrees ---
+
+TEST(TouchVideoFrame, Rotate180_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x2_2times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x3) {
+ TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
+ TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+// --- Rotate 270 degrees ---
+
+TEST(TouchVideoFrame, Rotate270_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_3x2_4times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
} // namespace test
} // namespace android
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
new file mode 100644
index 0000000..c00fbba
--- /dev/null
+++ b/libs/renderengine/OWNERS
@@ -0,0 +1,2 @@
+lpy@google.com
+stoza@google.com
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f73d498..63e759c 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -12,8 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_defaults {
+ name: "inputflinger_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+}
+
cc_library_shared {
name: "libinputflinger",
+ defaults: ["inputflinger_defaults"],
srcs: [
"InputClassifier.cpp",
@@ -23,10 +35,10 @@
shared_libs: [
"android.hardware.input.classifier@1.0",
+ "libbase",
"libinputflinger_base",
"libinputreporter",
"libinputreader",
- "libbase",
"libbinder",
"libcutils",
"libhidlbase",
@@ -38,12 +50,6 @@
],
cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-unused-parameter",
- // TODO(b/123097103): annotate InputDispatcher and uncomment the following line
- //"-Wthread-safety",
// TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
],
@@ -55,15 +61,14 @@
}
-
cc_library_headers {
- name: "libinputflinger_headers",
-
- export_include_dirs: ["include"],
+ name: "libinputflinger_headers",
+ export_include_dirs: ["include"],
}
cc_library_shared {
name: "libinputreader",
+ defaults: ["inputflinger_defaults"],
srcs: [
"EventHub.cpp",
@@ -73,17 +78,16 @@
],
shared_libs: [
- "libinputflinger_base",
"libbase",
+ "libinputflinger_base",
"libcrypto",
"libcutils",
"libinput",
"liblog",
- "libutils",
"libui",
+ "libutils",
"libhardware_legacy",
"libstatslog",
- "libutils",
],
header_libs: [
@@ -93,17 +97,11 @@
export_header_lib_headers: [
"libinputflinger_headers",
],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-unused-parameter",
- ],
}
cc_library_shared {
name: "libinputflinger_base",
+ defaults: ["inputflinger_defaults"],
srcs: [
"InputListener.cpp",
@@ -124,24 +122,17 @@
export_header_lib_headers: [
"libinputflinger_headers",
],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-unused-parameter",
- ],
}
cc_library_shared {
name: "libinputreporter",
+ defaults: ["inputflinger_defaults"],
srcs: [
"InputReporter.cpp",
],
shared_libs: [
- "libbase",
"liblog",
"libutils",
],
@@ -153,13 +144,6 @@
export_header_lib_headers: [
"libinputflinger_headers",
],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-unused-parameter",
- ],
}
subdirs = [
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index c9eb683..4b16027 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_BLOCKING_QUEUE_H
#define _UI_INPUT_BLOCKING_QUEUE_H
+#include "android-base/thread_annotations.h"
#include <condition_variable>
#include <mutex>
#include <vector>
@@ -43,10 +44,14 @@
*/
T pop() {
std::unique_lock<std::mutex> lock(mLock);
- mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); });
+ android::base::ScopedLockAssertion assumeLock(mLock);
+ mHasElements.wait(lock, [this]{
+ android::base::ScopedLockAssertion assumeLock(mLock);
+ return !this->mQueue.empty();
+ });
T t = std::move(mQueue.front());
mQueue.erase(mQueue.begin());
- return std::move(t);
+ return t;
};
/**
@@ -56,17 +61,19 @@
* Return false if the queue is full.
*/
bool push(T&& t) {
- std::unique_lock<std::mutex> lock(mLock);
- if (mQueue.size() == mCapacity) {
- return false;
+ {
+ std::scoped_lock lock(mLock);
+ if (mQueue.size() == mCapacity) {
+ return false;
+ }
+ mQueue.push_back(std::move(t));
}
- mQueue.push_back(std::move(t));
mHasElements.notify_one();
return true;
};
void erase(const std::function<bool(const T&)>& lambda) {
- std::unique_lock<std::mutex> lock(mLock);
+ std::scoped_lock lock(mLock);
mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
[&lambda](const T& t) { return lambda(t); }), mQueue.end());
}
@@ -91,7 +98,7 @@
}
private:
- size_t mCapacity;
+ const size_t mCapacity;
/**
* Used to signal that mQueue is non-empty.
*/
@@ -100,7 +107,7 @@
* Lock for accessing and waiting on elements.
*/
std::mutex mLock;
- std::vector<T> mQueue; //GUARDED_BY(mLock)
+ std::vector<T> mQueue GUARDED_BY(mLock);
};
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 4b9dae2..b97357d 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_CLASSIFIER_H
#define _UI_INPUT_CLASSIFIER_H
+#include <android-base/thread_annotations.h>
#include <utils/RefBase.h>
#include <unordered_map>
#include <thread>
@@ -151,7 +152,7 @@
* getClassification / setClassification methods.
*/
std::unordered_map<int32_t /*deviceId*/, MotionClassification>
- mClassifications; //GUARDED_BY(mLock);
+ mClassifications GUARDED_BY(mLock);
/**
* Set the current classification for a given device.
*/
@@ -161,7 +162,7 @@
*/
MotionClassification getClassification(int32_t deviceId);
void updateClassification(int32_t deviceId, nsecs_t eventTime,
- MotionClassification classification);
+ MotionClassification classification);
/**
* Clear all current classifications
*/
@@ -172,8 +173,8 @@
*
* Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
*/
- std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/>
- mLastDownTimes; //GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock);
+
void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
/**
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 7ff8b20..2ffd0a8 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -401,7 +401,7 @@
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
- if (isAppSwitchKeyEventLocked(typedEntry)) {
+ if (isAppSwitchKeyEvent(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
@@ -409,7 +409,7 @@
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
+ && isStaleEvent(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
@@ -425,7 +425,7 @@
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
+ && isStaleEvent(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
@@ -463,7 +463,7 @@
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
+ if (isAppSwitchKeyEvent(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
@@ -615,13 +615,13 @@
}
}
-bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) {
+static bool isAppSwitchKeyCode(int32_t keyCode) {
return keyCode == AKEYCODE_HOME
|| keyCode == AKEYCODE_ENDCALL
|| keyCode == AKEYCODE_APP_SWITCH;
}
-bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
+bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
&& isAppSwitchKeyCode(keyEntry->keyCode)
&& (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
@@ -644,7 +644,7 @@
#endif
}
-bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) {
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) {
return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
}
@@ -756,7 +756,7 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+ & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
commandEntry->eventTime = entry->eventTime;
return true;
}
@@ -811,7 +811,7 @@
entry->dispatchInProgress = true;
- logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+ logOutboundKeyDetails("dispatchKey - ", entry);
}
// Handle case where the policy asked us to try again later last time.
@@ -878,7 +878,7 @@
return true;
}
-void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
"policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
@@ -896,7 +896,7 @@
if (! entry->dispatchInProgress) {
entry->dispatchInProgress = true;
- logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+ logOutboundMotionDetails("dispatchMotion - ", entry);
}
// Clean up if dropping the event.
@@ -968,7 +968,7 @@
}
-void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
", policyFlags=0x%x, "
@@ -1049,7 +1049,7 @@
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(),
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
reason);
#endif
nsecs_t timeout;
@@ -1233,8 +1233,7 @@
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
+ updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
ALOGD("findFocusedWindow finished: injectionResult=%d, "
"timeSpentWaitingForApplication=%0.1fms",
@@ -1654,8 +1653,7 @@
mTempTouchState.reset();
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
+ updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
"timeSpentWaitingForApplication=%0.1fms",
@@ -1857,7 +1855,7 @@
return "";
}
-std::string InputDispatcher::getApplicationWindowLabelLocked(
+std::string InputDispatcher::getApplicationWindowLabel(
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
@@ -1956,7 +1954,7 @@
#if DEBUG_FOCUS
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
- logOutboundMotionDetailsLocked(" ", splitMotionEntry);
+ logOutboundMotionDetails(" ", splitMotionEntry);
#endif
enqueueDispatchEntriesLocked(currentTime, connection,
splitMotionEntry, inputTarget);
@@ -2076,12 +2074,12 @@
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatchesLocked(eventEntry);
+ incrementPendingForegroundDispatches(eventEntry);
}
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2196,9 +2194,9 @@
// Re-enqueue the event on the wait queue.
connection->outboundQueue.dequeue(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
connection->waitQueue.enqueueAtTail(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
+ traceWaitQueueLength(connection);
}
}
@@ -2229,9 +2227,9 @@
// Clear the dispatch queues.
drainDispatchQueueLocked(&connection->outboundQueue);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
drainDispatchQueueLocked(&connection->waitQueue);
- traceWaitQueueLengthLocked(connection);
+ traceWaitQueueLength(connection);
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
@@ -2323,7 +2321,7 @@
} // release lock
}
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked (
const CancelationOptions& options) {
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
synthesizeCancelationEventsForConnectionLocked(
@@ -2331,7 +2329,7 @@
}
}
-void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
const CancelationOptions& options) {
for (auto& it : mMonitoringChannelsByDisplay) {
const Vector<sp<InputChannel>>& monitoringChannels = it.second;
@@ -2374,11 +2372,11 @@
EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i);
switch (cancelationEventEntry->type) {
case EventEntry::TYPE_KEY:
- logOutboundKeyDetailsLocked("cancel - ",
+ logOutboundKeyDetails("cancel - ",
static_cast<KeyEntry*>(cancelationEventEntry));
break;
case EventEntry::TYPE_MOTION:
- logOutboundMotionDetailsLocked("cancel - ",
+ logOutboundMotionDetails("cancel - ",
static_cast<MotionEntry*>(cancelationEventEntry));
break;
}
@@ -2994,7 +2992,7 @@
}
}
-void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches += 1;
@@ -3014,7 +3012,7 @@
Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const {
std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it =
- mWindowHandlesByDisplay.find(displayId);
+ mWindowHandlesByDisplay.find(displayId);
if(it != mWindowHandlesByDisplay.end()) {
return it->second;
}
@@ -3038,8 +3036,7 @@
return nullptr;
}
-bool InputDispatcher::hasWindowHandleLocked(
- const sp<InputWindowHandle>& windowHandle) const {
+bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
for (auto& it : mWindowHandlesByDisplay) {
const Vector<sp<InputWindowHandle>> windowHandles = it.second;
size_t numWindows = windowHandles.size();
@@ -3884,7 +3881,7 @@
float waitDuration = (currentTime - waitStartTime) * 0.000001f;
ALOGI("Application is not responding: %s. "
"It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(),
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
dispatchLatency, waitDuration, reason);
// Capture a record of the InputDispatcher state at the time of the ANR.
@@ -3897,7 +3894,7 @@
mLastANRState += INDENT "ANR:\n";
mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
mLastANRState += StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str());
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
@@ -3911,7 +3908,7 @@
commandEntry->reason = reason;
}
-void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible (
CommandEntry* commandEntry) {
mLock.unlock();
@@ -4027,10 +4024,10 @@
// a few things.
if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
connection->waitQueue.dequeue(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
+ traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.enqueueAtHead(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
} else {
releaseDispatchEntryLocked(dispatchEntry);
}
@@ -4242,7 +4239,7 @@
entry->downTime, entry->eventTime);
}
-void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
}
@@ -4253,7 +4250,7 @@
}
}
-void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& connection) {
+void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
@@ -4261,7 +4258,7 @@
}
}
-void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connection) {
+void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 595b01d..ecfeb6c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -888,57 +888,58 @@
sp<Looper> mLooper;
- EventEntry* mPendingEvent;
- Queue<EventEntry> mInboundQueue;
- Queue<EventEntry> mRecentQueue;
- Queue<CommandEntry> mCommandQueue;
+ EventEntry* mPendingEvent GUARDED_BY(mLock);
+ Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
+ Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
+ Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);
- DropReason mLastDropReason;
+ DropReason mLastDropReason GUARDED_BY(mLock);
- void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
+ void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry);
+ bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
// Cleans up input state when dropping an inbound event.
- void dropInboundEventLocked(EventEntry* entry, DropReason dropReason);
+ void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry);
+ void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
// App switch latency optimization.
- bool mAppSwitchSawKeyDown;
- nsecs_t mAppSwitchDueTime;
+ bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
+ nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
- static bool isAppSwitchKeyCode(int32_t keyCode);
- bool isAppSwitchKeyEventLocked(KeyEntry* keyEntry);
- bool isAppSwitchPendingLocked();
- void resetPendingAppSwitchLocked(bool handled);
+ bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+ bool isAppSwitchPendingLocked() REQUIRES(mLock);
+ void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
// Stale event latency optimization.
- static bool isStaleEventLocked(nsecs_t currentTime, EventEntry* entry);
+ static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent;
+ EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
- bool addOutsideTargets = false, bool addPortalWindows = false);
+ bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
- KeyedVector<int, sp<Connection> > mConnectionsByFd;
+ KeyedVector<int, sp<Connection> > mConnectionsByFd GUARDED_BY(mLock);
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
return std::hash<IBinder *>{}(b.get());
}
};
- std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken;
+ std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
+ GUARDED_BY(mLock);
- ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
+ ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
- std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay;
+ std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay
+ GUARDED_BY(mLock);
// Event injection and synchronization.
Condition mInjectionResultAvailableCondition;
@@ -946,17 +947,17 @@
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
Condition mInjectionSyncFinishedCondition;
- void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
+ void incrementPendingForegroundDispatches(EventEntry* entry);
void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
// Key repeat tracking.
struct KeyRepeatState {
KeyEntry* lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
- } mKeyRepeatState;
+ } mKeyRepeatState GUARDED_BY(mLock);
- void resetKeyRepeatLocked();
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
+ void resetKeyRepeatLocked() REQUIRES(mLock);
+ KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
// Key replacement tracking
struct KeyReplacement {
@@ -970,39 +971,42 @@
}
};
// Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys;
+ KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
// Process certain Meta + Key combinations
void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
int32_t& keyCode, int32_t& metaState);
// Deferred command processing.
- bool haveCommandsLocked() const;
- bool runCommandsLockedInterruptible();
- CommandEntry* postCommandLocked(Command command);
+ bool haveCommandsLocked() const REQUIRES(mLock);
+ bool runCommandsLockedInterruptible() REQUIRES(mLock);
+ CommandEntry* postCommandLocked(Command command) REQUIRES(mLock);
// Input filter processing.
- bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args);
- bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args);
+ bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
+ bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
// Inbound event processing.
- void drainInboundQueueLocked();
- void releasePendingEventLocked();
- void releaseInboundEventLocked(EventEntry* entry);
+ void drainInboundQueueLocked() REQUIRES(mLock);
+ void releasePendingEventLocked() REQUIRES(mLock);
+ void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
// Dispatch state.
- bool mDispatchEnabled;
- bool mDispatchFrozen;
- bool mInputFilterEnabled;
+ bool mDispatchEnabled GUARDED_BY(mLock);
+ bool mDispatchFrozen GUARDED_BY(mLock);
+ bool mInputFilterEnabled GUARDED_BY(mLock);
- std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay;
+ std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
+ GUARDED_BY(mLock);
// Get window handles by display, return an empty vector if not found.
- Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const;
- sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const;
- sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const;
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
+ Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock);
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
+ REQUIRES(mLock);
+ sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+ bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
// Focus tracking for keys, trackball, etc.
- std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay;
+ std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
+ GUARDED_BY(mLock);
// Focus tracking for touch.
struct TouchedWindow {
@@ -1037,34 +1041,35 @@
bool isSlippery() const;
};
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay;
- TouchState mTempTouchState;
+ KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+ TouchState mTempTouchState GUARDED_BY(mLock);
// Focused applications.
- std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay;
+ std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
+ GUARDED_BY(mLock);
// Top focused display.
- int32_t mFocusedDisplayId;
+ int32_t mFocusedDisplayId GUARDED_BY(mLock);
// Dispatcher state at time of last ANR.
- std::string mLastANRState;
+ std::string mLastANRState GUARDED_BY(mLock);
// Dispatch inbound events.
bool dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry);
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock);
bool dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry);
+ nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
bool dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
bool dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
- const Vector<InputTarget>& inputTargets);
+ const Vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
- void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
+ void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
+ void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
// Keeping track of ANR timeouts.
enum InputTargetWaitCause {
@@ -1073,130 +1078,133 @@
INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
};
- InputTargetWaitCause mInputTargetWaitCause;
- nsecs_t mInputTargetWaitStartTime;
- nsecs_t mInputTargetWaitTimeoutTime;
- bool mInputTargetWaitTimeoutExpired;
- sp<IBinder> mInputTargetWaitApplicationToken;
+ InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
+ nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
+ nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
+ bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
+ sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
// Contains the last window which received a hover event.
- sp<InputWindowHandle> mLastHoverWindowHandle;
+ sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
// Finding targets for input events.
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason);
+ nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock);
- void removeWindowByTokenLocked(const sp<IBinder>& token);
+ void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel);
- nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
- void resetANRTimeoutsLocked();
+ const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
+ void resetANRTimeoutsLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry* entry);
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
- Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
+ Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock);
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions);
+ bool* outConflictingPointerActions) REQUIRES(mLock);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
+ int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets)
+ REQUIRES(mLock);
void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId,
- float xOffset = 0, float yOffset = 0);
+ float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
- void pokeUserActivityLocked(const EventEntry* eventEntry);
+ void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t x, int32_t y) const;
- bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const;
- std::string getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
+ int32_t x, int32_t y) const REQUIRES(mLock);
+ bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle);
std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
- const char* targetType);
+ const char* targetType) REQUIRES(mLock);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
+ EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
+ EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntryLocked(const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+ REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled);
+ uint32_t seq, bool handled) REQUIRES(mLock);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool notify);
+ bool notify) REQUIRES(mLock);
void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
void synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options);
- void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
// Splitting motion events across windows.
MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
// Reset and drop everything the dispatcher is doing.
- void resetAndDropEverythingLocked(const char* reason);
+ void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
// Dump state.
- void dumpDispatchStateLocked(std::string& dump);
- void logDispatchStateLocked();
+ void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
+ void logDispatchStateLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify);
-
- // Add or remove a connection to the mActiveConnections vector.
- void activateConnectionLocked(Connection* connection);
- void deactivateConnectionLocked(Connection* connection);
+ void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
+ nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled)
+ REQUIRES(mLock);
void onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection);
+ nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus);
+ const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
void onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
- nsecs_t eventTime, nsecs_t waitStartTime, const char* reason);
+ nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
// Outbound policy interactions.
- void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
- void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
- void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled);
+ DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled);
- void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+ void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
// Statistics gathering.
- void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
- void traceInboundQueueLengthLocked();
- void traceOutboundQueueLengthLocked(const sp<Connection>& connection);
- void traceWaitQueueLengthLocked(const sp<Connection>& connection);
+ void traceInboundQueueLengthLocked() REQUIRES(mLock);
+ void traceOutboundQueueLength(const sp<Connection>& connection);
+ void traceWaitQueueLength(const sp<Connection>& connection);
sp<InputReporterInterface> mReporter;
};
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 1a1ae21..21ba029 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -6565,6 +6565,8 @@
const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+ std::for_each(frames.begin(), frames.end(),
+ [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
source, displayId, policyFlags,
action, actionButton, flags, metaState, buttonState, MotionClassification::NONE,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index fbacb9b..80a55f1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -335,6 +335,7 @@
KeyedVector<int32_t, Device*> mDevices;
std::vector<std::string> mExcludedDevices;
List<RawEvent> mEvents;
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
protected:
virtual ~FakeEventHub() {
@@ -499,6 +500,11 @@
}
}
+ void setVideoFrames(std::unordered_map<int32_t /*deviceId*/,
+ std::vector<TouchVideoFrame>> videoFrames) {
+ mVideoFrames = std::move(videoFrames);
+ }
+
void assertQueueIsEmpty() {
ASSERT_EQ(size_t(0), mEvents.size())
<< "Expected the event queue to be empty (fully consumed).";
@@ -614,6 +620,12 @@
}
virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+ auto it = mVideoFrames.find(deviceId);
+ if (it != mVideoFrames.end()) {
+ std::vector<TouchVideoFrame> frames = std::move(it->second);
+ mVideoFrames.erase(deviceId);
+ return frames;
+ }
return {};
}
@@ -6415,4 +6427,78 @@
ASSERT_EQ(size_t(2), iter->second.size());
}
+TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ addMapperAndConfigure(mapper);
+
+ NotifyMotionArgs motionArgs;
+ // Unrotated video frame
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ std::vector<TouchVideoFrame> frames{frame};
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+
+ // Subsequent touch events should not have any videoframes
+ // This is implemented separately in FakeEventHub,
+ // but that should match the behaviour of TouchVideoDevice.
+ processPosition(mapper, 200, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames);
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addMapperAndConfigure(mapper);
+ // Unrotated video frame
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ NotifyMotionArgs motionArgs;
+
+ // Test all 4 orientations
+ for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
+ DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+ clearViewports();
+ prepareDisplay(orientation);
+ std::vector<TouchVideoFrame> frames{frame};
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ frames[0].rotate(orientation);
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+ }
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addMapperAndConfigure(mapper);
+ // Unrotated video frames. There's no rule that they must all have the same dimensions,
+ // so mix these.
+ TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3});
+ TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4});
+ std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
+ NotifyMotionArgs motionArgs;
+
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ std::for_each(frames.begin(), frames.end(),
+ [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); });
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index b3e2a4b..96c4992 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -397,9 +397,11 @@
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
- // Report the requested present time to the Scheduler.
- mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp, item.mIsAutoTimestamp,
- mName.c_str());
+ if (mFlinger->mUse90Hz && mFlinger->mUseSmart90ForVideo) {
+ // Report the requested present time to the Scheduler, if the feature is turned on.
+ mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
+ item.mIsAutoTimestamp, mName.c_str());
+ }
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 697d634..d848c97 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -85,7 +85,19 @@
}
}
+void DispSyncSource::pauseVsyncCallback(bool pause) {
+ std::lock_guard lock(mVsyncMutex);
+ mCallbackPaused = pause;
+}
+
void DispSyncSource::onDispSyncEvent(nsecs_t when) {
+ {
+ std::lock_guard lock(mVsyncMutex);
+ if (mCallbackPaused) {
+ return;
+ }
+ }
+
VSyncSource::Callback* callback;
{
std::lock_guard lock(mCallbackMutex);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 0fd84a2..5e3d181 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -33,6 +33,7 @@
void setVSyncEnabled(bool enable) override;
void setCallback(VSyncSource::Callback* callback) override;
void setPhaseOffset(nsecs_t phaseOffset) override;
+ void pauseVsyncCallback(bool pause) override;
private:
// The following method is the implementation of the DispSync::Callback.
@@ -53,6 +54,7 @@
std::mutex mVsyncMutex;
nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
+ bool mCallbackPaused GUARDED_BY(mVsyncMutex) = false;
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 5d9cfde..4b500f1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -203,6 +203,12 @@
mVSyncSource->setPhaseOffset(phaseOffset);
}
+void EventThread::pauseVsyncCallback(bool pause) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ ATRACE_INT("vsyncPaused", pause);
+ mVSyncSource->pauseVsyncCallback(pause);
+}
+
sp<EventThreadConnection> EventThread::createEventConnection(
ResyncCallback resyncCallback, ResetIdleTimerCallback resetIdleTimerCallback) const {
return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index b275183..3411438 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,9 @@
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ // pause/resume vsync callback generation
+ virtual void pauseVsyncCallback(bool pause) = 0;
};
class EventThreadConnection : public BnDisplayEventConnection {
@@ -119,6 +122,8 @@
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
bool resetIdleTimer) = 0;
+
+ virtual void pauseVsyncCallback(bool pause) = 0;
};
namespace impl {
@@ -152,6 +157,8 @@
void setPhaseOffset(nsecs_t phaseOffset) override;
+ void pauseVsyncCallback(bool pause) override;
+
private:
friend EventThreadTest;
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index a0e1447..90609af 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -44,6 +44,7 @@
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
+ void pauseVsyncCallback(bool) {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 30fba3c..4f846db 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -163,6 +163,12 @@
mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
}
+void Scheduler::pauseVsyncCallback(const android::sp<android::Scheduler::ConnectionHandle>& handle,
+ bool pause) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->pauseVsyncCallback(pause);
+}
+
void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
@@ -293,6 +299,7 @@
}
void Scheduler::incrementFrameCounter() {
+ std::lock_guard<std::mutex> lock(mLayerHistoryLock);
mLayerHistory.incrementCounter();
}
@@ -317,25 +324,28 @@
void Scheduler::determineLayerTimestampStats(const std::string layerName,
const nsecs_t framePresentTime) {
- mLayerHistory.insert(layerName, framePresentTime);
std::vector<int64_t> differencesMs;
-
- // Traverse through the layer history, and determine the differences in present times.
- nsecs_t newestPresentTime = framePresentTime;
std::string differencesText = "";
- for (int i = 1; i < mLayerHistory.getSize(); i++) {
- std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
- for (auto layer : layers) {
- if (layer.first != layerName) {
- continue;
+ {
+ std::lock_guard<std::mutex> lock(mLayerHistoryLock);
+ mLayerHistory.insert(layerName, framePresentTime);
+
+ // Traverse through the layer history, and determine the differences in present times.
+ nsecs_t newestPresentTime = framePresentTime;
+ for (int i = 1; i < mLayerHistory.getSize(); i++) {
+ std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
+ for (auto layer : layers) {
+ if (layer.first != layerName) {
+ continue;
+ }
+ int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
+ // Dismiss noise.
+ if (differenceMs > 10 && differenceMs < 60) {
+ differencesMs.push_back(differenceMs);
+ }
+ IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
+ newestPresentTime = layer.second;
}
- int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
- // Dismiss noise.
- if (differenceMs > 10 && differenceMs < 60) {
- differencesMs.push_back(differenceMs);
- }
- IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
- newestPresentTime = layer.second;
}
}
ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7b5278c..c566922 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -111,6 +111,9 @@
// Offers ability to modify phase offset in the event thread.
void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset);
+ // pause/resume vsync callback generation to avoid sending vsync callbacks during config switch
+ void pauseVsyncCallback(const sp<ConnectionHandle>& handle, bool pause);
+
void getDisplayStatInfo(DisplayStatInfo* stats);
void enableHardwareVsync();
@@ -198,7 +201,10 @@
std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
size_t mCounter = 0;
- LayerHistory mLayerHistory;
+ // DetermineLayerTimestampStats is called from BufferQueueLayer::onFrameAvailable which
+ // can run on any thread, and cause failure.
+ std::mutex mLayerHistoryLock;
+ LayerHistory mLayerHistory GUARDED_BY(mLayerHistoryLock);
// Timer that records time between requests for next vsync. If the time is higher than a given
// interval, a callback is fired. Set this variable to >0 to use this feature.
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 0bf3ceb..dab2003 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -54,6 +54,15 @@
mEarlyOffsets = early;
mEarlyGlOffsets = earlyGl;
mLateOffsets = late;
+
+ if (mSfConnectionHandle && late.sf != mOffsets.load().sf) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf);
+ }
+
+ if (mAppConnectionHandle && late.app != mOffsets.load().app) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, late.app);
+ }
+
mOffsets = late;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2cb5baa..03c144b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -396,6 +396,9 @@
property_get("debug.sf.use_90Hz", value, "0");
mUse90Hz = atoi(value);
+ property_get("debug.sf.use_smart_90_for_video", value, "0");
+ mUseSmart90ForVideo = atoi(value);
+
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
mVsyncModulator.setPhaseOffsets(early, gl, late);
@@ -960,12 +963,18 @@
// Don't check against the current mode yet. Worst case we set the desired
// config twice.
- {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+
+ if (!mDesiredActiveConfigChanged) {
+ // This is the first time we set the desired
+ mScheduler->pauseVsyncCallback(mAppConnectionHandle, true);
+
+ // This will trigger HWC refresh without resetting the idle timer.
+ repaintEverythingForHWC();
}
- // This will trigger HWC refresh without resetting the idle timer.
- repaintEverythingForHWC();
+ mDesiredActiveConfigChanged = true;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
}
status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
@@ -975,23 +984,6 @@
return NO_ERROR;
}
-void SurfaceFlinger::setActiveConfigInHWC() {
- ATRACE_CALL();
-
- const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
- if (!display) {
- return;
- }
-
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
-
- ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
- getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
- mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
- ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
-}
-
void SurfaceFlinger::setActiveConfigInternal() {
ATRACE_CALL();
@@ -1001,51 +993,68 @@
const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
display->setActiveConfig(mUpcomingActiveConfig.configId);
- mSetActiveConfigState = SetActiveConfigState::NONE;
- ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
-
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
}
-bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
+ // we may be in the process of changing the active state
+ if (mWaitForNextInvalidate) {
+ mWaitForNextInvalidate = false;
+
+ repaintEverythingForHWC();
+ // We do not want to give another frame to HWC while we are transitioning.
+ return true;
+ }
+
+ if (mCheckPendingFence) {
+ if (mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
+ // fence has not signaled yet. wait for the next invalidate
+ repaintEverythingForHWC();
+ return true;
+ }
+
+ // We received the present fence from the HWC, so we assume it successfully updated
+ // the config, hence we update SF.
+ mCheckPendingFence = false;
+ setActiveConfigInternal();
+ }
+
// Store the local variable to release the lock.
ActiveConfigInfo desiredActiveConfig;
{
std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (!mDesiredActiveConfigChanged) {
+ return false;
+ }
desiredActiveConfig = mDesiredActiveConfig;
}
const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
- if (display) {
- if (mSetActiveConfigState == SetActiveConfigState::NONE &&
- display->getActiveConfig() != desiredActiveConfig.configId) {
- // Step 1) Desired active config was set, it is different than the
- // config currently in use. Notify HWC.
- mUpcomingActiveConfig = desiredActiveConfig;
- setActiveConfigInHWC();
- } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
- // Step 2) HWC was notified and we received refresh from it.
- mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
- ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
- repaintEverythingForHWC();
- // We do not want to give another frame to HWC while we are transitioning.
- return false;
- } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
- !(mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
- // Step 3) We received the present fence from the HWC, so we assume it
- // successfully updated the config, hence we update SF.
- setActiveConfigInternal();
- // If the config changed again while we were transitioning, restart the
- // process.
- if (desiredActiveConfig != mUpcomingActiveConfig) {
- mUpcomingActiveConfig = desiredActiveConfig;
- setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
- }
- }
+ if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
+ // display is not valid or we are already in the requested mode
+ // on both cases there is nothing left to do
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mScheduler->pauseVsyncCallback(mAppConnectionHandle, false);
+ mDesiredActiveConfigChanged = false;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ return false;
}
- return true;
+
+ // Desired active config was set, it is different than the config currently in use. Notify HWC.
+ mUpcomingActiveConfig = desiredActiveConfig;
+ const auto displayId = display->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+ getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+
+ // we need to submit an empty frame to HWC to start the process
+ mWaitForNextInvalidate = true;
+ mCheckPendingFence = true;
+
+ return false;
}
status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1586,12 +1595,15 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- if (!updateSetActiveConfigStateMachine()) {
+ if (performSetActiveConfig()) {
break;
}
- // This call is made each time SF wakes up and creates a new frame.
- mScheduler->incrementFrameCounter();
+ if (mUse90Hz && mUseSmart90ForVideo) {
+ // This call is made each time SF wakes up and creates a new frame. It is part
+ // of video detection feature.
+ mScheduler->incrementFrameCounter();
+ }
bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE &&
(mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
@@ -4460,7 +4472,9 @@
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
- StringAppendF(&result, "Scheduler enabled. 90Hz feature: %s\n\n", mUse90Hz ? "on" : "off");
+ StringAppendF(&result, "Scheduler enabled. 90Hz feature: %s\n", mUse90Hz ? "on" : "off");
+ StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n",
+ mUseSmart90ForVideo ? "on" : "off");
mScheduler->dump(mAppConnectionHandle, result);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b1f0992..2d9e230 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -511,16 +511,14 @@
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
- // Calls setActiveConfig in HWC.
- void setActiveConfigInHWC();
// Once HWC has returned the present fence, this sets the active config and a new refresh
// rate in SF. It also triggers HWC vsync.
void setActiveConfigInternal() REQUIRES(mStateLock);
// Active config is updated on INVALIDATE call in a state machine-like manner. When the
- // desired config was set, HWC needs to update the pannel on the next refresh, and when
+ // desired config was set, HWC needs to update the panel on the next refresh, and when
// we receive the fence back, we know that the process was complete. It returns whether
- // the invalidate process should continue.
- bool updateSetActiveConfigStateMachine();
+ // we need to wait for the next invalidate
+ bool performSetActiveConfig();
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
@@ -1077,20 +1075,12 @@
* Scheduler
*/
bool mUse90Hz = false;
+ bool mUseSmart90ForVideo = false;
std::unique_ptr<Scheduler> mScheduler;
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
- // The following structs are used for configuring active config state at a desired time,
- // which is once per vsync at invalidate time.
- enum SetActiveConfigState {
- NONE, // not in progress
- NOTIFIED_HWC, // call to HWC has been made
- REFRESH_RECEIVED // onRefresh was received from HWC
- };
- std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE;
-
struct ActiveConfigInfo {
int configId;
sp<IBinder> displayToken;
@@ -1110,6 +1100,11 @@
// This bit can be set at any point in time when the system wants the new config.
ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+ // below flags are set by main thread only
+ bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
+ bool mWaitForNextInvalidate = false;
+ bool mCheckPendingFence = false;
+
/* ------------------------------------------------------------------------ */
sp<IInputFlinger> mInputFlinger;
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 3a7cfba..e499ff5 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -44,6 +44,7 @@
MOCK_METHOD1(setVSyncEnabled, void(bool));
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+ MOCK_METHOD1(pauseVsyncCallback, void(bool));
};
} // namespace
@@ -71,6 +72,7 @@
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ void expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause);
VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
void expectInterceptCallReceived(nsecs_t expectedTimestamp);
void expectVsyncEventReceivedByConnection(const char* name,
@@ -83,6 +85,7 @@
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)(bool)> mVSyncPauseVsyncCallbackCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)()> mResetIdleTimerCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
@@ -108,6 +111,9 @@
EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
.WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+ EXPECT_CALL(mVSyncSource, pauseVsyncCallback(_))
+ .WillRepeatedly(Invoke(mVSyncPauseVsyncCallbackCallRecorder.getInvocable()));
+
createThread();
mConnection = createConnection(mConnectionEventCallRecorder);
@@ -157,6 +163,12 @@
EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
}
+void EventThreadTest::expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause) {
+ auto args = mVSyncPauseVsyncCallbackCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedPause, std::get<0>(args.value()));
+}
+
VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
@@ -406,6 +418,16 @@
expectVSyncSetPhaseOffsetCallReceived(321);
}
+TEST_F(EventThreadTest, pauseVsyncCallbackForwardsToVSyncSource) {
+ mThread->pauseVsyncCallback(true);
+ expectVSyncPauseVsyncCallbackCallReceived(true);
+}
+
+TEST_F(EventThreadTest, resumeVsyncCallbackForwardsToVSyncSource) {
+ mThread->pauseVsyncCallback(false);
+ expectVSyncPauseVsyncCallbackCallReceived(false);
+}
+
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 589237d..5edee6e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -39,6 +39,7 @@
status_t(const sp<android::EventThreadConnection> &));
MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool));
+ MOCK_METHOD1(pauseVsyncCallback, void(bool));
};
} // namespace mock