Merge "[SurfaceFlinger] Add refresh rate counts to TimeStats" into rvc-dev
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 8dad475..be2c702 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -185,7 +185,7 @@
int argc = argv.size();
if (argc == 0) {
- errorLog << "cmd: No service specified; use -l to list all services" << endl;
+ errorLog << "cmd: No service specified; use -l to list all running services. Use -w to start and wait for a service." << endl;
return 20;
}
@@ -203,14 +203,22 @@
return 0;
}
- const auto cmd = argv[0];
+ bool waitForService = ((argc > 1) && (argv[0] == "-w"));
+ int serviceIdx = (waitForService) ? 1 : 0;
+ const auto cmd = argv[serviceIdx];
Vector<String16> args;
String16 serviceName = String16(cmd.data(), cmd.size());
- for (int i = 1; i < argc; i++) {
+ for (int i = serviceIdx + 1; i < argc; i++) {
args.add(String16(argv[i].data(), argv[i].size()));
}
- sp<IBinder> service = sm->checkService(serviceName);
+ sp<IBinder> service;
+ if(waitForService) {
+ service = sm->waitForService(serviceName);
+ } else {
+ service = sm->checkService(serviceName);
+ }
+
if (service == nullptr) {
if (runMode == RunMode::kStandalone) {
ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index 466575f..bfcc058 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -84,7 +84,8 @@
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd,
int bugreport_mode,
- const sp<IDumpstateListener>& listener) {
+ const sp<IDumpstateListener>& listener,
+ bool is_screenshot_requested) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
// Ensure there is only one bugreport in progress at a time.
@@ -118,7 +119,7 @@
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
- screenshot_fd);
+ screenshot_fd, is_screenshot_requested);
if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
MYLOGE("Invalid filedescriptor");
@@ -136,6 +137,8 @@
ds_info->calling_package = calling_package;
pthread_t thread;
+ // Initialize dumpstate
+ ds_->Initialize();
status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
if (err != 0) {
delete ds_info;
@@ -147,14 +150,13 @@
}
binder::Status DumpstateService::cancelBugreport() {
- // This is a no-op since the cancellation is done from java side via setting sys properties.
- // See BugreportManagerServiceImpl.
- // TODO(b/111441001): maybe make native and java sides use different binder interface
- // to avoid these annoyances.
+ std::lock_guard<std::mutex> lock(lock_);
+ ds_->Cancel();
return binder::Status::ok();
}
status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+ std::lock_guard<std::mutex> lock(lock_);
if (ds_ == nullptr) {
dprintf(fd, "Bugreport not in progress yet");
return NO_ERROR;
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 6dc0225..ac8d3ac 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -41,7 +41,8 @@
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd, int bugreport_mode,
- const sp<IDumpstateListener>& listener) override;
+ const sp<IDumpstateListener>& listener,
+ bool is_screenshot_requested) override;
// No-op
binder::Status cancelBugreport();
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 3f359c8..ba008bb 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -64,10 +64,12 @@
* @param screenshotFd the file to which screenshot should be written
* @param bugreportMode the mode that specifies other run time options; must be one of above
* @param listener callback for updates; optional
+ * @param isScreenshotRequested indicates screenshot is requested or not
*/
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener);
+ int bugreportMode, IDumpstateListener listener,
+ boolean isScreenshotRequested);
/*
* Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index e17f18e..8ff4ca6 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -32,7 +32,7 @@
*
* @param progress the progress in [0, 100]
*/
- void onProgress(int progress);
+ oneway 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.
@@ -54,16 +54,18 @@
/**
* Called on an error condition with one of the error codes listed above.
+ * This is not an asynchronous method since it can race with dumpstate exiting, thus triggering
+ * death recipient.
*/
void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
*/
- void onFinished();
+ oneway void onFinished();
/**
* Called when screenshot is taken.
*/
- void onScreenshotTaken(boolean success);
+ oneway void onScreenshotTaken(boolean success);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0a3d33d..b475c90 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -239,6 +239,9 @@
}
static bool UnlinkAndLogOnError(const std::string& file) {
+ if (file.empty()) {
+ return false;
+ }
if (unlink(file.c_str())) {
MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno));
return false;
@@ -246,7 +249,6 @@
return true;
}
-
int64_t GetModuleMetadataVersion() {
auto binder = defaultServiceManager()->getService(android::String16("package_native"));
if (binder == nullptr) {
@@ -2171,7 +2173,7 @@
}
if (ds.options_->do_screenshot) {
- ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
+ ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-png.tmp" : ".png");
}
ds.tmp_path_ = ds.GetPath(".tmp");
ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
@@ -2190,7 +2192,7 @@
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (ds.options_->do_zip_file) {
- ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
+ ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
create_parent_dirs(ds.path_.c_str());
ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2249,20 +2251,21 @@
}
}
-static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) {
+static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options,
+ bool is_screenshot_requested) {
// Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for
// default system screenshots.
options->bugreport_mode = ModeToString(mode);
switch (mode) {
case Dumpstate::BugreportMode::BUGREPORT_FULL:
- options->do_screenshot = true;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::FULL;
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
options->do_start_service = true;
options->do_progress_updates = true;
- options->do_screenshot = true;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
break;
case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
@@ -2275,7 +2278,7 @@
options->do_start_service = true;
options->do_progress_updates = true;
options->do_zip_file = true;
- options->do_screenshot = true;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::WEAR;
break;
// TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
@@ -2312,7 +2315,8 @@
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
const android::base::unique_fd& bugreport_fd_in,
- const android::base::unique_fd& screenshot_fd_in) {
+ const android::base::unique_fd& screenshot_fd_in,
+ bool is_screenshot_requested) {
// In the new API world, date is always added; output is always a zip file.
// TODO(111441001): remove these options once they are obsolete.
do_add_date = true;
@@ -2322,7 +2326,7 @@
bugreport_fd.reset(dup(bugreport_fd_in.get()));
screenshot_fd.reset(dup(screenshot_fd_in.get()));
- SetOptionsFromMode(bugreport_mode, this);
+ SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested);
}
Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
@@ -2391,6 +2395,13 @@
options_ = std::move(options);
}
+void Dumpstate::Initialize() {
+ /* gets the sequential id */
+ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+ id_ = ++last_id;
+ android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+}
+
Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
if (listener_ != nullptr) {
@@ -2417,6 +2428,17 @@
return status;
}
+void Dumpstate::Cancel() {
+ CleanupTmpFiles();
+ android::os::UnlinkAndLogOnError(log_path_);
+ for (int i = 0; i < NUM_OF_DUMPS; i++) {
+ android::os::UnlinkAndLogOnError(ds.bugreport_internal_dir_ + "/" +
+ kDumpstateBoardFiles[i]);
+ }
+ tombstone_data_.clear();
+ anr_data_.clear();
+}
+
/*
* Dumps relevant information to a bugreport based on the given options.
*
@@ -2490,11 +2512,6 @@
: "";
progress_.reset(new Progress(stats_path));
- /* gets the sequential id */
- uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
- id_ = ++last_id;
- android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
-
if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
} else {
@@ -2753,7 +2770,7 @@
return ds.options_->bugreport_fd.get() != -1 ? true : false;
}
-void Dumpstate::CleanupFiles() {
+void Dumpstate::CleanupTmpFiles() {
android::os::UnlinkAndLogOnError(tmp_path_);
android::os::UnlinkAndLogOnError(screenshot_path_);
android::os::UnlinkAndLogOnError(path_);
@@ -2761,7 +2778,7 @@
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
- CleanupFiles();
+ CleanupTmpFiles();
return USER_CONSENT_DENIED;
}
@@ -2822,8 +2839,9 @@
assert(options_->bugreport_fd.get() == -1);
// calling_uid and calling_package are for user consent to share the bugreport with
- // an app; they are irrelvant here because bugreport is only written to a local
- // directory, and not shared.
+ // an app; they are irrelevant here because bugreport is triggered via command line.
+ // Update Last ID before calling Run().
+ Initialize();
status = Run(-1 /* calling_uid */, "" /* calling_package */);
}
return status;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 7e27787..498f338 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -216,6 +216,9 @@
/* Checkes whether dumpstate is generating a zipped bugreport. */
bool IsZipping() const;
+ /* Initialize dumpstate fields before starting bugreport generation */
+ void Initialize();
+
/*
* Forks a command, waits for it to finish, and returns its status.
*
@@ -329,11 +332,19 @@
struct DumpOptions;
- /* Main entry point for running a complete bugreport. */
+ /*
+ * Main entry point for running a complete bugreport.
+ *
+ * Initialize() dumpstate before calling this method.
+ *
+ */
RunStatus Run(int32_t calling_uid, const std::string& calling_package);
RunStatus ParseCommandlineAndRun(int argc, char* argv[]);
+ /* Deletes in-progress files */
+ void Cancel();
+
/* Sets runtime options. */
void SetOptions(std::unique_ptr<DumpOptions> options);
@@ -390,7 +401,8 @@
/* Initializes options from the requested mode. */
void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd);
+ const android::base::unique_fd& screenshot_fd,
+ bool is_screenshot_requested);
/* Returns true if the options set so far are consistent. */
bool ValidateOptions() const;
@@ -501,7 +513,7 @@
// Removes the in progress files output files (tmp file, zip/txt file, screenshot),
// but leaves the log file alone.
- void CleanupFiles();
+ void CleanupTmpFiles();
RunStatus HandleUserConsentDenied();
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index f26e4db..047a1c3 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -451,7 +451,7 @@
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
EXPECT_TRUE(status.isOk());
@@ -488,7 +488,7 @@
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
2000, // invalid bugreport mode
- listener);
+ listener, false);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
// The service should have died, freeing itself up for a new invocation.
@@ -519,13 +519,13 @@
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 0a0c40e..2efb130 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -233,7 +233,7 @@
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
@@ -250,7 +250,7 @@
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
@@ -267,7 +267,7 @@
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.is_remote_mode);
@@ -283,7 +283,7 @@
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
@@ -300,7 +300,7 @@
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
@@ -317,7 +317,7 @@
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 70bbc33..1af6edd 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1273,11 +1273,6 @@
Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
- // We don't create an image for secondary dex files.
- if (is_secondary_dex) {
- return Dex2oatFileWrapper();
- }
-
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index eefbe4f..d773790 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -138,10 +138,10 @@
return 4;
}
- PrepareEnvironment();
+ PrepareEnvironmentVariables();
- if (!PrepareBootImage(/* force */ false)) {
- LOG(ERROR) << "Failed preparing boot image.";
+ if (!EnsureBootImageAndDalvikCache()) {
+ LOG(ERROR) << "Bad boot image.";
return 5;
}
@@ -302,7 +302,7 @@
return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
}
- void PrepareEnvironment() {
+ void PrepareEnvironmentVariables() {
environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
@@ -312,9 +312,8 @@
}
}
- // Ensure that we have the right boot image. The first time any app is
- // compiled, we'll try to generate it.
- bool PrepareBootImage(bool force) const {
+ // Ensure that we have the right boot image and cache file structures.
+ bool EnsureBootImageAndDalvikCache() const {
if (parameters_.instruction_set == nullptr) {
LOG(ERROR) << "Instruction set missing.";
return false;
@@ -340,34 +339,19 @@
}
}
- // Check whether we have files in /data.
+ // Clear cached artifacts.
+ ClearDirectory(isa_path);
+
+ // Check whether we have a boot image.
// TODO: check that the files are correct wrt/ jars.
- std::string art_path = isa_path + "/system@framework@boot.art";
- std::string oat_path = isa_path + "/system@framework@boot.oat";
- bool cleared = false;
- if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
- // Files exist, assume everything is alright if not forced. Otherwise clean up.
- if (!force) {
- return true;
- }
- ClearDirectory(isa_path);
- cleared = true;
+ std::string preopted_boot_art_path =
+ StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
+ if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
+ PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
+ return false;
}
- // Check whether we have an image in /system.
- // TODO: check that the files are correct wrt/ jars.
- std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
- if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
- // Note: we ignore |force| here.
- return true;
- }
-
-
- if (!cleared) {
- ClearDirectory(isa_path);
- }
-
- return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
+ return true;
}
static bool CreatePath(const std::string& path) {
@@ -432,77 +416,6 @@
CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
}
- bool Dex2oatBootImage(const std::string& boot_cp,
- const std::string& art_path,
- const std::string& oat_path,
- const char* isa) const {
- // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
- std::vector<std::string> cmd;
- cmd.push_back(kDex2oatPath);
- cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
- for (const std::string& boot_part : Split(boot_cp, ":")) {
- cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
- }
- cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
-
- int32_t base_offset = ChooseRelocationOffsetDelta(
- art::imagevalues::GetImageMinBaseAddressDelta(),
- art::imagevalues::GetImageMaxBaseAddressDelta());
- cmd.push_back(StringPrintf("--base=0x%x",
- art::imagevalues::GetImageBaseAddress() + base_offset));
-
- cmd.push_back(StringPrintf("--instruction-set=%s", isa));
-
- // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
- "-Xms",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
- "-Xmx",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
- "--compiler-filter=",
- false,
- cmd);
- cmd.push_back("--profile-file=/system/etc/boot-image.prof");
- // TODO: Compiled-classes.
- const std::string* extra_opts =
- system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
- if (extra_opts != nullptr) {
- std::vector<std::string> extra_vals = Split(*extra_opts, " ");
- cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
- }
- // TODO: Should we lower this? It's usually set close to max, because
- // normally there's not much else going on at boot.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
- "-j",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
- "--cpu-set=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
- "--instruction-set-variant=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
- "--instruction-set-features=",
- false,
- cmd);
-
- std::string error_msg;
- bool result = Exec(cmd, &error_msg);
- if (!result) {
- LOG(ERROR) << "Could not generate boot image: " << error_msg;
- }
- return result;
- }
-
static const char* ParseNull(const char* arg) {
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
@@ -592,22 +505,6 @@
return 0;
}
- // If the dexopt failed, we may have a stale boot image from a previous OTA run.
- // Then regenerate and retry.
- if (WEXITSTATUS(dexopt_result) ==
- static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) {
- if (!PrepareBootImage(/* force */ true)) {
- LOG(ERROR) << "Forced boot image creating failed. Original error return was "
- << dexopt_result;
- return dexopt_result;
- }
-
- int dexopt_result_boot_image_retry = Dexopt();
- if (dexopt_result_boot_image_retry == 0) {
- return 0;
- }
- }
-
// If this was a profile-guided run, we may have profile version issues. Try to downgrade,
// if possible.
if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index abe6436..cbbea12 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -522,6 +522,11 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
+ if (serviceIt->second.guaranteeClient) {
+ LOG(INFO) << "Tried to unregister " << name << ", but there is about to be a client.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
int clients = handleServiceClientCallback(name, false);
// clients < 0: feature not implemented or other error. Assume clients.
@@ -532,6 +537,8 @@
if (clients < 0 || clients > 2) {
// client callbacks are either disabled or there are other clients
LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
+ // Set this flag to ensure the clients are acknowledged in the next callback
+ serviceIt->second.guaranteeClient = true;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index c30dcfe..cbcf6ec 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -409,7 +409,7 @@
#if __ANDROID_API__ >= 30
-/*
+/**
* Sets the intended frame rate for |surface_control|.
*
* On devices that are capable of running the display at different refresh rates, the system may
@@ -421,9 +421,9 @@
*
* |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
* value that indicates the app will accept the system's choice for the display frame rate, which is
- * the default behavior if this function isn't called. The frameRate param does *not* need to be a
- * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can
- * only run the display at 60fps.
+ * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
+ * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
+ * that can only run the display at 60fps.
*
* |compatibility| The frame rate compatibility of this surface. The compatibility value may
* influence the system's choice of display frame rate. To specify a compatibility use the
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0f4b4d9..3247fa1 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,7 +109,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8ca178c..7ca9031 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -404,6 +404,13 @@
*/
bool hasPendingBatch() const;
+ /* Returns the source of first pending batch if exist.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ int32_t getPendingBatchSource() const;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..4f2709d 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
return PROCESS_STATE_UNKNOWN;
}
+bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->isUidActiveOrForeground(uid, callingPackage);
+ }
+ return false;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index bc541f4..e6cfeb4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -158,7 +158,9 @@
filegroup {
name: "libbinder_aidl",
srcs: [
+ "aidl/android/content/pm/IPackageChangeObserver.aidl",
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/content/pm/PackageChangeEvent.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 43b0da3..2174ce2 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -96,11 +96,11 @@
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId, const String16& message) {
+ const std::unique_ptr<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
- message)
+ ? service->noteOperation(op, uid, callingPackage, attributionTag,
+ shouldCollectNotes(op), message)
: AppOpsManager::MODE_IGNORED;
return mode;
@@ -113,12 +113,12 @@
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getClientId(), op, uid, callingPackage,
- featureId, startIfModeDefault, shouldCollectNotes(op), message)
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
: AppOpsManager::MODE_IGNORED;
return mode;
@@ -129,10 +129,10 @@
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& callingFeatureId) {
+ const std::unique_ptr<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
+ service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
}
}
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..9e1249b 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,18 @@
}
return reply.readInt32();
}
+
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeString16(callingPackage);
+ remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return false;
+ return reply.readInt32() == 1;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 0ce1dd5..b9eb281 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -39,7 +39,7 @@
data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
data.writeInt32(op);
data.writeString16(packageName);
- remote()->transact(OP_CHANGED_TRANSACTION, data, &reply);
+ remote()->transact(OP_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -58,7 +58,6 @@
String16 packageName;
(void)data.readString16(&packageName);
opChanged(op, packageName);
- reply->writeNoException();
return NO_ERROR;
} break;
default:
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index a5555a3..0714723 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -47,14 +47,14 @@
}
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
data.writeString16(message);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
@@ -64,7 +64,7 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId,
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -72,7 +72,7 @@
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
data.writeInt32(startIfModeDefault ? 1 : 0);
data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
data.writeString16(message);
@@ -83,14 +83,14 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId) {
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
}
@@ -182,11 +182,11 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
- int32_t res = noteOperation(code, uid, packageName, featureId,
+ int32_t res = noteOperation(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
@@ -198,12 +198,12 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
bool startIfModeDefault = data.readInt32() == 1;
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
- int32_t res = startOperation(token, code, uid, packageName, featureId,
+ int32_t res = startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
@@ -215,9 +215,9 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
- finishOperation(token, code, uid, packageName, featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
+ finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
return NO_ERROR;
} break;
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 71d8130..74aece8 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -31,11 +31,16 @@
class ClientCounterCallback : public ::android::os::BnClientCallback {
public:
- ClientCounterCallback() : mNumConnectedServices(0) {}
+ ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
bool registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags);
+ /**
+ * Set a flag to prevent services from automatically shutting down
+ */
+ void forcePersist(bool persist);
+
protected:
Status onClients(const sp<IBinder>& service, bool clients) override;
@@ -60,6 +65,8 @@
* Map of registered names and services
*/
std::map<std::string, Service> mRegisteredServices;
+
+ bool mForcePersist;
};
bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
@@ -88,6 +95,14 @@
return true;
}
+void ClientCounterCallback::forcePersist(bool persist) {
+ mForcePersist = persist;
+ if(!mForcePersist) {
+ // Attempt a shutdown in case the number of clients hit 0 while the flag was on
+ tryShutdown();
+ }
+}
+
/**
* onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
* invocations could occur on different threads however.
@@ -103,14 +118,21 @@
mNumConnectedServices, mRegisteredServices.size(),
String8(service->getInterfaceDescriptor()).string(), clients);
- if (mNumConnectedServices == 0) {
- tryShutdown();
- }
-
+ tryShutdown();
return Status::ok();
}
void ClientCounterCallback::tryShutdown() {
+ if(mNumConnectedServices > 0) {
+ // Should only shut down if there are no clients
+ return;
+ }
+
+ if(mForcePersist) {
+ ALOGI("Shutdown prevented by forcePersist override flag.");
+ return;
+ }
+
ALOGI("Trying to shut down the service. No clients in use for any service in process.");
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
@@ -165,5 +187,9 @@
return OK;
}
+void LazyServiceRegistrar::forcePersist(bool persist) {
+ mClientCC->forcePersist(persist);
+}
+
} // namespace hardware
} // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
new file mode 100644
index 0000000..6929a6c
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.PackageChangeEvent;
+
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+ void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 618f88c..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
package android.content.pm;
+import android.content.pm.IPackageChangeObserver;
+
/**
* Parallel implementation of certain {@link PackageManager} APIs that need to
* be exposed to native code.
@@ -90,4 +92,13 @@
/* Returns the names of all packages. */
@utf8InCpp String[] getAllPackages();
+
+ /** Register an extra package change observer to receive the multi-cast. */
+ void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+ /**
+ * Unregister an existing package change observer.
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
}
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+ @utf8InCpp String packageName;
+ long version;
+ long lastUpdateTimeMillis;
+ boolean newInstalled;
+ boolean dataRemoved;
+ boolean isDeleted;
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..0bb6d28 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,25 +46,24 @@
PROCESS_STATE_PERSISTENT = 0,
PROCESS_STATE_PERSISTENT_UI = 1,
PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_BOUND_TOP = 4,
- PROCESS_STATE_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
- PROCESS_STATE_BACKUP = 10,
- PROCESS_STATE_SERVICE = 11,
- PROCESS_STATE_RECEIVER = 12,
- PROCESS_STATE_TOP_SLEEPING = 13,
- PROCESS_STATE_HEAVY_WEIGHT = 14,
- PROCESS_STATE_HOME = 15,
- PROCESS_STATE_LAST_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY = 17,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
- PROCESS_STATE_CACHED_RECENT = 19,
- PROCESS_STATE_CACHED_EMPTY = 20,
- PROCESS_STATE_NONEXISTENT = 21,
+ PROCESS_STATE_BOUND_TOP = 3,
+ PROCESS_STATE_FOREGROUND_SERVICE = 4,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+ PROCESS_STATE_BACKUP = 9,
+ PROCESS_STATE_SERVICE = 10,
+ PROCESS_STATE_RECEIVER = 11,
+ PROCESS_STATE_TOP_SLEEPING = 12,
+ PROCESS_STATE_HEAVY_WEIGHT = 13,
+ PROCESS_STATE_HOME = 14,
+ PROCESS_STATE_LAST_ACTIVITY = 15,
+ PROCESS_STATE_CACHED_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+ PROCESS_STATE_CACHED_RECENT = 18,
+ PROCESS_STATE_CACHED_EMPTY = 19,
+ PROCESS_STATE_NONEXISTENT = 20,
};
ActivityManager();
@@ -77,6 +76,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 5b6eb68..6afcd77 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -122,7 +122,16 @@
OP_LEGACY_STORAGE = 87,
OP_ACCESS_ACCESSIBILITY = 88,
OP_READ_DEVICE_IDENTIFIERS = 89,
- _NUM_OP = 90
+ OP_ACCESS_MEDIA_LOCATION = 90,
+ OP_QUERY_ALL_PACKAGES = 91,
+ OP_MANAGE_EXTERNAL_STORAGE = 92,
+ OP_INTERACT_ACROSS_PROFILES = 93,
+ OP_ACTIVATE_PLATFORM_VPN = 94,
+ OP_LOADER_USAGE_STATS = 95,
+ OP_DEPRECATED_1 = 96,
+ OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
+ _NUM_OP = 99
};
AppOpsManager();
@@ -134,18 +143,18 @@
// const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId, const String16& message);
+ const std::unique_ptr<String16>& attributionTag, const String16& message);
// @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
// const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
const String16& message);
// @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId);
+ const std::unique_ptr<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..1815ecc 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
};
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1b4bcce..1ffb8de 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -36,13 +36,13 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId,
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index efdecc4..6d711bc 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -34,6 +34,12 @@
const std::string& name = "default",
bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+ /**
+ * Force the service to persist, even when it has 0 clients.
+ * If setting this flag from the server side, make sure to do so before calling registerService,
+ * or there may be a race with the default dynamic shutdown.
+ */
+ void forcePersist(bool persist);
private:
std::shared_ptr<internal::ClientCounterCallback> mClientCC;
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
new file mode 100644
index 0000000..b605bd3
--- /dev/null
+++ b/libs/binder/include/binder/Nullable.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <utility>
+
+namespace android {
+
+namespace aidl {
+
+// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
+// usage:
+// nullable<Foo> a;
+// nullable<Foo> b = make_nullable<Foo>(...);
+// auto c = make_nullable<Foo>(...);
+// c.reset();
+// c = make_nullable<Foo>(...);
+// c = std::move(a);
+
+template <typename T>
+using nullable = std::unique_ptr<T>;
+
+template <typename T, typename... Args>
+inline nullable<T> make_nullable(Args&&... args) {
+ return std::make_unique<T>(std::forward<Args>(args)...);
+}
+
+} // namespace aidl
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 5b65c95..fd557b7 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -73,8 +73,10 @@
"android.hardware.thermal@2.0::IThermal",
"android.hardware.vr@1.0::IVr",
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.neuralnetworks@1.0::IDevice",
NULL,
};
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 30e1351..a8384ac 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -110,7 +110,7 @@
mProducer->setMaxDequeuedBufferCount(2);
}
mBufferItemConsumer =
- new BLASTBufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
+ new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
static int32_t id = 0;
auto name = std::string("BLAST Consumer") + std::to_string(id);
id++;
@@ -119,7 +119,9 @@
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+
mTransformHint = mSurfaceControl->getTransformHint();
+ mBufferItemConsumer->setTransformHint(mTransformHint);
mNumAcquired = 0;
mNumFrameAvailable = 0;
@@ -130,9 +132,12 @@
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
std::unique_lock _lock{mMutex};
mSurfaceControl = surface;
- mWidth = width;
- mHeight = height;
- mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+
+ if (mWidth != width || mHeight != height) {
+ mWidth = width;
+ mHeight = height;
+ mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+ }
}
static void transactionCallbackThunk(void* context, nsecs_t latchTime,
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 04c21a9..8d79cf8 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1140,8 +1140,7 @@
return err;
}
- err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply,
- IBinder::FLAG_ONEWAY);
+ err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
<option name="cleanup" value="true" />
<option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7335b30..ef7cc7d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1130,6 +1130,16 @@
return !mBatches.isEmpty();
}
+int32_t InputConsumer::getPendingBatchSource() const {
+ if (mBatches.isEmpty()) {
+ return AINPUT_SOURCE_CLASS_NONE;
+ }
+
+ const Batch& batch = mBatches.itemAt(0);
+ const InputMessage& head = batch.samples.itemAt(0);
+ return head.body.motion.source;
+}
+
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
const Batch& batch = mBatches.itemAt(i);
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 59aa665..25130e2 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -233,15 +233,15 @@
#if __ANDROID_API__ >= 30
-/* Parameter for ANativeWindow_setFrameRate */
-enum {
+/** Compatibility value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_FrameRateCompatibility {
/**
* There are no inherent restrictions on the frame rate of this window.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
/**
* This window is being used to display content with an inherently fixed
- * frame rate, e.g. a video that has a specific frame rate. When the system
+ * frame rate, e.g.\ a video that has a specific frame rate. When the system
* selects a frame rate other than what the app requested, the app will need
* to do pull down or use some other technique to adapt to the system's
* frame rate. The user experience is likely to be worse (e.g. more frame
@@ -272,9 +272,9 @@
* \param frameRate The intended frame rate of this window, in frames per
* second. 0 is a special value that indicates the app will accept the system's
* choice for the display frame rate, which is the default behavior if this
- * function isn't called. The frameRate param does *not* need to be a valid
- * refresh rate for this device's display - e.g., it's fine to pass 30fps to a
- * device that can only run the display at 60fps.
+ * function isn't called. The frameRate param does <em>not</em> need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
+ * to a device that can only run the display at 60fps.
*
* \param compatibility The frame rate compatibility of this window. The
* compatibility value may influence the system's choice of display refresh
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 1075f16..eb6080f 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -55,6 +55,7 @@
"gl/GLShadowTexture.cpp",
"gl/GLShadowVertexGenerator.cpp",
"gl/GLSkiaShadowPort.cpp",
+ "gl/GLVertexBuffer.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 153935b..cb0d5cf 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -32,23 +32,14 @@
namespace gl {
GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
- : GLFramebuffer(engine, false /* multiTarget */) {}
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
: mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
glGenTextures(1, &mTextureName);
- if (multiTarget) {
- glGenTextures(1, &mSecondaryTextureName);
- }
glGenFramebuffers(1, &mFramebufferName);
}
GLFramebuffer::~GLFramebuffer() {
glDeleteFramebuffers(1, &mFramebufferName);
glDeleteTextures(1, &mTextureName);
- if (mSecondaryTextureName != -1) {
- glDeleteTextures(1, &mSecondaryTextureName);
- }
}
bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -87,28 +78,12 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- const bool multiTarget = mSecondaryTextureName != -1;
- if (multiTarget) {
- glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- }
-
mBufferHeight = height;
mBufferWidth = width;
mEngine.checkErrors("Allocating Fbo texture");
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
- if (multiTarget) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
- mSecondaryTextureName, 0);
- GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
- glDrawBuffers(2, buffers);
- }
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
unbind();
glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 69102d6..b88da3b 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -42,7 +42,6 @@
void allocateBuffers(uint32_t width, uint32_t height);
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
- uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
int32_t getBufferHeight() const { return mBufferHeight; }
int32_t getBufferWidth() const { return mBufferWidth; }
@@ -59,7 +58,6 @@
bool usingFramebufferCache = false;
GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
uint32_t mTextureName, mFramebufferName;
- uint32_t mSecondaryTextureName = -1;
int32_t mBufferHeight = 0;
int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+ glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+ glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+ ATRACE_CALL();
+ bind();
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+ unbind();
+}
+
+void GLVertexBuffer::bind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+ explicit GLVertexBuffer();
+ ~GLVertexBuffer();
+
+ void allocateBuffers(const GLfloat data[], const GLuint size);
+ uint32_t getBufferName() const { return mBufferName; }
+ void bind() const;
+ void unbind() const;
+
+private:
+ uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index e704907..724877b 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -49,13 +49,30 @@
mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ const GLfloat vboData[] = {
+ // Vertex data
+ translation - size, -translation - size,
+ translation - size, -translation + size,
+ translation + size, -translation + size,
+ // UV data
+ 0.0f, 0.0f - translation,
+ 0.0f, size - translation,
+ size, size - translation
+ };
+ mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
}
status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
ATRACE_NAME("BlurFilter::setAsDrawTarget");
mRadius = radius;
- if (!mTexturesAllocated) {
+ if (mDisplayWidth < display.physicalDisplay.width() ||
+ mDisplayHeight < display.physicalDisplay.height()) {
+ ATRACE_NAME("BlurFilter::allocatingTextures");
+
mDisplayWidth = display.physicalDisplay.width();
mDisplayHeight = display.physicalDisplay.height();
mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
@@ -64,16 +81,23 @@
const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
mPingFbo.allocateBuffers(fboWidth, fboHeight);
mPongFbo.allocateBuffers(fboWidth, fboHeight);
- mTexturesAllocated = true;
- }
- if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid blur buffer");
- return mPingFbo.getStatus();
- }
- if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid composition buffer");
- return mCompositionFbo.getStatus();
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid ping buffer");
+ return mPingFbo.getStatus();
+ }
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid pong buffer");
+ return mPongFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
}
mCompositionFbo.bind();
@@ -82,70 +106,56 @@
}
void BlurFilter::drawMesh(GLuint uv, GLuint position) {
- static constexpr auto size = 2.0f;
- static constexpr auto translation = 1.0f;
- GLfloat positions[] = {
- translation-size, -translation-size,
- translation-size, -translation+size,
- translation+size, -translation+size
- };
- GLfloat texCoords[] = {
- 0.0f, 0.0f-translation,
- 0.0f, size-translation,
- size, size-translation
- };
- // set attributes
glEnableVertexAttribArray(uv);
- glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray(position);
- glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
- positions);
+ mMeshBuffer.bind();
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+ (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+ mMeshBuffer.unbind();
// draw mesh
glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
- mEngine.checkErrors("Drawing blur mesh");
}
status_t BlurFilter::prepare() {
ATRACE_NAME("BlurFilter::prepare");
- if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid FBO");
- return mPongFbo.getStatus();
- }
- if (!mBlurProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
-
- blit(mCompositionFbo, mPingFbo);
-
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
- auto radius = mRadius / 6.0f;
+ const auto radius = mRadius / 6.0f;
// Calculate how many passes we'll do, based on the radius.
// Too many passes will make the operation expensive.
- auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+ const auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
- // We'll ping pong between our textures, to accumulate the result of various offsets.
+ const float radiusByPasses = radius / (float)passes;
+ const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth();
+ const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight();
+
+ // Let's start by downsampling and blurring the composited frame simultaneously.
mBlurProgram.useProgram();
- GLFramebuffer* read = &mPingFbo;
- GLFramebuffer* draw = &mPongFbo;
- float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
- float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
glActiveTexture(GL_TEXTURE0);
glUniform1i(mBTextureLoc, 0);
- for (auto i = 0; i < passes; i++) {
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform2f(mBOffsetLoc, stepX, stepY);
+ glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight());
+ mPingFbo.bind();
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // And now we'll ping pong between our textures, to accumulate the result of various offsets.
+ GLFramebuffer* read = &mPingFbo;
+ GLFramebuffer* draw = &mPongFbo;
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ for (auto i = 1; i < passes; i++) {
ATRACE_NAME("BlurFilter::renderPass");
draw->bind();
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
glBindTexture(GL_TEXTURE_2D, read->getTextureName());
glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
- mEngine.checkErrors("Setting uniforms");
drawMesh(mBUvLoc, mBPosLoc);
@@ -156,9 +166,6 @@
}
mLastDrawTarget = read;
- // Cleanup
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
return NO_ERROR;
}
@@ -177,7 +184,6 @@
glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
mLastDrawTarget->getBufferHeight(), 0, 0, mDisplayWidth, mDisplayHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return NO_ERROR;
}
@@ -189,17 +195,18 @@
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
glUniform1i(mMCompositionTextureLoc, 1);
- mEngine.checkErrors("Setting final pass uniforms");
drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
+ mEngine.checkErrors("Drawing blur mesh");
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(#version 310 es
+ precision mediump float;
in vec2 aPosition;
in highp vec2 aUV;
@@ -219,7 +226,7 @@
uniform sampler2D uTexture;
uniform vec2 uOffset;
- highp in vec2 vUV;
+ in highp vec2 vUV;
out vec4 fragColor;
void main() {
@@ -255,12 +262,12 @@
}
void BlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+ ATRACE_NAME("BlurFilter::blit");
read.bindAsReadBuffer();
draw.bindAsDrawBuffer();
glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
GL_LINEAR);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
} // namespace gl
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index eb6120b..36e5a77 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -19,6 +19,7 @@
#include <ui/GraphicTypes.h>
#include "../GLESRenderEngine.h"
#include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
#include "GenericProgram.h"
using namespace std;
@@ -66,11 +67,13 @@
// Frame buffers holding the blur passes.
GLFramebuffer mPingFbo;
GLFramebuffer mPongFbo;
- uint32_t mDisplayWidth;
- uint32_t mDisplayHeight;
+ uint32_t mDisplayWidth = 0;
+ uint32_t mDisplayHeight = 0;
// Buffer holding the final blur pass.
GLFramebuffer* mLastDrawTarget;
- bool mTexturesAllocated = false;
+
+ // VBO containing vertex and uv data of a fullscreen triangle.
+ GLVertexBuffer mMeshBuffer;
GenericProgram mMixProgram;
GLuint mMPosLoc;
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index d9b713d..f1e8252 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <limits>
+#include <ostream>
#include <type_traits>
#include <utility>
@@ -108,31 +109,70 @@
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType> clamp(
- typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
- FromType&&>::type v) {
- static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
- static constexpr auto toLowest =
- std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
- static constexpr auto fromHighest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::max();
- static constexpr auto fromLowest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+ static Size::remove_cv_reference_t<ToType>
+ clamp(typename std::enable_if<
+ std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
+ std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
+ FromType>::type v) {
+ using BareToType = remove_cv_reference_t<ToType>;
+ using BareFromType = remove_cv_reference_t<FromType>;
+ static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
+ static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
+ static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
+ static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
- // A clamp is needed if the range of FromType is not a subset of the range of ToType
- static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+ // Get the closest representation of [toLowest, toHighest] in type
+ // FromType to use to clamp the input value before conversion.
+
+ // std::common_type<...> is used to get a value-preserving type for the
+ // top end of the range.
+ using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+
+ // std::make_signed<std::common_type<...>> is used to get a
+ // value-preserving type for the bottom end of the range, except this is
+ // a bit trickier for non-integer types like float.
+ using CommonLowestType =
+ std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
+ std::make_signed_t<std::conditional_t<
+ std::numeric_limits<CommonHighestType>::is_integer,
+ CommonHighestType, int /* not used */>>,
+ CommonHighestType>;
+
+ // We can then compute the clamp range in a way that can be later
+ // trivially converted to either the 'from' or 'to' types, and be
+ // representabile in either.
+ static constexpr auto commonClampHighest =
+ std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ static constexpr auto commonClampLowest =
+ std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
+
+ static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+
+ // A clamp is needed only if the range we are clamping to is not the
+ // same as the range of the input.
+ static constexpr bool isClampNeeded =
+ (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
if (!isClampNeeded) {
- return static_cast<ToType>(v);
+ return static_cast<BareToType>(v);
}
- // Otherwise we leverage implicit conversion to safely compare values of
- // different types, to ensure we return a value clamped to the range of
- // ToType.
- return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v));
+ // Note: Clang complains about the value of INT32_MAX not being
+ // convertible back to int32_t from float if this is made "constexpr",
+ // when clamping a float value to an int32_t value. This is however
+ // covered by a test case to ensure the run-time cast works correctly.
+ const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
+ const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
+
+ // Otherwise clamping is done by using the already computed endpoints
+ // for each type.
+ return (v <= fromClampLowest)
+ ? toClampLowest
+ : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
}
};
@@ -154,5 +194,10 @@
return lhs.height < rhs.height;
}
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Size& size, ::std::ostream* os) {
+ *os << "Size(" << size.width << ", " << size.height << ")";
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index ff55bc2..605c5a9 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -111,6 +111,7 @@
cc_test {
name: "Size_test",
+ test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
cflags: ["-Wall", "-Werror"],
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 69e1ac8..38f37ad 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -19,8 +19,18 @@
#include <cmath>
#include <cstdlib>
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wimplicit-int-float-conversion"
+#pragma clang diagnostic error "-Wconversion"
+#endif // __clang__
+
#include <ui/Size.h>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
#include <gtest/gtest.h>
namespace android {
@@ -176,9 +186,34 @@
TEST(SizeTest, FloatRangeIsClamped) {
ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), std::numeric_limits<float>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::max(), 0)));
ClampTest(float(0), int32_t(0));
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0)));
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(),
+ std::numeric_limits<float>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
}
+TEST(SizeTest, Uint32RangeIsClamped) {
+ ClampTest(std::numeric_limits<uint32_t>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(std::numeric_limits<uint32_t>::max() - 1, std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1,
+ std::numeric_limits<int32_t>::max() - 1);
+ ClampTest(uint32_t(0), int32_t(0));
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
new file mode 100644
index 0000000..7fcd7de
--- /dev/null
+++ b/libs/ui/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "Size_test"
+ }
+ ]
+}
diff --git a/opengl/TEST_MAPPING b/opengl/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/opengl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 8ff0711..c3da216 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -40,4 +40,8 @@
cflags: [
"-DLOG_TAG=\"AutomotiveDisplayService\""
],
+
+ vintf_fragments: [
+ "manifest_android.frameworks.automotive.display@1.0.xml",
+ ],
}
diff --git a/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
new file mode 100644
index 0000000..464dcac
--- /dev/null
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.automotive.display</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IAutomotiveDisplayProxyService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d094532..231d068 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -30,16 +30,11 @@
namespace android {
-GpuStats::GpuStats() {
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
-}
-
GpuStats::~GpuStats() {
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ if (mStatsdRegistered) {
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ }
}
static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
@@ -97,6 +92,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
ALOGV("Received:\n"
"\tdriverPackageName[%s]\n"
"\tdriverVersionName[%s]\n"
@@ -150,6 +146,7 @@
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
if (!mAppStats.count(appStatsKey)) {
return;
}
@@ -179,6 +176,16 @@
mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
}
+void GpuStats::registerStatsdCallbacksIfNeeded() {
+ if (!mStatsdRegistered) {
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ mStatsdRegistered = true;
+ }
+}
+
void GpuStats::dump(const Vector<String16>& args, std::string* result) {
ATRACE_CALL();
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 8ca4e4e..55f0da1 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -30,7 +30,6 @@
class GpuStats {
public:
- GpuStats();
~GpuStats();
// Insert new gpu driver stats into global stats and app stats.
@@ -66,12 +65,16 @@
void dumpAppLocked(std::string* result);
// Append cpuVulkanVersion and glesVersion to system driver stats
void interceptSystemDriverStatsLocked();
+ // Registers statsd callbacks if they have not already been registered
+ void registerStatsdCallbacksIfNeeded();
// Below limits the memory usage of GpuStats to be less than 10KB. This is
// the preferred number for statsd while maintaining nice data quality.
static const size_t MAX_NUM_APP_RECORDS = 100;
// GpuStats access should be guarded by mLock.
std::mutex mLock;
+ // True if statsd callbacks have been registered.
+ bool mStatsdRegistered = false;
// Key is driver version code.
std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
// Key is <app package name>+<driver version code>.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 4ec4e25..f67c9d0 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -52,7 +52,6 @@
"libstatslog",
"libutils",
"libui",
- "server_configurable_flags",
],
}
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index e5e83d7..77a0716 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
#if defined(__linux__)
#include <pthread.h>
#endif
-#include <server_configurable_flags/get_flags.h>
#include <unordered_set>
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,13 +45,6 @@
namespace android {
-static constexpr bool DEBUG = false;
-
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
//Max number of elements to store in mEvents.
static constexpr size_t MAX_EVENTS = 5;
@@ -79,20 +71,6 @@
return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
}
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
- std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
- std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
- if (flag_value == "1" || flag_value == "true") {
- ALOGI("Deep press feature enabled.");
- return true;
- }
- ALOGI("Deep press feature is not enabled.");
- return false;
-}
-
-
// --- ClassifierEvent ---
ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -141,53 +119,40 @@
// --- MotionClassifier ---
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
- mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
- mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
-#if defined(__linux__)
- // Set the thread name for debugging
- pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
-#endif
-}
-
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
- ensureHalThread(__func__);
- sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
- classifier::V1_0::IInputClassifier::getService();
- if (!service) {
- // Not really an error, maybe the device does not have this HAL,
- // but somehow the feature flag is flipped
- ALOGI("Could not obtain InputClassifier HAL");
- return false;
- }
-
- sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
- if (recipient != nullptr) {
- const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
- if (!linked) {
- ALOGE("Could not link MotionClassifier to the HAL death");
- return false;
- }
- }
-
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+ : mEvents(MAX_EVENTS), mService(service) {
// Under normal operation, we do not need to reset the HAL here. But in the case where system
// crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
// have received events in the past. That means, that HAL could be in an inconsistent state
// once it receives events from the newly created MotionClassifier.
mEvents.push(ClassifierEvent::createHalResetEvent());
- {
- std::scoped_lock lock(mLock);
- if (mService) {
- ALOGE("MotionClassifier::%s should only be called once", __func__);
- }
- mService = service;
+ mHalThread = std::thread(&MotionClassifier::processEvents, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient) {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (!service) {
+ // Not really an error, maybe the device does not have this HAL,
+ // but somehow the feature flag is flipped
+ ALOGI("Could not obtain InputClassifier HAL");
+ return nullptr;
}
- return true;
+
+ const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link death recipient to the HAL death");
+ return nullptr;
+ }
+ // Using 'new' to access a non-public constructor
+ return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
}
MotionClassifier::~MotionClassifier() {
@@ -195,14 +160,6 @@
mHalThread.join();
}
-void MotionClassifier::ensureHalThread(const char* function) {
- if (DEBUG) {
- if (std::this_thread::get_id() != mHalThread.get_id()) {
- LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
- }
- }
-}
-
/**
* Obtain the classification from the HAL for a given MotionEvent.
* Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +170,7 @@
* To remove any possibility of negatively affecting the touch latency, the HAL
* is called from a dedicated thread.
*/
-void MotionClassifier::callInputClassifierHal() {
- ensureHalThread(__func__);
- const bool initialized = init();
- if (!initialized) {
- // MotionClassifier no longer useful.
- // Deliver death notification from a separate thread
- // because ~MotionClassifier may be invoked, which calls mHalThread.join()
- std::thread([deathRecipient = mDeathRecipient](){
- sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
- if (recipient != nullptr) {
- recipient->serviceDied(0 /*cookie*/, nullptr);
- }
- }).detach();
- return;
- }
- // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
@@ -389,24 +330,41 @@
}
}
+// --- HalDeathRecipient
+
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
+
+void InputClassifier::HalDeathRecipient::serviceDied(
+ uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+ mParent.setMotionClassifier(nullptr);
+}
// --- InputClassifier ---
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
- mListener(listener) {
- // The rest of the initialization is done in onFirstRef, because we need to obtain
- // an sp to 'this' in order to register for HAL death notifications
-}
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
-void InputClassifier::onFirstRef() {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return;
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+ if (enabled) {
+ ALOGI("Enabling motion classifier");
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
+#endif
+ } else {
+ ALOGI("Disabling motion classifier");
+ setMotionClassifier(nullptr);
}
- std::scoped_lock lock(mLock);
- mMotionClassifier = std::make_unique<MotionClassifier>(this);
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -447,22 +405,15 @@
mListener->notifyDeviceReset(args);
}
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
- const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+ std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
- ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
- mMotionClassifier = nullptr;
- sp<android::hidl::base::V1_0::IBase> service = who.promote();
- if (service) {
- service->unlinkToDeath(this);
- }
+ mMotionClassifier = std::move(motionClassifier);
}
void InputClassifier::dump(std::string& dump) {
std::scoped_lock lock(mLock);
dump += "Input Classifier State:\n";
- dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled");
-
dump += INDENT1 "Motion Classifier:\n";
if (mMotionClassifier) {
mMotionClassifier->dump(dump);
@@ -472,4 +423,10 @@
dump += "\n";
}
+InputClassifier::~InputClassifier() {
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 9692352..03510a6 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -19,8 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/RefBase.h>
-#include <unordered_map>
#include <thread>
+#include <unordered_map>
#include "BlockingQueue.h"
#include "InputListener.h"
@@ -90,6 +90,7 @@
*/
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
public:
+ virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
* Dump the state of the input classifier.
* This method may be called on any thread (usually by the input manager).
@@ -113,23 +114,23 @@
*/
class MotionClassifier final : public MotionClassifierInterface {
public:
- /**
- * The deathRecipient will be subscribed to the HAL death. If the death recipient
- * owns MotionClassifier and receives HAL death, it should delete its copy of it.
- * The callback serviceDied will also be sent if the MotionClassifier itself fails
- * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
- * should be deleted.
- * If no death recipient is supplied, then the registration step will be skipped, so there will
- * be no listeners registered for the HAL death. This is useful for testing
- * MotionClassifier in isolation.
+ /*
+ * Create an instance of MotionClassifier.
+ * The death recipient, if provided, will be subscribed to the HAL death.
+ * The death recipient could be used to destroy MotionClassifier.
+ *
+ * This function should be called asynchronously, because getService takes a long time.
*/
- explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+ static std::unique_ptr<MotionClassifierInterface> create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient);
+
~MotionClassifier();
/**
* Classifies events asynchronously; that is, it doesn't block events on a classification,
- * but instead sends them over to the classifier HAL and after a classification is
- * determined, it then marks the next event it sees in the stream with it.
+ * but instead sends them over to the classifier HAL. After a classification of a specific
+ * event is determined, MotionClassifier then marks the next event in the stream with this
+ * classification.
*
* Therefore, it is acceptable to have the classifications be delayed by 1-2 events
* in a particular gesture.
@@ -141,15 +142,9 @@
virtual void dump(std::string& dump) override;
private:
- /**
- * Initialize MotionClassifier.
- * Return true if initializaion is successful.
- */
- bool init();
- /**
- * Entity that will be notified of the HAL death (most likely InputClassifier).
- */
- wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+ friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
+ explicit MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
// The events that need to be sent to the HAL.
BlockingQueue<ClassifierEvent> mEvents;
@@ -164,14 +159,9 @@
*/
std::thread mHalThread;
/**
- * Print an error message if the caller is not on the InputClassifier thread.
- * Caller must supply the name of the calling function as __func__
+ * Process events and call the InputClassifier HAL
*/
- void ensureHalThread(const char* function);
- /**
- * Call the InputClassifier HAL
- */
- void callInputClassifierHal();
+ void processEvents();
/**
* Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
* When init() successfully completes, mService is guaranteed to remain non-null and to not
@@ -225,19 +215,15 @@
const char* getServiceStatus() REQUIRES(mLock);
};
-
/**
* Implementation of the InputClassifierInterface.
* Represents a separate stage of input processing. All of the input events go through this stage.
* Acts as a passthrough for all input events except for motion events.
* The events of motion type are sent to MotionClassifier.
*/
-class InputClassifier : public InputClassifierInterface,
- public android::hardware::hidl_death_recipient {
+class InputClassifier : public InputClassifierInterface {
public:
explicit InputClassifier(const sp<InputListenerInterface>& listener);
- // Some of the constructor logic is finished in onFirstRef
- virtual void onFirstRef() override;
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -245,17 +231,47 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override;
-
virtual void dump(std::string& dump) override;
+ ~InputClassifier();
+
+ // Called from InputManager
+ virtual void setMotionClassifierEnabled(bool enabled) override;
+
private:
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
- std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
// The next stage to pass input events to
sp<InputListenerInterface> mListener;
+
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+ std::thread mInitializeMotionClassifierThread;
+ /**
+ * Set the value of mMotionClassifier.
+ * This is called from 2 different threads:
+ * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
+ * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
+ * mMotionClassifier to become nullptr.
+ */
+ void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+
+ /**
+ * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
+ */
+ class HalDeathRecipient : public android::hardware::hidl_death_recipient {
+ public:
+ explicit HalDeathRecipient(InputClassifier& parent);
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ InputClassifier& mParent;
+ };
+ // We retain a reference to death recipient, because the death recipient will be calling
+ // ~MotionClassifier if the HAL dies.
+ // If we don't retain a reference, and MotionClassifier is the only owner of the death
+ // recipient, the serviceDied call will cause death recipient to call its own destructor.
+ sp<HalDeathRecipient> mHalDeathRecipient;
};
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c7c61cf..e68946d 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -111,8 +111,10 @@
handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
}
- for (auto const& i : handlesPerDisplay) {
- mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener);
+ mDispatcher->setInputWindows(handlesPerDisplay);
+
+ if (setInputWindowsListener) {
+ setInputWindowsListener->onSetInputWindowsFinished();
}
}
@@ -132,4 +134,8 @@
mDispatcher->unregisterInputChannel(channel);
}
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+ mClassifier->setMotionClassifierEnabled(enabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 586097f..0158441 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,6 +100,8 @@
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+ void setMotionClassifierEnabled(bool enabled);
+
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 3b18813..7c5c9c5 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -252,7 +252,7 @@
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
- dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyMotionArgs motionArgs = generateMotionArgs();
@@ -288,7 +288,7 @@
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
- dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 308d19b..4ec61b0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3621,6 +3621,18 @@
mWindowHandlesByDisplay[displayId] = newHandles;
}
+void InputDispatcher::setInputWindows(
+ const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ for (auto const& i : handlesPerDisplay) {
+ setInputWindowsLocked(i.second, i.first);
+ }
+ }
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
/**
* Called from InputManagerService, update window handle list by displayId that can receive input.
* A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -3628,9 +3640,8 @@
* For focused handle, check if need to change and send a cancel event to previous one.
* For removed handle, check if need to send a cancel event if already in touch.
*/
-void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
- int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+void InputDispatcher::setInputWindowsLocked(
+ const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
if (DEBUG_FOCUS) {
std::string windowList;
for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
@@ -3638,109 +3649,97 @@
}
ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
}
- { // acquire lock
- std::scoped_lock _l(mLock);
- // Copy old handles for release if they are no longer present.
- const std::vector<sp<InputWindowHandle>> oldWindowHandles =
- getWindowHandlesLocked(displayId);
+ // Copy old handles for release if they are no longer present.
+ const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
- updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
+ updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
- sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
- bool foundHoveredWindow = false;
- for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
- // Set newFocusedWindowHandle to the top most focused window instead of the last one
- if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
- windowHandle->getInfo()->visible) {
- newFocusedWindowHandle = windowHandle;
+ sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
+ bool foundHoveredWindow = false;
+ for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+ // Set newFocusedWindowHandle to the top most focused window instead of the last one
+ if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
+ windowHandle->getInfo()->visible) {
+ newFocusedWindowHandle = windowHandle;
+ }
+ if (windowHandle == mLastHoverWindowHandle) {
+ foundHoveredWindow = true;
+ }
+ }
+
+ if (!foundHoveredWindow) {
+ mLastHoverWindowHandle = nullptr;
+ }
+
+ sp<InputWindowHandle> oldFocusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+ if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
+ if (oldFocusedWindowHandle != nullptr) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Focus left window: %s in display %" PRId32,
+ oldFocusedWindowHandle->getName().c_str(), displayId);
}
- if (windowHandle == mLastHoverWindowHandle) {
- foundHoveredWindow = true;
+ sp<InputChannel> focusedInputChannel =
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ if (focusedInputChannel != nullptr) {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+ enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
}
+ mFocusedWindowHandlesByDisplay.erase(displayId);
+ }
+ if (newFocusedWindowHandle != nullptr) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Focus entered window: %s in display %" PRId32,
+ newFocusedWindowHandle->getName().c_str(), displayId);
+ }
+ mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
+ enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
}
- if (!foundHoveredWindow) {
- mLastHoverWindowHandle = nullptr;
+ if (mFocusedDisplayId == displayId) {
+ onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
}
+ }
- sp<InputWindowHandle> oldFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-
- if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
- if (oldFocusedWindowHandle != nullptr) {
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+ if (stateIndex >= 0) {
+ TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ for (size_t i = 0; i < state.windows.size();) {
+ TouchedWindow& touchedWindow = state.windows[i];
+ if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
if (DEBUG_FOCUS) {
- ALOGD("Focus left window: %s in display %" PRId32,
- oldFocusedWindowHandle->getName().c_str(), displayId);
+ ALOGD("Touched window was removed: %s in display %" PRId32,
+ touchedWindow.windowHandle->getName().c_str(), displayId);
}
- sp<InputChannel> focusedInputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
- if (focusedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
+ sp<InputChannel> touchedInputChannel =
+ getInputChannelLocked(touchedWindow.windowHandle->getToken());
+ if (touchedInputChannel != nullptr) {
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "touched window was removed");
+ synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
}
- mFocusedWindowHandlesByDisplay.erase(displayId);
- }
- if (newFocusedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus entered window: %s in display %" PRId32,
- newFocusedWindowHandle->getName().c_str(), displayId);
- }
- mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
- enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
- }
-
- if (mFocusedDisplayId == displayId) {
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ state.windows.erase(state.windows.begin() + i);
+ } else {
+ ++i;
}
}
+ }
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex >= 0) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
- for (size_t i = 0; i < state.windows.size();) {
- TouchedWindow& touchedWindow = state.windows[i];
- if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
- if (DEBUG_FOCUS) {
- ALOGD("Touched window was removed: %s in display %" PRId32,
- touchedWindow.windowHandle->getName().c_str(), displayId);
- }
- sp<InputChannel> touchedInputChannel =
- getInputChannelLocked(touchedWindow.windowHandle->getToken());
- if (touchedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "touched window was removed");
- synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel,
- options);
- }
- state.windows.erase(state.windows.begin() + i);
- } else {
- ++i;
- }
+ // Release information for windows that are no longer present.
+ // This ensures that unused input channels are released promptly.
+ // Otherwise, they might stick around until the window handle is destroyed
+ // which might not happen until the next GC.
+ for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
+ if (!hasWindowHandleLocked(oldWindowHandle)) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
}
+ oldWindowHandle->releaseChannel();
}
-
- // Release information for windows that are no longer present.
- // This ensures that unused input channels are released promptly.
- // Otherwise, they might stick around until the window handle is destroyed
- // which might not happen until the next GC.
- for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
- if (!hasWindowHandleLocked(oldWindowHandle)) {
- if (DEBUG_FOCUS) {
- ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
- }
- oldWindowHandle->releaseChannel();
- }
- }
- } // release lock
-
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
-
- if (setInputWindowsListener) {
- setInputWindowsListener->onSetInputWindowsFinished();
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4aa47f8..cbba7e1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -109,8 +109,8 @@
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
virtual void setInputWindows(
- const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
+ const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+ handlesPerDisplay) override;
virtual void setFocusedApplication(
int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
virtual void setFocusedDisplay(int32_t displayId) override;
@@ -278,6 +278,8 @@
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
+ void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
+ int32_t displayId) REQUIRES(mLock);
// Get window handles by display, return an empty vector if not found.
std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 6e98676..09dc92c 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -19,6 +19,7 @@
#include <InputListener.h>
#include <input/ISetInputWindowsListener.h>
+#include <unordered_map>
namespace android {
@@ -99,13 +100,13 @@
*/
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
- /* Sets the list of input windows.
+ /* Sets the list of input windows per display.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual void setInputWindows(
- const std::vector<sp<InputWindowHandle> >& inputWindowHandles, int32_t displayId,
- const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
+ const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+ handlesPerDisplay) = 0;
/* Sets the focused application on the given display.
*
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index bbc8e53..99a572a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -160,8 +160,8 @@
: InputMapper(deviceContext),
mSource(0),
mDeviceMode(DEVICE_MODE_DISABLED),
- mSurfaceWidth(-1),
- mSurfaceHeight(-1),
+ mRawSurfaceWidth(-1),
+ mRawSurfaceHeight(-1),
mSurfaceLeft(0),
mSurfaceTop(0),
mPhysicalWidth(-1),
@@ -680,7 +680,7 @@
naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.deviceHeight - naturalPhysicalWidth;
+ naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
naturalPhysicalTop = mViewport.physicalLeft;
naturalDeviceWidth = mViewport.deviceHeight;
naturalDeviceHeight = mViewport.deviceWidth;
@@ -701,7 +701,7 @@
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalLeft = mViewport.physicalTop;
- naturalPhysicalTop = mViewport.deviceWidth - naturalPhysicalHeight;
+ naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
naturalDeviceWidth = mViewport.deviceHeight;
naturalDeviceHeight = mViewport.deviceWidth;
break;
@@ -729,10 +729,12 @@
mPhysicalLeft = naturalPhysicalLeft;
mPhysicalTop = naturalPhysicalTop;
- mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+ mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+ mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+ mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
+ mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
mSurfaceOrientation =
mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
@@ -742,8 +744,8 @@
mPhysicalLeft = 0;
mPhysicalTop = 0;
- mSurfaceWidth = rawWidth;
- mSurfaceHeight = rawHeight;
+ mRawSurfaceWidth = rawWidth;
+ mRawSurfaceHeight = rawHeight;
mSurfaceLeft = 0;
mSurfaceTop = 0;
mSurfaceOrientation = DISPLAY_ORIENTATION_0;
@@ -769,12 +771,12 @@
if (viewportChanged || deviceModeChanged) {
ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
+ getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
mSurfaceOrientation, mDeviceMode, mViewport.displayId);
// Configure X and Y factors.
- mXScale = float(mSurfaceWidth) / rawWidth;
- mYScale = float(mSurfaceHeight) / rawHeight;
+ mXScale = float(mRawSurfaceWidth) / rawWidth;
+ mYScale = float(mRawSurfaceHeight) / rawHeight;
mXTranslate = -mSurfaceLeft;
mYTranslate = -mSurfaceTop;
mXPrecision = 1.0f / mXScale;
@@ -793,7 +795,7 @@
mGeometricScale = avg(mXScale, mYScale);
// Size of diagonal axis.
- float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
+ float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
// Size factors.
if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
@@ -956,13 +958,13 @@
mOrientedYPrecision = mXPrecision;
mOrientedRanges.x.min = mYTranslate;
- mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
mOrientedRanges.y.min = mXTranslate;
- mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -973,13 +975,13 @@
mOrientedYPrecision = mYPrecision;
mOrientedRanges.x.min = mXTranslate;
- mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
mOrientedRanges.y.min = mYTranslate;
- mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -992,7 +994,7 @@
if (mDeviceMode == DEVICE_MODE_POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
+ float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
@@ -1027,10 +1029,12 @@
void TouchInputMapper::dumpSurface(std::string& dump) {
dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
- dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+ dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
+ dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
+ dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
+ dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
@@ -1074,16 +1078,16 @@
int32_t halfHeight = virtualKeyDefinition.height / 2;
virtualKey.hitLeft =
- (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth +
+ (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
touchScreenLeft;
virtualKey.hitRight =
- (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth +
+ (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
touchScreenLeft;
- virtualKey.hitTop =
- (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight +
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+ mRawSurfaceHeight +
touchScreenTop;
- virtualKey.hitBottom =
- (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight +
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+ mRawSurfaceHeight +
touchScreenTop;
mVirtualKeys.push_back(virtualKey);
}
@@ -2188,13 +2192,10 @@
rotateAndScale(xTransformed, yTransformed);
// Adjust X, Y, and coverage coords for surface orientation.
- float x, y;
float left, top, right, bottom;
switch (mSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
- x = yTransformed + mYTranslate;
- y = xTransformed + mXTranslate;
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
@@ -2207,8 +2208,6 @@
}
break;
case DISPLAY_ORIENTATION_180:
- x = xTransformed + mXTranslate;
- y = yTransformed + mYTranslate;
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
@@ -2221,8 +2220,6 @@
}
break;
case DISPLAY_ORIENTATION_270:
- x = yTransformed + mYTranslate;
- y = xTransformed + mXTranslate;
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
@@ -2235,8 +2232,6 @@
}
break;
default:
- x = xTransformed + mXTranslate;
- y = yTransformed + mYTranslate;
left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
@@ -2247,8 +2242,8 @@
// Write output coords.
PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
out.clear();
- out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed);
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed);
out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
@@ -3624,34 +3619,47 @@
abortTouches(when, 0 /* policyFlags*/);
}
+// Transform raw coordinate to surface coordinate
void TouchInputMapper::rotateAndScale(float& x, float& y) {
+ // Scale to surface coordinate.
+ const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
+ const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
+
+ // Rotate to surface coordinate.
+ // 0 - no swap and reverse.
+ // 90 - swap x/y and reverse y.
+ // 180 - reverse x, y.
+ // 270 - swap x/y and reverse x.
switch (mSurfaceOrientation) {
+ case DISPLAY_ORIENTATION_0:
+ x = xScaled + mXTranslate;
+ y = yScaled + mYTranslate;
+ break;
case DISPLAY_ORIENTATION_90:
- x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
- y = float(y - mRawPointerAxes.y.minValue) * mYScale;
+ y = mSurfaceRight - xScaled;
+ x = yScaled + mYTranslate;
break;
case DISPLAY_ORIENTATION_180:
- x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
- y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+ x = mSurfaceRight - xScaled;
+ y = mSurfaceBottom - yScaled;
break;
case DISPLAY_ORIENTATION_270:
- x = float(x - mRawPointerAxes.x.minValue) * mXScale;
- y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+ y = xScaled + mXTranslate;
+ x = mSurfaceBottom - yScaled;
break;
default:
- x = float(x - mRawPointerAxes.x.minValue) * mXScale;
- y = float(y - mRawPointerAxes.y.minValue) * mYScale;
- break;
+ assert(false);
}
}
bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
- float xTransformed = x, yTransformed = y;
- rotateAndScale(xTransformed, yTransformed);
+ const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
+ const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
+
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xTransformed >= mSurfaceLeft && xTransformed <= mSurfaceLeft + mSurfaceWidth &&
+ xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yTransformed >= mSurfaceTop && yTransformed <= mSurfaceTop + mSurfaceHeight;
+ yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e21a33a..58bfc5c 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -407,12 +407,16 @@
// The surface orientation, width and height set by configureSurface().
// The width and height are derived from the viewport but are specified
// in the natural orientation.
+ // They could be used for calculating diagonal, scaling factors, and virtual keys.
+ int32_t mRawSurfaceWidth;
+ int32_t mRawSurfaceHeight;
+
// The surface origin specifies how the surface coordinates should be translated
// to align with the logical display coordinate space.
- int32_t mSurfaceWidth;
- int32_t mSurfaceHeight;
int32_t mSurfaceLeft;
int32_t mSurfaceTop;
+ int32_t mSurfaceRight;
+ int32_t mSurfaceBottom;
// Similar to the surface coordinates, but in the raw display coordinate space rather than in
// the logical coordinate space.
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 40086ef..ab74a04 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -22,6 +22,9 @@
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
using namespace android::hardware::input;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::input::common::V1_0::Classification;
namespace android {
@@ -132,6 +135,49 @@
ASSERT_EQ(args, outArgs);
}
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+ mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+/**
+ * A minimal implementation of IInputClassifier.
+ */
+struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
+ Return<Classification> classify(
+ const android::hardware::input::common::V1_0::MotionEvent& event) override {
+ return Classification::NONE;
+ };
+ Return<void> reset() override { return Void(); };
+ Return<void> resetDevice(int32_t deviceId) override { return Void(); };
+};
+
+/**
+ * An entity that will be subscribed to the HAL death.
+ */
+class TestDeathRecipient : public android::hardware::hidl_death_recipient {
+public:
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override{};
+};
+
// --- MotionClassifierTest ---
class MotionClassifierTest : public testing::Test {
@@ -139,7 +185,14 @@
std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
virtual void SetUp() override {
- mMotionClassifier = std::make_unique<MotionClassifier>();
+ mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
+ if (mMotionClassifier == nullptr) {
+ // If the device running this test does not have IInputClassifier service,
+ // use the test HAL instead.
+ // Using 'new' to access non-public constructor
+ mMotionClassifier =
+ std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
+ }
}
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1f283b1..29f3dac 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -871,7 +871,7 @@
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -888,7 +888,7 @@
sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -910,7 +910,7 @@
// Display should have only one focused window
windowSecond->setFocus(true);
- mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
windowSecond->consumeFocusEvent(true);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -935,7 +935,7 @@
windowTop->setFocus(true);
windowSecond->setFocus(true);
- mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
windowTop->consumeFocusEvent(true);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -960,7 +960,7 @@
windowSecond->setFocus(true);
// Release channel for window is no longer valid.
windowTop->releaseChannel();
- mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
windowSecond->consumeFocusEvent(true);
// Test inject a key down, should dispatch to a valid window.
@@ -986,7 +986,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
@@ -1003,7 +1003,7 @@
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
@@ -1025,7 +1025,7 @@
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -1053,7 +1053,7 @@
"Second Window", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
// Send down to the first window
NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -1090,7 +1090,7 @@
"Second Window", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
// Send down to the first window
NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -1152,7 +1152,7 @@
| InputWindowInfo::FLAG_SPLIT_TOUCH);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -1204,7 +1204,7 @@
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true);
@@ -1220,7 +1220,7 @@
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
mDispatcher->notifyKey(&keyArgs);
@@ -1235,7 +1235,7 @@
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
// Send key
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
@@ -1289,7 +1289,7 @@
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
@@ -1309,7 +1309,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
@@ -1325,7 +1325,7 @@
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
@@ -1351,7 +1351,7 @@
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -1387,29 +1387,29 @@
window->setFocus(true);
SCOPED_TRACE("Check default value of touch mode");
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocus(false);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Disable touch mode");
mDispatcher->setInTouchMode(false);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocus(false);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
SCOPED_TRACE("Enable touch mode again");
mDispatcher->setInTouchMode(true);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
window->assertNoEvents();
@@ -1423,7 +1423,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocus(true);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
@@ -1459,7 +1459,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -1512,7 +1512,7 @@
mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mWindow->setFocus(true);
- mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
mWindow->consumeFocusEvent(true);
}
@@ -1602,7 +1602,7 @@
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocus(true);
- mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
windowInPrimary->consumeFocusEvent(true);
application2 = new FakeApplicationHandle();
@@ -1614,7 +1614,7 @@
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocus(true);
- mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
+ mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
windowInSecondary->consumeFocusEvent(true);
}
@@ -1664,7 +1664,7 @@
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
- mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID);
+ mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
// Expect old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
@@ -1828,7 +1828,7 @@
mFocusedWindow->setFocus(true);
// Expect one focus window exist in display.
- mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
mFocusedWindow->consumeFocusEvent(true);
}
@@ -1916,7 +1916,7 @@
mWindow2->setId(1);
mWindow2->setFrame(Rect(100, 100, 200, 200));
- mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
}
protected:
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 618aefc..96d86b6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6990,51 +6990,7 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
}
-/**
- * Test touch should not work if outside of surface.
- */
-TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- // Touch on left-top area should work.
- int32_t rawX = DISPLAY_WIDTH / 2 - 1;
- int32_t rawY = DISPLAY_HEIGHT / 2 - 1;
- processPosition(mapper, rawX, rawY);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-
- // Reset.
- mapper.reset(ARBITRARY_TIME);
-
- // Let logical display be different to physical display and rotate 90-degrees.
- std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
- internalViewport->orientation = DISPLAY_ORIENTATION_90;
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_HEIGHT;
- internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
- internalViewport->physicalLeft = DISPLAY_HEIGHT;
- internalViewport->physicalTop = DISPLAY_WIDTH / 2;
- internalViewport->physicalRight = DISPLAY_HEIGHT;
- internalViewport->physicalBottom = DISPLAY_WIDTH;
-
- internalViewport->deviceWidth = DISPLAY_HEIGHT;
- internalViewport->deviceHeight = DISPLAY_WIDTH;
- mFakePolicy->updateViewport(internalViewport.value());
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-
- // Display align to right-top after rotate 90-degrees, touch on left-top area should not work.
- processPosition(mapper, rawX, rawY);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -7161,4 +7117,130 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
+
+/**
+ * Test touch should not work if outside of surface.
+ */
+class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
+protected:
+ void halfDisplayToCenterHorizontal(int32_t orientation) {
+ std::optional<DisplayViewport> internalViewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+
+ // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
+ internalViewport->orientation = orientation;
+ if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
+ internalViewport->logicalLeft = 0;
+ internalViewport->logicalTop = 0;
+ internalViewport->logicalRight = DISPLAY_HEIGHT;
+ internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
+
+ internalViewport->physicalLeft = 0;
+ internalViewport->physicalTop = DISPLAY_WIDTH / 4;
+ internalViewport->physicalRight = DISPLAY_HEIGHT;
+ internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
+
+ internalViewport->deviceWidth = DISPLAY_HEIGHT;
+ internalViewport->deviceHeight = DISPLAY_WIDTH;
+ } else {
+ internalViewport->logicalLeft = 0;
+ internalViewport->logicalTop = 0;
+ internalViewport->logicalRight = DISPLAY_WIDTH / 2;
+ internalViewport->logicalBottom = DISPLAY_HEIGHT;
+
+ internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
+ internalViewport->physicalTop = 0;
+ internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
+ internalViewport->physicalBottom = DISPLAY_HEIGHT;
+
+ internalViewport->deviceWidth = DISPLAY_WIDTH;
+ internalViewport->deviceHeight = DISPLAY_HEIGHT;
+ }
+
+ mFakePolicy->updateViewport(internalViewport.value());
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xInside, int32_t yInside,
+ int32_t xOutside, int32_t yOutside, int32_t xExpected,
+ int32_t yExpected) {
+ // touch on outside area should not work.
+ processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // touch on inside area should receive the event.
+ NotifyMotionArgs args;
+ processPosition(mapper, toRawX(xInside), toRawY(yInside));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ // Reset.
+ mapper.reset(ARBITRARY_TIME);
+ }
+};
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Touch on center of normal display should work.
+ const int32_t x = DISPLAY_WIDTH / 4;
+ const int32_t y = DISPLAY_HEIGHT / 2;
+ processPosition(mapper, toRawX(x), toRawY(y));
+ processSync(mapper);
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 0.0f));
+ // Reset.
+ mapper.reset(ARBITRARY_TIME);
+
+ // Let physical display be different to device, and make surface and physical could be 1:1.
+ halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0);
+
+ const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
+ const int32_t yExpected = y;
+ processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
+ halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
+
+ const int32_t x = DISPLAY_WIDTH / 4;
+ const int32_t y = DISPLAY_HEIGHT / 2;
+
+ // expect x/y = swap x/y then reverse y.
+ const int32_t xExpected = y;
+ const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
+ processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
+ halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
+
+ const int32_t x = DISPLAY_WIDTH / 4;
+ const int32_t y = DISPLAY_HEIGHT / 2;
+
+ // expect x/y = swap x/y then reverse x.
+ constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
+ constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
+ processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
} // namespace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 9a13c00..77d8c11 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <cinttypes>
#include <sys/socket.h>
#include <utils/threads.h>
@@ -69,17 +70,17 @@
}
bool SensorService::SensorEventConnection::needsWakeLock() {
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
return !mDead && mWakeLockRefCount > 0;
}
void SensorService::SensorEventConnection::resetWakeLockRefCount() {
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
mWakeLockRefCount = 0;
}
void SensorService::SensorEventConnection::dump(String8& result) {
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
result.appendFormat("\tOperating Mode: ");
if (!mService->isWhiteListedPackage(getPackageName())) {
result.append("RESTRICTED\n");
@@ -91,11 +92,11 @@
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
"max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
+ for (auto& it : mSensorInfo) {
+ const FlushInfo& flushInfo = it.second.flushInfo;
result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(mSensorInfo.keyAt(i)).string(),
- mSensorInfo.keyAt(i),
+ mService->getSensorName(it.first).string(),
+ it.first,
flushInfo.mFirstFlushPending ? "First flush pending" :
"active",
flushInfo.mPendingFlushEventsToSend);
@@ -121,7 +122,7 @@
*/
void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) const {
using namespace service::SensorEventConnectionProto;
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
if (!mService->isWhiteListedPackage(getPackageName())) {
proto->write(OPERATING_MODE, OP_MODE_RESTRICTED);
@@ -135,12 +136,12 @@
proto->write(UID, int32_t(mUid));
proto->write(CACHE_SIZE, int32_t(mCacheSize));
proto->write(MAX_CACHE_SIZE, int32_t(mMaxCacheSize));
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
+ for (auto& it : mSensorInfo) {
+ const FlushInfo& flushInfo = it.second.flushInfo;
const uint64_t token = proto->start(FLUSH_INFOS);
proto->write(FlushInfoProto::SENSOR_NAME,
- std::string(mService->getSensorName(mSensorInfo.keyAt(i))));
- proto->write(FlushInfoProto::SENSOR_HANDLE, mSensorInfo.keyAt(i));
+ std::string(mService->getSensorName(it.first)));
+ proto->write(FlushInfoProto::SENSOR_HANDLE, it.first);
proto->write(FlushInfoProto::FIRST_FLUSH_PENDING, flushInfo.mFirstFlushPending);
proto->write(FlushInfoProto::PENDING_FLUSH_EVENTS_TO_SEND,
flushInfo.mPendingFlushEventsToSend);
@@ -157,40 +158,54 @@
#endif
}
-bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
- Mutex::Autolock _l(mConnectionLock);
+bool SensorService::SensorEventConnection::addSensor(
+ int32_t handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags) {
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
!canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
- mSensorInfo.indexOfKey(handle) >= 0) {
+ mSensorInfo.count(handle) > 0) {
return false;
}
- mSensorInfo.add(handle, FlushInfo());
+
+ SensorRequest request = {
+ .samplingPeriodNs = samplingPeriodNs,
+ .maxBatchReportLatencyNs = maxBatchReportLatencyNs,
+ .reservedFlags = reservedFlags
+ };
+
+ mSensorInfo[handle] = request;
return true;
}
bool SensorService::SensorEventConnection::removeSensor(int32_t handle) {
- Mutex::Autolock _l(mConnectionLock);
- if (mSensorInfo.removeItem(handle) >= 0) {
- return true;
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ return mSensorInfo.erase(handle) > 0;
+}
+
+std::vector<int32_t> SensorService::SensorEventConnection::getActiveSensorHandles() const {
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ std::vector<int32_t> list;
+ for (auto& it : mSensorInfo) {
+ list.push_back(it.first);
}
- return false;
+ return list;
}
bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const {
- Mutex::Autolock _l(mConnectionLock);
- return mSensorInfo.indexOfKey(handle) >= 0;
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ return mSensorInfo.count(handle) + mSensorInfoBackup.count(handle) > 0;
}
bool SensorService::SensorEventConnection::hasAnySensor() const {
- Mutex::Autolock _l(mConnectionLock);
- return mSensorInfo.size() ? true : false;
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ return mSensorInfo.size() + mSensorInfoBackup.size() ? true : false;
}
bool SensorService::SensorEventConnection::hasOneShotSensors() const {
- Mutex::Autolock _l(mConnectionLock);
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- const int handle = mSensorInfo.keyAt(i);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ for (auto &it : mSensorInfo) {
+ const int handle = it.first;
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
return true;
@@ -205,16 +220,15 @@
void SensorService::SensorEventConnection::setFirstFlushPending(int32_t handle,
bool value) {
- Mutex::Autolock _l(mConnectionLock);
- ssize_t index = mSensorInfo.indexOfKey(handle);
- if (index >= 0) {
- FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ if (mSensorInfo.count(handle) > 0) {
+ FlushInfo& flushInfo = mSensorInfo[handle].flushInfo;
flushInfo.mFirstFlushPending = value;
}
}
void SensorService::SensorEventConnection::updateLooperRegistration(const sp<Looper>& looper) {
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
updateLooperRegistrationLocked(looper);
}
@@ -233,8 +247,8 @@
int looper_flags = 0;
if (mCacheSize > 0) looper_flags |= ALOOPER_EVENT_OUTPUT;
if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- const int handle = mSensorInfo.keyAt(i);
+ for (auto& it : mSensorInfo) {
+ const int handle = it.first;
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().isWakeUpSensor()) {
looper_flags |= ALOOPER_EVENT_INPUT;
@@ -265,10 +279,9 @@
}
void SensorService::SensorEventConnection::incrementPendingFlushCount(int32_t handle) {
- Mutex::Autolock _l(mConnectionLock);
- ssize_t index = mSensorInfo.indexOfKey(handle);
- if (index >= 0) {
- FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ if (mSensorInfo.count(handle) > 0) {
+ FlushInfo& flushInfo = mSensorInfo[handle].flushInfo;
flushInfo.mPendingFlushEventsToSend++;
}
}
@@ -282,7 +295,7 @@
std::unique_ptr<sensors_event_t[]> sanitizedBuffer;
int count = 0;
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
if (scratch) {
size_t i=0;
while (i<numEvents) {
@@ -296,15 +309,14 @@
sensor_handle = buffer[i].meta_data.sensor;
}
- ssize_t index = mSensorInfo.indexOfKey(sensor_handle);
// Check if this connection has registered for this sensor. If not continue to the
// next sensor_event.
- if (index < 0) {
+ if (mSensorInfo.count(sensor_handle) == 0) {
++i;
continue;
}
- FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+ FlushInfo& flushInfo = mSensorInfo[sensor_handle].flushInfo;
// Check if there is a pending flush_complete event for this sensor on this connection.
if (buffer[i].type == SENSOR_TYPE_META_DATA && flushInfo.mFirstFlushPending == true &&
mapFlushEventsToConnections[i] == this) {
@@ -425,9 +437,55 @@
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
+void SensorService::SensorEventConnection::updateSensorSubscriptions() {
+ if (!hasSensorAccess()) {
+ stopAll();
+ } else {
+ recoverAll();
+ }
+}
+
void SensorService::SensorEventConnection::setSensorAccess(const bool hasAccess) {
- Mutex::Autolock _l(mConnectionLock);
- mHasSensorAccess = hasAccess;
+ if (mHasSensorAccess != hasAccess) {
+ mHasSensorAccess = hasAccess;
+ updateSensorSubscriptions();
+ }
+}
+
+void SensorService::SensorEventConnection::stopAll() {
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ if (!mSensorInfo.empty()) {
+ mSensorInfoBackup = mSensorInfo;
+ mSensorInfo.clear();
+
+ for (auto& it : mSensorInfoBackup) {
+ int32_t handle = it.first;
+
+ status_t err = mService->disable(this, handle);
+
+ if (err != NO_ERROR) {
+ ALOGE("Error disabling sensor %d", handle);
+ }
+ }
+ }
+}
+
+void SensorService::SensorEventConnection::recoverAll() {
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ for (auto& it : mSensorInfoBackup) {
+ int32_t handle = it.first;
+ SensorRequest &request = it.second;
+
+ status_t err = mService->enable(
+ this, handle, request.samplingPeriodNs, request.maxBatchReportLatencyNs,
+ request.reservedFlags, mOpPackageName);
+
+ if (err != NO_ERROR) {
+ ALOGE("Error recovering sensor %d", handle);
+ }
+ }
+
+ mSensorInfoBackup.clear();
}
bool SensorService::SensorEventConnection::hasSensorAccess() {
@@ -522,14 +580,14 @@
flushCompleteEvent.type = SENSOR_TYPE_META_DATA;
// Loop through all the sensors for this connection and check if there are any pending
// flush complete events to be sent.
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- const int handle = mSensorInfo.keyAt(i);
+ for (auto& it : mSensorInfo) {
+ const int handle = it.first;
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
continue;
}
- FlushInfo& flushInfo = mSensorInfo.editValueAt(i);
+ FlushInfo& flushInfo = it.second.flushInfo;
while (flushInfo.mPendingFlushEventsToSend > 0) {
flushCompleteEvent.meta_data.sensor = handle;
bool wakeUpSensor = si->getSensor().isWakeUpSensor();
@@ -554,7 +612,7 @@
// half the size of the socket buffer allocated in BitTube whichever is smaller.
const int maxWriteSize = helpers::min(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT/2,
int(mService->mSocketBufferSize/(sizeof(sensors_event_t)*2)));
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
// Send pending flush complete events (if any)
sendPendingFlushEventsLocked();
for (int numEventsSent = 0; numEventsSent < mCacheSize;) {
@@ -615,14 +673,13 @@
// separately before the next batch of events.
for (int j = 0; j < numEventsDropped; ++j) {
if (scratch[j].type == SENSOR_TYPE_META_DATA) {
- ssize_t index = mSensorInfo.indexOfKey(scratch[j].meta_data.sensor);
- if (index < 0) {
+ if (mSensorInfo.count(scratch[j].meta_data.sensor) == 0) {
ALOGW("%s: sensor 0x%x is not found in connection",
__func__, scratch[j].meta_data.sensor);
continue;
}
- FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+ FlushInfo& flushInfo = mSensorInfo[scratch[j].meta_data.sensor].flushInfo;
flushInfo.mPendingFlushEventsToSend++;
ALOGD_IF(DEBUG_CONNECTIONS, "increment pendingFlushCount %d",
flushInfo.mPendingFlushEventsToSend);
@@ -658,13 +715,21 @@
} else {
err = mService->disable(this, handle);
}
+
return err;
}
status_t SensorService::SensorEventConnection::setEventRate(
int handle, nsecs_t samplingPeriodNs)
{
- return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
+ status_t err = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
+
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
+ if (err == NO_ERROR && mSensorInfo.count(handle) > 0) {
+ mSensorInfo[handle].samplingPeriodNs = samplingPeriodNs;
+ }
+
+ return err;
}
status_t SensorService::SensorEventConnection::flush() {
@@ -685,7 +750,7 @@
// and remove the fd from Looper. Call checkWakeLockState to know if SensorService
// can release the wake-lock.
ALOGD_IF(DEBUG_CONNECTIONS, "%p Looper error %d", this, fd);
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
mDead = true;
mWakeLockRefCount = 0;
updateLooperRegistrationLocked(mService->getLooper());
@@ -704,7 +769,7 @@
unsigned char buf[sizeof(sensors_event_t)];
ssize_t numBytesRead = ::recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
{
- Mutex::Autolock _l(mConnectionLock);
+ std::lock_guard<std::recursive_mutex> _l(mConnectionLock);
if (numBytesRead == sizeof(sensors_event_t)) {
if (!mDataInjectionMode) {
ALOGE("Data injected in normal mode, dropping event"
@@ -764,8 +829,8 @@
int SensorService::SensorEventConnection::computeMaxCacheSizeLocked() const {
size_t fifoWakeUpSensors = 0;
size_t fifoNonWakeUpSensors = 0;
- for (size_t i = 0; i < mSensorInfo.size(); ++i) {
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(mSensorInfo.keyAt(i));
+ for (auto& it : mSensorInfo) {
+ sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
if (si == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index caf5d7c..3ba5c07 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -23,7 +23,6 @@
#include <utils/Vector.h>
#include <utils/SortedVector.h>
-#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/AndroidThreads.h>
#include <utils/RefBase.h>
@@ -58,8 +57,10 @@
bool hasSensor(int32_t handle) const;
bool hasAnySensor() const;
bool hasOneShotSensors() const;
- bool addSensor(int32_t handle);
+ bool addSensor(
+ int32_t handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags);
bool removeSensor(int32_t handle);
+ std::vector<int32_t> getActiveSensorHandles() const;
void setFirstFlushPending(int32_t handle, bool value);
void dump(String8& result);
void dump(util::ProtoOutputStream* proto) const;
@@ -70,7 +71,7 @@
uid_t getUid() const { return mUid; }
void setSensorAccess(const bool hasAccess);
-
+ void updateSensorSubscriptions();
private:
virtual ~SensorEventConnection();
virtual void onFirstRef();
@@ -136,13 +137,16 @@
// privacy not being enabled.
bool hasSensorAccess();
+ void stopAll();
+ void recoverAll();
+
// Call noteOp for the sensor if the sensor requires a permission
bool noteOpIfRequired(const sensors_event_t& event);
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
- mutable Mutex mConnectionLock;
+ mutable std::recursive_mutex mConnectionLock;
// Number of events from wake up sensors which are still pending and haven't been delivered to
// the corresponding application. It is incremented by one unit for each write to the socket.
uint32_t mWakeLockRefCount;
@@ -169,8 +173,17 @@
FlushInfo() : mPendingFlushEventsToSend(0), mFirstFlushPending(false) {}
};
- // protected by SensorService::mLock. Key for this vector is the sensor handle.
- KeyedVector<int, FlushInfo> mSensorInfo;
+
+ struct SensorRequest {
+ nsecs_t samplingPeriodNs;
+ nsecs_t maxBatchReportLatencyNs;
+ int reservedFlags;
+ FlushInfo flushInfo;
+ };
+
+ // protected by SensorService::mLock. Key for this map is the sensor handle.
+ std::unordered_map<int32_t, SensorRequest> mSensorInfo;
+ std::unordered_map<int32_t, SensorRequest> mSensorInfoBackup;
sensors_event_t *mEventCache;
int mCacheSize, mMaxCacheSize;
@@ -185,7 +198,7 @@
mutable Mutex mDestroyLock;
bool mDestroyed;
- bool mHasSensorAccess;
+ std::atomic_bool mHasSensorAccess;
// Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
// valid mapping for sensors that require a permission in order to reduce the lookup time.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 22a15c6..5ae7c51 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -301,7 +301,10 @@
void SensorService::setSensorAccess(uid_t uid, bool hasAccess) {
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
- for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
+ const auto& connections = connLock.getActiveConnections();
+
+ mLock.unlock();
+ for (const sp<SensorEventConnection>& conn : connections) {
if (conn->getUid() == uid) {
conn->setSensorAccess(hasAccess);
}
@@ -638,6 +641,9 @@
void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
SensorDevice& dev(SensorDevice::getInstance());
+ for (const sp<SensorEventConnection>& connection : connLock->getActiveConnections()) {
+ connection->updateSensorSubscriptions();
+ }
for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
connection->stopAll(true /* backupRecord */);
}
@@ -666,6 +672,9 @@
}
SensorDevice& dev(SensorDevice::getInstance());
dev.enableAllSensors();
+ for (const sp<SensorEventConnection>& connection : connLock->getActiveConnections()) {
+ connection->updateSensorSubscriptions();
+ }
for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
connection->recoverAll();
}
@@ -1589,7 +1598,7 @@
}
}
- if (connection->addSensor(handle)) {
+ if (connection->addSensor(handle, samplingPeriodNs, maxBatchReportLatencyNs, reservedFlags)) {
BatteryService::enableSensor(connection->getUid(), handle);
// the sensor was added (which means it wasn't already there)
// so, see if this connection becomes active
@@ -1739,18 +1748,22 @@
const int halVersion = dev.getHalDeviceVersion();
status_t err(NO_ERROR);
Mutex::Autolock _l(mLock);
+
+ size_t numSensors = 0;
// Loop through all sensors for this connection and call flush on each of them.
- for (size_t i = 0; i < connection->mSensorInfo.size(); ++i) {
- const int handle = connection->mSensorInfo.keyAt(i);
+ for (int handle : connection->getActiveSensorHandles()) {
sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr) {
continue;
}
+ numSensors++;
+
if (sensor->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
ALOGE("flush called on a one-shot sensor");
err = INVALID_OPERATION;
continue;
}
+
if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0 || isVirtualSensor(handle)) {
// For older devices just increment pending flush count which will send a trivial
// flush complete event.
@@ -1768,7 +1781,8 @@
err = (err_flush != NO_ERROR) ? err_flush : err;
}
}
- return err;
+
+ return (numSensors == 0) ? INVALID_OPERATION : err;
}
bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation,
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index e4d754c..a32bc2b 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -272,7 +272,7 @@
// pixel format is HDR Y410 masquerading as RGBA_1010102
return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
- mBufferInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+ mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
}
sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
@@ -374,6 +374,12 @@
return true;
}
+void BufferLayer::gatherBufferInfo() {
+ mBufferInfo.mPixelFormat =
+ !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+ mBufferInfo.mFrameLatencyNeeded = true;
+}
+
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
ATRACE_CALL();
@@ -434,7 +440,6 @@
gatherBufferInfo();
mRefreshPending = true;
- mBufferInfo.mFrameLatencyNeeded = true;
if (oldBufferInfo.mBuffer == nullptr) {
// the first time we receive a buffer, we need to trigger a
// geometry invalidation.
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index f678910..fbec6ee 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -161,7 +161,7 @@
Region mSurfaceDamage;
HdrMetadata mHdrMetadata;
int mApi;
- PixelFormat mPixelFormat;
+ PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
bool mTransformToDisplayInverse{false};
sp<GraphicBuffer> mBuffer;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 5e04d95..e50a909 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -443,10 +443,7 @@
}
void BufferLayerConsumer::onDisconnect() {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->onDisconnect();
- }
+ mLayer->onDisconnect();
}
void BufferLayerConsumer::onSidebandStreamChanged() {
@@ -480,10 +477,7 @@
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->addAndGetFrameTimestamps(newTimestamps, outDelta);
- }
+ mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
void BufferLayerConsumer::abandonLocked() {
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 39ed370..c71a1d9 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -332,7 +332,7 @@
const uint32_t mTexName;
// The layer for this BufferLayerConsumer
- const wp<Layer> mLayer;
+ Layer* mLayer;
wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index fac9024..c84b15d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -346,8 +346,6 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer =
mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
- auto* layerCompositionState = editCompositionState();
- layerCompositionState->buffer = mBufferInfo.mBuffer;
if (mBufferInfo.mBuffer == nullptr) {
// this can only happen if the very first buffer was rejected.
@@ -527,8 +525,6 @@
return BAD_VALUE;
}
- mFormat = format;
-
setDefaultBufferSize(w, h);
mConsumer->setDefaultBufferFormat(format);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
@@ -552,6 +548,8 @@
}
void BufferQueueLayer::gatherBufferInfo() {
+ BufferLayer::gatherBufferInfo();
+
mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
mBufferInfo.mFence = mConsumer->getCurrentFence();
@@ -562,7 +560,6 @@
mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage();
mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata();
mBufferInfo.mApi = mConsumer->getCurrentApi();
- mBufferInfo.mPixelFormat = mFormat;
mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
}
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5f7587c..ea7f203 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -132,8 +132,6 @@
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
- PixelFormat mFormat{PIXEL_FORMAT_NONE};
-
bool mUpdateTexImageFailed{false};
uint64_t mPreviousBufferId = 0;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index de5429b..3e65171 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -616,7 +616,6 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer = s.buffer;
mBufferInfo.mFence = s.acquireFence;
- editCompositionState()->buffer = mBufferInfo.mBuffer;
return NO_ERROR;
}
@@ -719,8 +718,9 @@
}
void BufferStateLayer::gatherBufferInfo() {
- const State& s(getDrawingState());
+ BufferLayer::gatherBufferInfo();
+ const State& s(getDrawingState());
mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
mBufferInfo.mFence = s.acquireFence;
@@ -731,8 +731,6 @@
mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
mBufferInfo.mHdrMetadata = s.hdrMetadata;
mBufferInfo.mApi = s.api;
- mBufferInfo.mPixelFormat =
- !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index acaaf4e..2d9f01b 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -35,7 +35,8 @@
return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
- lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 02226ab..88f6649 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -45,6 +45,7 @@
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+ MOCK_METHOD2(allocatePhysicalDisplay, void(hwc2_display_t, DisplayId));
MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
MOCK_METHOD3(getDeviceCompositionChanges,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e740b13..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index fb6c817..397fedc 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -183,9 +183,10 @@
struct Physical {
DisplayId id;
DisplayConnectionType type;
+ hwc2_display_t hwcDisplayId;
bool operator==(const Physical& other) const {
- return id == other.id && type == other.type;
+ return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
}
};
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 9aaef65..f24f314 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -104,9 +104,8 @@
return static_cast<uint16_t>(value >> 40);
}
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
- return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
- port};
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
+ return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
}
bool isEdid(const DisplayIdentificationData& data) {
@@ -209,23 +208,30 @@
view.remove_prefix(kDescriptorLength);
}
- if (displayName.empty()) {
+ std::string_view modelString = displayName;
+
+ if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
- displayName = serialNumber;
+ modelString = serialNumber;
}
- if (displayName.empty()) {
+ if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
- displayName = asciiText;
+ modelString = asciiText;
}
- if (displayName.empty()) {
+ if (modelString.empty()) {
ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
return {};
}
+ // Hash model string instead of using product code or (integer) serial number, since the latter
+ // have been observed to change on some displays with multiple inputs.
+ const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+
return Edid{.manufacturerId = manufacturerId,
- .pnpId = *pnpId,
- .displayName = displayName,
.productId = productId,
+ .pnpId = *pnpId,
+ .modelHash = modelHash,
+ .displayName = displayName,
.manufactureWeek = manufactureWeek,
.manufactureOrModelYear = manufactureOrModelYear};
}
@@ -253,10 +259,8 @@
return {};
}
- // Hash display name instead of using product code or serial number, since the latter have been
- // observed to change on some displays with multiple inputs.
- const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
- return DisplayIdentificationInfo{.id = DisplayId::fromEdid(port, edid->manufacturerId, hash),
+ const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+ return DisplayIdentificationInfo{.id = displayId,
.name = std::string(edid->displayName),
.deviceProductInfo = buildDeviceProductInfo(*edid)};
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 0a18ba1..d91b957 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -34,7 +34,7 @@
uint16_t manufacturerId() const;
- static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+ static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
};
inline bool operator==(DisplayId lhs, DisplayId rhs) {
@@ -61,6 +61,7 @@
uint16_t manufacturerId;
uint16_t productId;
PnpId pnpId;
+ uint32_t modelHash;
std::string_view displayName;
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index aeffb0e..4c3b3e5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -183,10 +183,7 @@
}
ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
- // TODO(b/149495759): Use the ui::Size constructor once it no longer is broken.
- ui::Size framebufferSize;
- framebufferSize.width = width;
- framebufferSize.height = height;
+ ui::Size framebufferSize(width, height);
bool wasLimited = true;
if (width > mMaxWidth && mMaxWidth != 0) {
float aspectRatio = float(width) / float(height);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1c1e113..560299a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -205,56 +205,14 @@
std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
HWC2::Connection connection) {
- std::optional<DisplayIdentificationInfo> info;
-
- if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
- info = DisplayIdentificationInfo{.id = *displayId,
- .name = std::string(),
- .deviceProductInfo = std::nullopt};
- } else {
- if (connection == HWC2::Connection::Disconnected) {
- ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+ switch (connection) {
+ case HWC2::Connection::Connected:
+ return onHotplugConnect(hwcDisplayId);
+ case HWC2::Connection::Disconnected:
+ return onHotplugDisconnect(hwcDisplayId);
+ case HWC2::Connection::Invalid:
return {};
- }
-
- info = onHotplugConnect(hwcDisplayId);
- if (!info) return {};
}
-
- ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
- hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
- to_string(info->id).c_str(), hwcDisplayId);
-
- if (connection == HWC2::Connection::Connected) {
- auto& displayData = mDisplayData[info->id];
- // If we get a hotplug connected event for a display we already have,
- // destroy the display and recreate it. This will force us to requery
- // the display params and recreate all layers on that display.
- if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
- ALOGI("Hotplug connecting an already connected display."
- " Clearing old display state.");
- }
- displayData.hwcDisplay.reset();
- auto newDisplay =
- std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
- HWC2::DisplayType::Physical);
- newDisplay->setConnected(true);
- displayData.hwcDisplay = std::move(newDisplay);
- mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
- } else if (connection == HWC2::Connection::Disconnected) {
- // The display will later be destroyed by a call to
- // destroyDisplay(). For now we just mark it disconnected.
- auto& displayData = mDisplayData[info->id];
- if (displayData.hwcDisplay) {
- displayData.hwcDisplay->setConnected(false);
- } else {
- ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
- }
- // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
- // via SurfaceFlinger's onHotplugReceived callback handling
- }
-
- return info;
}
bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
@@ -337,6 +295,22 @@
return displayId;
}
+void HWComposer::allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) {
+ if (!mInternalHwcDisplayId) {
+ mInternalHwcDisplayId = hwcDisplayId;
+ } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
+ mExternalHwcDisplayId = hwcDisplayId;
+ }
+
+ auto& displayData = mDisplayData[displayId];
+ auto newDisplay =
+ std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+ HWC2::DisplayType::Physical);
+ newDisplay->setConnected(true);
+ displayData.hwcDisplay = std::move(newDisplay);
+ mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+}
+
HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
@@ -911,52 +885,93 @@
return {};
}
-std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+bool HWComposer::shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
+ bool hasDisplayIdentificationData) const {
if (isUsingVrComposer() && mInternalHwcDisplayId) {
ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
- return {};
+ return true;
}
- uint8_t port;
- DisplayIdentificationData data;
- const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);
-
- if (mPhysicalDisplayIdMap.empty()) {
- mHasMultiDisplaySupport = hasMultiDisplaySupport;
- ALOGI("Switching to %s multi-display mode",
- hasMultiDisplaySupport ? "generalized" : "legacy");
- } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
+ if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
hwcDisplayId);
- return {};
+ return true;
}
- std::optional<DisplayIdentificationInfo> info;
-
- if (mHasMultiDisplaySupport) {
- info = parseDisplayIdentificationData(port, data);
- ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
- } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
+ if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
- return {};
+ return true;
+ }
+
+ return false;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+ std::optional<DisplayIdentificationInfo> info;
+ if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+ info = DisplayIdentificationInfo{.id = *displayId,
+ .name = std::string(),
+ .deviceProductInfo = std::nullopt};
} else {
- ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
- hwcDisplayId);
- port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
+ uint8_t port;
+ DisplayIdentificationData data;
+ const bool hasDisplayIdentificationData =
+ getDisplayIdentificationData(hwcDisplayId, &port, &data);
+ if (mPhysicalDisplayIdMap.empty()) {
+ mHasMultiDisplaySupport = hasDisplayIdentificationData;
+ ALOGI("Switching to %s multi-display mode",
+ mHasMultiDisplaySupport ? "generalized" : "legacy");
+ }
+
+ if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
+ return {};
+ }
+
+ info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
+ const bool isPrimary = !mInternalHwcDisplayId;
+ if (mHasMultiDisplaySupport) {
+ if (const auto info = parseDisplayIdentificationData(port, data)) {
+ return *info;
+ }
+ ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+ } else {
+ ALOGW_IF(hasDisplayIdentificationData,
+ "Ignoring identification data for display %" PRIu64, hwcDisplayId);
+ port = isPrimary ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL;
+ }
+
+ return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+ .name = isPrimary ? "Internal display"
+ : "External display",
+ .deviceProductInfo = std::nullopt};
+ }();
}
- if (!mInternalHwcDisplayId) {
- mInternalHwcDisplayId = hwcDisplayId;
- } else if (!mExternalHwcDisplayId) {
- mExternalHwcDisplayId = hwcDisplayId;
+ if (!isConnected(info->id)) {
+ allocatePhysicalDisplay(hwcDisplayId, info->id);
+ }
+ return info;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
+ hwc2_display_t hwcDisplayId) {
+ const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+ if (!displayId) {
+ ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+ return {};
}
- if (info) return info;
-
- return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
- .name = hwcDisplayId == mInternalHwcDisplayId
- ? "Internal display"
- : "External display",
+ // The display will later be destroyed by a call to
+ // destroyDisplay(). For now we just mark it disconnected.
+ if (isConnected(*displayId)) {
+ mDisplayData[*displayId].hwcDisplay->setConnected(false);
+ } else {
+ ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+ }
+ // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+ // via SurfaceFlinger's onHotplugReceived callback handling
+ return DisplayIdentificationInfo{.id = *displayId,
+ .name = std::string(),
.deviceProductInfo = std::nullopt};
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41db501..3cb40b1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -77,6 +77,8 @@
virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
ui::PixelFormat* format) = 0;
+ virtual void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) = 0;
+
// Attempts to create a new layer on this display
virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
// Destroy a previously created layer
@@ -164,6 +166,7 @@
// Returns stable display ID (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
+ // This function is called from SurfaceFlinger.
virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
HWC2::Connection connection) = 0;
@@ -238,6 +241,9 @@
std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
ui::PixelFormat* format) override;
+ // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
+ void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) override;
+
// Attempts to create a new layer on this display
HWC2::Layer* createLayer(DisplayId displayId) override;
// Destroy a previously created layer
@@ -361,6 +367,10 @@
friend TestableSurfaceFlinger;
std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+ std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hwc2_display_t hwcDisplayId);
+ bool shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
+ bool hasDisplayIdentificationData) const;
+
void loadCapabilities();
void loadLayerMetadataSupport();
uint32_t getMaxVirtualDisplayCount() const;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 06e0cbb..1d8179c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -72,6 +72,10 @@
}
}
+void PowerAdvisor::onBootFinished() {
+ mBootFinished.store(true);
+}
+
void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
if (expected) {
mExpensiveDisplays.insert(displayId);
@@ -97,6 +101,12 @@
}
void PowerAdvisor::notifyDisplayUpdateImminent() {
+ // Only start sending this notification once the system has booted so we don't introduce an
+ // early-boot dependency on Power HAL
+ if (!mBootFinished.load()) {
+ return;
+ }
+
if (mSendUpdateImminent.load()) {
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 957d289..4a90acb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -35,6 +35,7 @@
public:
virtual ~PowerAdvisor();
+ virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual void notifyDisplayUpdateImminent() = 0;
};
@@ -56,12 +57,14 @@
PowerAdvisor();
~PowerAdvisor() override;
+ void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
void notifyDisplayUpdateImminent() override;
private:
HalWrapper* getPowerHal();
+ std::atomic_bool mBootFinished = false;
bool mReconnectPowerHal = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3765d0d..3d67a6b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,6 +116,7 @@
mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
+ mCurrentState.treeHasFrameRateVote = false;
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -219,6 +220,9 @@
}
mFlinger->markLayerPendingRemovalLocked(this);
+ if (hasInput()) {
+ mFlinger->dirtyInput();
+ }
}
void Layer::onRemovedFromCurrentState() {
@@ -1334,6 +1338,44 @@
return true;
}
+void Layer::updateTreeHasFrameRateVote() {
+ const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
+ auto parent = getParent();
+ while (parent) {
+ visitor(parent.get());
+ parent = parent->getParent();
+ }
+
+ traverse(LayerVector::StateSet::Current, visitor);
+ };
+
+ // update parents and children about the vote
+ // First traverse the tree and count how many layers has votes
+ int layersWithVote = 0;
+ traverseTree([&layersWithVote](Layer* layer) {
+ if (layer->mCurrentState.frameRate.rate > 0 ||
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+ layersWithVote++;
+ }
+ });
+
+ // Now update the other layers
+ bool transactionNeeded = false;
+ traverseTree([layersWithVote, &transactionNeeded](Layer* layer) {
+ if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) {
+ layer->mCurrentState.sequence++;
+ layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0;
+ layer->mCurrentState.modified = true;
+ layer->setTransactionFlags(eTransactionNeeded);
+ transactionNeeded = true;
+ }
+ });
+
+ if (transactionNeeded) {
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ }
+}
+
bool Layer::setFrameRate(FrameRate frameRate) {
if (!mFlinger->useFrameRateApi) {
return false;
@@ -1345,12 +1387,26 @@
mCurrentState.sequence++;
mCurrentState.frameRate = frameRate;
mCurrentState.modified = true;
+
+ updateTreeHasFrameRateVote();
+
setTransactionFlags(eTransactionNeeded);
return true;
}
-Layer::FrameRate Layer::getFrameRate() const {
- return getDrawingState().frameRate;
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+ const auto frameRate = getDrawingState().frameRate;
+ if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
+ return frameRate;
+ }
+
+ // This layer doesn't have a frame rate. If one of its ancestors or successors
+ // have a vote, return a NoVote for ancestors/successors to set the vote
+ if (getDrawingState().treeHasFrameRateVote) {
+ return {0, FrameRateCompatibility::NoVote};
+ }
+
+ return frameRate;
}
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1608,6 +1664,7 @@
mCurrentChildren.add(layer);
layer->setParent(this);
+ updateTreeHasFrameRateVote();
}
ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1615,7 +1672,24 @@
setTransactionFlags(eTransactionNeeded);
layer->setParent(nullptr);
- return mCurrentChildren.remove(layer);
+ const auto removeResult = mCurrentChildren.remove(layer);
+
+ updateTreeHasFrameRateVote();
+ layer->updateTreeHasFrameRateVote();
+
+ return removeResult;
+}
+
+void Layer::reparentChildren(const sp<Layer>& newParent) {
+ if (attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+
+ for (const sp<Layer>& child : mCurrentChildren) {
+ newParent->addChild(child);
+ }
+ mCurrentChildren.clear();
+ updateTreeHasFrameRateVote();
}
bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
@@ -1631,13 +1705,7 @@
return false;
}
- if (attachChildren()) {
- setTransactionFlags(eTransactionNeeded);
- }
- for (const sp<Layer>& child : mCurrentChildren) {
- newParent->addChild(child);
- }
- mCurrentChildren.clear();
+ reparentChildren(newParent);
return true;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e21a866..be80f78 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -259,6 +259,9 @@
int32_t frameRateSelectionPriority;
FrameRate frameRate;
+
+ // Indicates whether parents / children of this layer had set FrameRate
+ bool treeHasFrameRateVote;
};
explicit Layer(const LayerCreationArgs& args);
@@ -344,7 +347,8 @@
virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
virtual bool setMetadata(const LayerMetadata& data);
- virtual bool reparentChildren(const sp<IBinder>& layer);
+ bool reparentChildren(const sp<IBinder>& newParentHandle);
+ void reparentChildren(const sp<Layer>& newParent);
virtual void setChildrenDrawingParent(const sp<Layer>& layer);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
@@ -801,7 +805,7 @@
Rect getCroppedBufferSize(const Layer::State& s) const;
bool setFrameRate(FrameRate frameRate);
- virtual FrameRate getFrameRate() const;
+ virtual FrameRate getFrameRateForLayerTree() const;
protected:
// constant
@@ -830,6 +834,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
friend class RefreshRateSelectionTest;
+ friend class SetFrameRateTest;
virtual void commitTransaction(const State& stateToCommit);
@@ -1017,6 +1022,8 @@
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
+ void updateTreeHasFrameRateVote();
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 0031d70..68cd84f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -201,9 +201,10 @@
void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) {
- wp<Layer> stopLayer = stopLayerHandle != nullptr
- ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner
- : nullptr;
+ wp<Layer> stopLayer;
+ if (stopLayerHandle != nullptr && stopLayerHandle->localBinder() != nullptr) {
+ stopLayer = static_cast<Layer::Handle*>(stopLayerHandle.get())->owner;
+ }
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->linkToDeath(this);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 776e984..4e3f85f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -30,6 +30,7 @@
#include "EventThread.h"
namespace android {
+using base::StringAppendF;
DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name)
@@ -107,6 +108,12 @@
}
}
+void DispSyncSource::dump(std::string& result) const {
+ std::lock_guard lock(mVsyncMutex);
+ StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+ mDispSync->dump(result);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 328b8c1..f278712 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -36,6 +36,8 @@
void setCallback(VSyncSource::Callback* callback) override;
void setPhaseOffset(nsecs_t phaseOffset) override;
+ void dump(std::string&) const override;
+
private:
// The following method is the implementation of the DispSync::Callback.
virtual void onDispSyncEvent(nsecs_t when);
@@ -52,7 +54,7 @@
std::mutex mCallbackMutex;
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
- std::mutex mVsyncMutex;
+ mutable std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index acab5a6..0d6a92e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -425,7 +425,12 @@
// display is off, keep feeding clients at 60 Hz.
const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
- ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+ if (mState == State::VSync) {
+ ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+ std::string debugInfo = "VsyncSource debug info:\n";
+ mVSyncSource->dump(debugInfo);
+ ALOGW("%s", debugInfo.c_str());
+ }
LOG_FATAL_IF(!mVSyncState);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 466234d..98b1876 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,8 @@
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ virtual void dump(std::string& result) const = 0;
};
class EventThreadConnection : public BnDisplayEventConnection {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index fa46e6f..31da588 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,6 +45,7 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
+ void dump(std::string&) const override {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index a8e6756..8958d9a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRate().rate > 0) {
+ if (layer.getFrameRateForLayerTree().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -109,7 +109,7 @@
// Only use the layer if the reference still exists.
if (layer || CC_UNLIKELY(mTraceEnabled)) {
// Check if frame rate was set on layer.
- const auto frameRate = layer->getFrameRate();
+ const auto frameRate = layer->getFrameRateForLayerTree();
if (frameRate.rate > 0.f) {
const auto voteType = [&]() {
switch (frameRate.type) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 8a14572..8b08592 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
- if (layer.getFrameRate().rate > 0) {
+ if (layer.getFrameRateForLayerTree().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -166,7 +166,7 @@
if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
i++;
// Set layer vote if set
- const auto frameRate = layer->getFrameRate();
+ const auto frameRate = layer->getFrameRateForLayerTree();
const auto voteType = [&]() {
switch (frameRate.type) {
case Layer::FrameRateCompatibility::Default:
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index c04447d..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
return std::abs(fpsA - fpsB) <= MARGIN;
}
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->fps);
+ }
+
+ return refreshRates;
+}
+
} // namespace
namespace android::scheduler {
@@ -45,14 +58,21 @@
namespace impl {
PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())),
- mOffsets(initializeOffsets(refreshRateConfigs)),
- mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync)
+ : mThresholdForNextVsync(thresholdForNextVsync),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
void PhaseOffsets::dump(std::string& result) const {
const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,19 +87,24 @@
}
std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
- for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
- if (refreshRate->fps > 65.0f) {
- offsets.emplace(refreshRate->fps, getHighFpsOffsets(refreshRate->vsyncPeriod));
- } else {
- offsets.emplace(refreshRate->fps, getDefaultOffsets(refreshRate->vsyncPeriod));
- }
+ for (const auto& refreshRate : refreshRates) {
+ offsets.emplace(refreshRate,
+ getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
}
return offsets;
}
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+ if (fps > 65.0f) {
+ return getHighFpsOffsets(vsyncPeriod);
+ } else {
+ return getDefaultOffsets(vsyncPeriod);
+ }
+}
+
PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -159,8 +184,15 @@
[&fps](const std::pair<float, Offsets>& candidateFps) {
return fpsEqualsWithMargin(fps, candidateFps.first);
});
- LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
- return iter->second;
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
}
static void validateSysprops() {
@@ -231,19 +263,6 @@
};
}
-static std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->fps);
- }
-
- return refreshRates;
-}
-
std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
@@ -288,8 +307,7 @@
return iter->second;
}
- // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
- // This happens only during tests and not during regular device operation.
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
// In this case just construct the offset.
ALOGW("Can't find offset for %.2f fps", fps);
return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index b7d4eae..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,11 +66,15 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
-private:
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync);
std::unordered_map<float, Offsets> initializeOffsets(
- const scheduler::RefreshRateConfigs&) const;
- Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+ const std::vector<float>& refreshRates) const;
+ Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
const nsecs_t mThresholdForNextVsync;
const std::unordered_map<float, Offsets> mOffsets;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 920f0ec..cd6075f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -419,20 +419,6 @@
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
-
- // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
- // API to set desired rate
- {
- const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
- if (vote != 0 &&
- layer->getName() ==
- "SurfaceView - "
- "com.google.android.youtube/"
- "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
- layer->setFrameRate(
- Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
- }
- }
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index a6fb3a4..2a2d7c5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -108,6 +108,8 @@
*/
virtual CancelResult cancel(CallbackToken token) = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncDispatch() = default;
VSyncDispatch(VSyncDispatch const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index d0f18ab..460d4a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,6 +15,7 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
#include <utils/Trace.h>
#include <vector>
@@ -23,6 +24,7 @@
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
@@ -122,6 +124,28 @@
mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
}
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mRunningMutex);
+ std::string armedInfo;
+ if (mArmedInfo) {
+ StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+ (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+ (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+ }
+
+ StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+ mRunning ? "(in callback function)" : "", armedInfo.c_str());
+ StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+ mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+ if (mLastDispatchTime) {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+ (systemTime() - *mLastDispatchTime) / 1e6f);
+ } else {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+ }
+}
+
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
VSyncTracker& tracker, nsecs_t timerSlack,
nsecs_t minVsyncDistance)
@@ -296,6 +320,18 @@
return CancelResult::TooLate;
}
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+ mMinVsyncDistance / 1e6f);
+ StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+ (mIntendedWakeupTime - systemTime()) / 1e6f);
+ StringAppendF(&result, "\tCallbacks:\n");
+ for (const auto& [token, entry] : mCallbacks) {
+ entry->dump(result);
+ }
+}
+
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
VSyncDispatch::Callback const& callbackFn,
std::string const& callbackName)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fd0a034..390e0c4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -71,6 +71,8 @@
// Block calling thread while the callback is executing.
void ensureNotRunning();
+ void dump(std::string& result) const;
+
private:
std::string const mName;
VSyncDispatch::Callback const mCallback;
@@ -86,7 +88,7 @@
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
- std::mutex mRunningMutex;
+ mutable std::mutex mRunningMutex;
std::condition_variable mCv;
bool mRunning GUARDED_BY(mRunningMutex) = false;
};
@@ -112,6 +114,7 @@
void unregisterCallback(CallbackToken token) final;
ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
CancelResult cancel(CallbackToken token) final;
+ void dump(std::string& result) const final;
private:
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 399da19..a3cb772 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -22,6 +22,7 @@
//#define LOG_NDEBUG 0
#include "VSyncPredictor.h"
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <utils/Log.h>
@@ -31,6 +32,7 @@
#include <sstream>
namespace android::scheduler {
+using base::StringAppendF;
static auto constexpr kMaxPercent = 100u;
@@ -115,10 +117,10 @@
auto it = mRateMap.find(mIdealPeriod);
auto const currentPeriod = std::get<0>(it->second);
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
- // for the intercept calculation, so scale the ordinals by 10 to continue
+ // for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
// scheduler::utils::calculate_mean to have a fixed point fractional part.
- static constexpr int kScalingFactor = 10;
+ static constexpr int64_t kScalingFactor = 1000;
for (auto i = 0u; i < mTimestamps.size(); i++) {
traceInt64If("VSP-ts", mTimestamps[i]);
@@ -147,7 +149,7 @@
return false;
}
- nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+ nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
@@ -263,6 +265,18 @@
clearTimestamps();
}
+void VSyncPredictor::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+ StringAppendF(&result, "\tRefresh Rate Map:\n");
+ for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+ StringAppendF(&result,
+ "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+ idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+ std::get<1>(periodInterceptTuple));
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index ef1d88a..3ca878d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -60,6 +60,8 @@
std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ void dump(std::string& result) const final;
+
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 8987dcd..892ae62 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -28,6 +28,7 @@
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
@@ -73,11 +74,12 @@
public:
CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
nsecs_t period, nsecs_t offset, nsecs_t notBefore)
- : mCallback(cb),
+ : mName(name),
+ mCallback(cb),
mRegistration(dispatch,
std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
std::placeholders::_2),
- std::string(name)),
+ mName),
mPeriod(period),
mOffset(offset),
mLastCallTime(notBefore) {}
@@ -112,6 +114,13 @@
mRegistration.cancel();
}
+ void dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+ mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+ mStopped ? "stopped" : "running");
+ }
+
private:
void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
@@ -137,6 +146,7 @@
// Note change in sign between the two defnitions.
nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+ const std::string mName;
DispSync::Callback* const mCallback;
std::mutex mutable mMutex;
@@ -349,7 +359,35 @@
}
void VSyncReactor::dump(std::string& result) const {
- result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "VsyncReactor in use\n");
+ StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+ StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+ mInternalIgnoreFences, mExternalIgnoreFences);
+ StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+ mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+ if (mPeriodTransitioningTo) {
+ StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+ } else {
+ StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+ }
+
+ if (mLastHwVsync) {
+ StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+ (mClock->now() - *mLastHwVsync) / 1e6f);
+ } else {
+ StringAppendF(&result, "No Last HW vsync\n");
+ }
+
+ StringAppendF(&result, "CallbackRepeaters:\n");
+ for (const auto& [callback, repeater] : mCallbacks) {
+ repeater->dump(result);
+ }
+
+ StringAppendF(&result, "VSyncTracker:\n");
+ mTracker->dump(result);
+ StringAppendF(&result, "VSyncDispatch:\n");
+ mDispatch->dump(result);
}
void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 7d8a8e3..5ee29f8 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -75,7 +75,7 @@
std::unique_ptr<VSyncDispatch> const mDispatch;
size_t const mPendingLimit;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index a25b8a9..05a6fc3 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bbb07a8..dd2be7c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -364,11 +364,8 @@
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlurs = atoi(value);
- property_get("debug.sf.disable_blurs", value, "0");
- bool disableBlurs = atoi(value);
- mEnableBlurs = supportsBlurs && !disableBlurs;
- ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
- disableBlurs);
+ mSupportsBlur = supportsBlurs;
+ ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
property_get("ro.sf.blurs_are_expensive", value, "0");
mBlursAreExpensive = atoi(value);
@@ -570,6 +567,7 @@
postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
readPersistentProperties();
+ mPowerAdvisor.onBootFinished();
mBootStage = BootStage::FINISHED;
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -625,7 +623,7 @@
.setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mEnableBlurs)
+ .setSupportsBackgroundBlur(mSupportsBlur)
.setContextPriority(useContextPriority
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
@@ -707,6 +705,11 @@
property_get("persist.sys.sf.color_mode", value, "0");
mForceColorMode = static_cast<ColorMode>(atoi(value));
+
+ property_get("persist.sys.sf.disable_blurs", value, "0");
+ bool disableBlurs = atoi(value);
+ mDisableBlurs = disableBlurs;
+ ALOGI_IF(disableBlurs, "Disabling blur effects, user preference.");
}
void SurfaceFlinger::startBootAnim() {
@@ -1032,7 +1035,7 @@
ATRACE_CALL();
ALOGV("performSetActiveConfig");
if (mCheckPendingFence) {
- if (previousFrameMissed()) {
+ if (previousFramePending()) {
// fence has not signaled yet. wait for the next invalidate
mEventQueue->invalidate();
return true;
@@ -1779,13 +1782,17 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+ const sp<Fence>& fence = previousFrameFence();
if (fence == Fence::NO_FENCE) {
return false;
@@ -1798,6 +1805,16 @@
return (fence->getStatus() == Fence::Status::Unsignaled);
}
+nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+ const sp<Fence>& fence = previousFrameFence();
+
+ if (fence == Fence::NO_FENCE) {
+ return Fence::SIGNAL_TIME_INVALID;
+ }
+
+ return fence->getSignalTime();
+}
+
void SurfaceFlinger::populateExpectedPresentTime() {
DisplayStatInfo stats;
mScheduler->getDisplayStatInfo(&stats);
@@ -1815,6 +1832,7 @@
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
+ const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
populateExpectedPresentTime();
// When Backpressure propagation is enabled we want to give a small grace period
@@ -1825,12 +1843,32 @@
(mPropagateBackpressureClientComposition || !mHadClientComposition))
? 1
: 0;
- const TracedOrdinal<bool> frameMissed = {"FrameMissed",
- previousFrameMissed(
- graceTimeForPresentFenceMs)};
- const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+
+ // Pending frames may trigger backpressure propagation.
+ const TracedOrdinal<bool> framePending = {"PrevFramePending",
+ previousFramePending(
+ graceTimeForPresentFenceMs)};
+
+ // Frame missed counts for metrics tracking.
+ // A frame is missed if the prior frame is still pending. If no longer pending,
+ // then we still count the frame as missed if the predicted present time
+ // was further in the past than when the fence actually fired.
+
+ // Add some slop to correct for drift. This should generally be
+ // smaller than a typical frame duration, but should not be so small
+ // that it reports reasonable drift as a missed frame.
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
+ const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+ const nsecs_t previousPresentTime = previousFramePresentTime();
+ const TracedOrdinal<bool> frameMissed =
+ {"PrevFrameMissed",
+ framePending ||
+ (previousPresentTime >= 0 &&
+ (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
+ const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
mHadDeviceComposition && frameMissed};
- const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+ const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
mHadClientComposition && frameMissed};
if (frameMissed) {
@@ -1850,7 +1888,7 @@
mGpuFrameMissedCount++;
}
- if (frameMissed && mPropagateBackpressure) {
+ if (framePending && mPropagateBackpressure) {
if ((hwcFrameMissed && !gpuFrameMissed) ||
mPropagateBackpressureClientComposition) {
signalLayerUpdate();
@@ -2353,7 +2391,8 @@
}
DisplayDeviceState state;
- state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId)};
+ state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
+ event.hwcDisplayId};
state.isSecure = true; // All physical displays are currently considered secure.
state.displayName = info->name;
@@ -2362,6 +2401,12 @@
mPhysicalDisplayTokens.emplace(displayId, std::move(token));
mInterceptor->saveDisplayCreation(state);
+ } else {
+ ALOGV("Recreating display %s", to_string(displayId).c_str());
+
+ const auto token = it->second;
+ auto& state = mCurrentState.displays.editValueFor(token);
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
}
} else {
ALOGV("Removing display %s", to_string(displayId).c_str());
@@ -2389,13 +2434,14 @@
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
- const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+ const DisplayDeviceState& state,
+ const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) {
auto displayId = compositionDisplay->getDisplayId();
DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
creationArgs.isSecure = state.isSecure;
- creationArgs.displaySurface = dispSurface;
+ creationArgs.displaySurface = displaySurface;
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
@@ -2471,6 +2517,143 @@
return display;
}
+void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& state) {
+ int width = 0;
+ int height = 0;
+ ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+ if (state.physical) {
+ const auto& activeConfig =
+ getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
+ width = activeConfig->getWidth();
+ height = activeConfig->getHeight();
+ pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
+ } else if (state.surface != nullptr) {
+ int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+ ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+ status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+ ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+ int intPixelFormat;
+ status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
+ ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+ pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
+ } else {
+ // Virtual displays without a surface are dormant:
+ // they have external state (layer stack, projection,
+ // etc.) but no internal state (i.e. a DisplayDevice).
+ return;
+ }
+
+ compositionengine::DisplayCreationArgsBuilder builder;
+ if (const auto& physical = state.physical) {
+ builder.setPhysical({physical->id, physical->type});
+ }
+ builder.setPixels(ui::Size(width, height));
+ builder.setPixelFormat(pixelFormat);
+ builder.setIsSecure(state.isSecure);
+ builder.setLayerStackId(state.layerStack);
+ builder.setPowerAdvisor(&mPowerAdvisor);
+ builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+ builder.setName(state.displayName);
+ const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+
+ sp<compositionengine::DisplaySurface> displaySurface;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferProducer> bqProducer;
+ sp<IGraphicBufferConsumer> bqConsumer;
+ getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
+
+ std::optional<DisplayId> displayId = compositionDisplay->getId();
+
+ if (state.isVirtual()) {
+ sp<VirtualDisplaySurface> vds =
+ new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+ bqConsumer, state.displayName);
+
+ displaySurface = vds;
+ producer = vds;
+ } else {
+ ALOGE_IF(state.surface != nullptr,
+ "adding a supported display, but rendering "
+ "surface is provided (%p), ignoring it",
+ state.surface.get());
+
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+ maxGraphicsWidth, maxGraphicsHeight);
+ producer = bqProducer;
+ }
+
+ LOG_FATAL_IF(!displaySurface);
+ const auto display = setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer);
+ mDisplays.emplace(displayToken, display);
+ if (!state.isVirtual()) {
+ LOG_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, true);
+ }
+
+ if (display->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+ }
+}
+
+void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ // Save display ID before disconnecting.
+ const auto displayId = display->getId();
+ display->disconnect();
+
+ if (!display->isVirtual()) {
+ LOG_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, false);
+ }
+ }
+
+ mDisplays.erase(displayToken);
+}
+
+void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& currentState,
+ const DisplayDeviceState& drawingState) {
+ const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
+ const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
+ if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
+ // changing the surface is like destroying and recreating the DisplayDevice
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ display->disconnect();
+ }
+ mDisplays.erase(displayToken);
+ if (const auto& physical = currentState.physical) {
+ getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
+ }
+ processDisplayAdded(displayToken, currentState);
+ if (currentState.physical) {
+ initializeDisplays();
+ }
+ return;
+ }
+
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ if (currentState.layerStack != drawingState.layerStack) {
+ display->setLayerStack(currentState.layerStack);
+ }
+ if ((currentState.orientation != drawingState.orientation) ||
+ (currentState.viewport != drawingState.viewport) ||
+ (currentState.frame != drawingState.frame)) {
+ display->setProjection(currentState.orientation, currentState.viewport,
+ currentState.frame);
+ }
+ if (currentState.width != drawingState.width ||
+ currentState.height != drawingState.height) {
+ display->setDisplaySize(currentState.width, currentState.height);
+ if (display->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
+ }
+ }
+ }
+}
+
void SurfaceFlinger::processDisplayChangesLocked() {
// here we take advantage of Vector's copy-on-write semantics to
// improve performance by skipping the transaction entirely when
@@ -2479,159 +2662,31 @@
const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
if (!curr.isIdenticalTo(draw)) {
mVisibleRegionsDirty = true;
- const size_t cc = curr.size();
- size_t dc = draw.size();
// find the displays that were removed
// (ie: in drawing state but not in current state)
// also handle displays that changed
// (ie: displays that are in both lists)
- for (size_t i = 0; i < dc;) {
- const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+ for (size_t i = 0; i < draw.size(); i++) {
+ const wp<IBinder>& displayToken = draw.keyAt(i);
+ const ssize_t j = curr.indexOfKey(displayToken);
if (j < 0) {
// in drawing state but not in current state
- if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
- // Save display ID before disconnecting.
- const auto displayId = display->getId();
- display->disconnect();
-
- if (!display->isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, false);
- }
- }
-
- mDisplays.erase(draw.keyAt(i));
+ processDisplayRemoved(displayToken);
} else {
// this display is in both lists. see if something changed.
- const DisplayDeviceState& state(curr[j]);
- const wp<IBinder>& displayToken = curr.keyAt(j);
- const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
- const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
- if (state_binder != draw_binder) {
- // changing the surface is like destroying and
- // recreating the DisplayDevice, so we just remove it
- // from the drawing state, so that it get re-added
- // below.
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- display->disconnect();
- }
- mDisplays.erase(displayToken);
- mDrawingState.displays.removeItemsAt(i);
- dc--;
- // at this point we must loop to the next item
- continue;
- }
-
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- if (state.layerStack != draw[i].layerStack) {
- display->setLayerStack(state.layerStack);
- }
- if ((state.orientation != draw[i].orientation) ||
- (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
- display->setProjection(state.orientation, state.viewport, state.frame);
- }
- if (state.width != draw[i].width || state.height != draw[i].height) {
- display->setDisplaySize(state.width, state.height);
- if (display->isPrimary()) {
- mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
- }
- }
- }
+ const DisplayDeviceState& currentState = curr[j];
+ const DisplayDeviceState& drawingState = draw[i];
+ processDisplayChanged(displayToken, currentState, drawingState);
}
- ++i;
}
// find displays that were added
// (ie: in current state but not in drawing state)
- for (size_t i = 0; i < cc; i++) {
- if (draw.indexOfKey(curr.keyAt(i)) < 0) {
- const DisplayDeviceState& state(curr[i]);
-
- int width = 0;
- int height = 0;
- ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
- if (state.physical) {
- const auto& activeConfig =
- getCompositionEngine().getHwComposer().getActiveConfig(
- state.physical->id);
- width = activeConfig->getWidth();
- height = activeConfig->getHeight();
- pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
- } else if (state.surface != nullptr) {
- int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
- ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
- status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
- ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
- int intPixelFormat;
- status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
- ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
- pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
- } else {
- // Virtual displays without a surface are dormant:
- // they have external state (layer stack, projection,
- // etc.) but no internal state (i.e. a DisplayDevice).
- continue;
- }
-
- compositionengine::DisplayCreationArgsBuilder builder;
- if (const auto& physical = state.physical) {
- builder.setPhysical({physical->id, physical->type});
- }
- builder.setPixels(ui::Size(width, height));
- builder.setPixelFormat(pixelFormat);
- builder.setIsSecure(state.isSecure);
- builder.setLayerStackId(state.layerStack);
- builder.setPowerAdvisor(&mPowerAdvisor);
- builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays ||
- getHwComposer().isUsingVrComposer());
- builder.setName(state.displayName);
- auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
-
- sp<compositionengine::DisplaySurface> dispSurface;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferProducer> bqProducer;
- sp<IGraphicBufferConsumer> bqConsumer;
- getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
-
- std::optional<DisplayId> displayId = compositionDisplay->getId();
-
- if (state.isVirtual()) {
- sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
- bqProducer, bqConsumer, state.displayName);
-
- dispSurface = vds;
- producer = vds;
- } else {
- ALOGE_IF(state.surface != nullptr,
- "adding a supported display, but rendering "
- "surface is provided (%p), ignoring it",
- state.surface.get());
-
- LOG_ALWAYS_FATAL_IF(!displayId);
- dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
- maxGraphicsWidth, maxGraphicsHeight);
- producer = bqProducer;
- }
-
- const wp<IBinder>& displayToken = curr.keyAt(i);
- if (dispSurface != nullptr) {
- mDisplays.emplace(displayToken,
- setupNewDisplayDeviceInternal(displayToken,
- compositionDisplay, state,
- dispSurface, producer));
- if (!state.isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, true);
- }
-
- const auto displayDevice = mDisplays[displayToken];
- if (displayDevice->isPrimary()) {
- mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
- displayDevice->getHeight());
- }
- }
+ for (size_t i = 0; i < curr.size(); i++) {
+ const wp<IBinder>& displayToken = curr.keyAt(i);
+ if (draw.indexOfKey(displayToken) < 0) {
+ processDisplayAdded(displayToken, curr[i]);
}
}
}
@@ -2806,7 +2861,7 @@
// input changes but all input changes will spring from these transactions
// so the cache is safe but not optimal. It seems like it might be annoyingly
// costly to cache and comapre the actual InputWindowHandle vector though.
- if (!mInputDirty) {
+ if (!mInputDirty && !mInputWindowCommands.syncInputWindows) {
return;
}
@@ -3593,7 +3648,7 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eLayerStackChanged) {
@@ -4093,6 +4148,9 @@
}
if (currentMode == HWC_POWER_MODE_OFF) {
+ if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
+ }
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -4103,19 +4161,11 @@
mVisibleRegionsDirty = true;
mHasPoweredOff = true;
repaintEverything();
-
- struct sched_param param = {0};
- param.sched_priority = 1;
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
- ALOGW("Couldn't set SCHED_FIFO on display on");
- }
} else if (mode == HWC_POWER_MODE_OFF) {
// Turn off the display
- struct sched_param param = {0};
- if (sched_setscheduler(0, SCHED_OTHER, ¶m) != 0) {
- ALOGW("Couldn't set SCHED_OTHER on display off");
+ if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
}
-
if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
@@ -4488,12 +4538,11 @@
result.append("\n");
}
-LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
- Mutex::Autolock _l(mStateLock);
- const auto device = getDefaultDisplayDeviceLocked();
+LayersProto SurfaceFlinger::dumpDrawingStateProto(
+ uint32_t traceFlags, const sp<const DisplayDevice>& displayDevice) const {
LayersProto layersProto;
for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- layer->writeToProto(layersProto, traceFlags, device);
+ layer->writeToProto(layersProto, traceFlags, displayDevice);
}
return layersProto;
@@ -4525,7 +4574,10 @@
LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
LayersProto layersProto;
- postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); }));
+ postMessageSync(new LambdaMessage([&]() {
+ const auto& displayDevice = getDefaultDisplayDeviceLocked();
+ layersProto = dumpDrawingStateProto(traceFlags, displayDevice);
+ }));
return layersProto;
}
@@ -5300,6 +5352,26 @@
}
}
+status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+ static constexpr int kFifoPriority = 2;
+ static constexpr int kOtherPriority = 0;
+
+ struct sched_param param = {0};
+ int sched_policy;
+ if (enabled) {
+ sched_policy = SCHED_FIFO;
+ param.sched_priority = kFifoPriority;
+ } else {
+ sched_policy = SCHED_OTHER;
+ param.sched_priority = kOtherPriority;
+ }
+
+ if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
if (displayToken) {
@@ -6064,18 +6136,22 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mStateLock);
- if (authenticateSurfaceTextureLocked(surface)) {
- sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
- if (layer->setFrameRate(
- Layer::FrameRate(frameRate,
- Layer::FrameRate::convertCompatibility(compatibility)))) {
- setTransactionFlags(eTraversalNeeded);
+ postMessageAsync(new LambdaMessage([=]() NO_THREAD_SAFETY_ANALYSIS {
+ Mutex::Autolock lock(mStateLock);
+ if (authenticateSurfaceTextureLocked(surface)) {
+ sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer->setFrameRate(
+ Layer::FrameRate(frameRate,
+ Layer::FrameRate::convertCompatibility(compatibility)))) {
+ setTransactionFlags(eTraversalNeeded);
+ }
+ } else {
+ ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
+ return BAD_VALUE;
}
- } else {
- ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
- return BAD_VALUE;
- }
+ return NO_ERROR;
+ }));
+
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e6b91e6..60904f6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -259,6 +259,9 @@
// overhead that is caused by reading from sysprop.
static bool useFrameRateApi;
+ // set main thread scheduling policy
+ static status_t setSchedFifo(bool enabled) ANDROID_API;
+
static char const* getServiceName() ANDROID_API {
return "SurfaceFlinger";
}
@@ -820,9 +823,14 @@
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
const DisplayDeviceState& state,
- const sp<compositionengine::DisplaySurface>& dispSurface,
+ const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer);
void processDisplayChangesLocked();
+ void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
+ void processDisplayRemoved(const wp<IBinder>& displayToken);
+ void processDisplayChanged(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& currentState,
+ const DisplayDeviceState& drawingState);
void processDisplayHotplugEventsLocked();
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
@@ -839,7 +847,21 @@
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
- bool previousFrameMissed(int graceTimeMs = 0);
+ // Gets the fence for the previous frame.
+ // Must be called on the main thread.
+ sp<Fence> previousFrameFence();
+
+ // Whether the previous frame has not yet been presented to the display.
+ // If graceTimeMs is positive, this method waits for at most the provided
+ // grace period before reporting if the frame missed.
+ // Must be called on the main thread.
+ bool previousFramePending(int graceTimeMs = 0);
+
+ // Returns the previous time that the frame was presented. If the frame has
+ // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+ // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+ // Must be called on the main thread.
+ nsecs_t previousFramePresentTime();
// Populates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
@@ -923,7 +945,8 @@
void dumpDisplayIdentificationData(std::string& result) const;
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const;
- LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
+ const sp<const DisplayDevice>& displayDevice = nullptr) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
// Dumps state from HW Composer
@@ -1054,8 +1077,10 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
bool mUseHwcVirtualDisplays = false;
+ // If blurs should be enabled on this device.
+ bool mSupportsBlur = false;
// Disable blurs, for debugging
- bool mEnableBlurs = false;
+ std::atomic<bool> mDisableBlurs = false;
// If blurs are considered expensive and should require high GPU frequency.
bool mBlursAreExpensive = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 5b3cd69..80102bd 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -194,7 +194,7 @@
Increment* SurfaceInterceptor::createTraceIncrementLocked() {
Increment* increment(mTrace.add_increment());
- increment->set_time_stamp(systemTime());
+ increment->set_time_stamp(elapsedRealtimeNano());
return increment;
}
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index c5556ec..a9c3332 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -45,19 +45,21 @@
}
void SurfaceTracing::addFirstEntry() {
+ const auto displayDevice = mFlinger.getDefaultDisplayDevice();
LayersTraceProto entry;
{
std::scoped_lock lock(mSfLock);
- entry = traceLayersLocked("tracing.enable");
+ entry = traceLayersLocked("tracing.enable", displayDevice);
}
addTraceToBuffer(entry);
}
LayersTraceProto SurfaceTracing::traceWhenNotified() {
+ const auto displayDevice = mFlinger.getDefaultDisplayDevice();
std::unique_lock<std::mutex> lock(mSfLock);
mCanStartTrace.wait(lock);
android::base::ScopedLockAssertion assumeLock(mSfLock);
- LayersTraceProto entry = traceLayersLocked(mWhere);
+ LayersTraceProto entry = traceLayersLocked(mWhere, displayDevice);
lock.unlock();
return entry;
}
@@ -127,7 +129,12 @@
}
status_t SurfaceTracing::writeToFile() {
- mThread.join();
+ std::thread thread;
+ {
+ std::scoped_lock lock(mTraceLock);
+ thread = std::move(mThread);
+ }
+ thread.join();
return mLastErr;
}
@@ -160,13 +167,14 @@
mTraceFlags = flags;
}
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
+LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where,
+ const sp<const DisplayDevice>& displayDevice) {
ATRACE_CALL();
LayersTraceProto entry;
entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags, displayDevice));
mFlinger.dumpOffscreenLayersProto(layers);
entry.mutable_layers()->Swap(&layers);
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 3c24881..83872ed 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -16,17 +16,19 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include <layerproto/LayerProtoHeader.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
-#include <android-base/thread_annotations.h>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
+#include "DisplayDevice.h"
+
using namespace android::surfaceflinger;
namespace android {
@@ -85,19 +87,21 @@
void mainLoop();
void addFirstEntry();
LayersTraceProto traceWhenNotified();
- LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+ LayersTraceProto traceLayersLocked(const char* where,
+ const sp<const DisplayDevice>& displayDevice)
+ REQUIRES(mSfLock);
// Returns true if trace is enabled.
bool addTraceToBuffer(LayersTraceProto& entry);
void writeProtoFileLocked() REQUIRES(mTraceLock);
- const SurfaceFlinger& mFlinger;
+ SurfaceFlinger& mFlinger;
status_t mLastErr = NO_ERROR;
std::thread mThread;
std::condition_variable mCanStartTrace;
std::mutex& mSfLock;
- uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_ALL;
+ uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
const char* mWhere GUARDED_BY(mSfLock) = "";
mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 96fd03a..606e137 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -188,17 +188,16 @@
TimeStats::~TimeStats() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
}
void TimeStats::onBootFinished() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8bbae1f..806b47e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -167,15 +167,15 @@
virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
return AStatsEventList_addStatsEvent(data);
}
- virtual void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata* metadata,
- void* cookie) {
- return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
+ virtual void setStatsPullAtomCallback(int32_t atom_tag,
+ AStatsManager_PullAtomMetadata* metadata,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) {
+ return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
}
- virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
- return AStatsManager_unregisterPullAtomCallback(atom_tag);
+ virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
+ return AStatsManager_clearPullAtomCallback(atom_tag);
}
virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index d7ad9de..2b8424c 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -31,6 +31,7 @@
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
#include <displayservice/DisplayService.h>
+#include <errno.h>
#include <hidl/LegacySupport.h>
#include <processgroup/sched_policy.h>
#include "SurfaceFlinger.h"
@@ -114,10 +115,8 @@
startDisplayService(); // dependency on SF getting registered above
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO");
+ if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
}
// run surface flinger in this thread
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 8315037..000628f 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 932c7c8..40ec502 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -52,9 +52,10 @@
virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
const char* name, uint32_t width, uint32_t height,
uint32_t flags = 0, SurfaceControl* parent = nullptr,
- uint32_t* outTransformHint = nullptr) {
- auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags,
- parent, outTransformHint);
+ uint32_t* outTransformHint = nullptr,
+ PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+ auto layer =
+ createSurface(client, name, width, height, format, flags, parent, outTransformHint);
Transaction t;
t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
@@ -81,8 +82,9 @@
virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
uint32_t flags = 0, SurfaceControl* parent = nullptr,
- uint32_t* outTransformHint = nullptr) {
- return createLayer(mClient, name, width, height, flags, parent, outTransformHint);
+ uint32_t* outTransformHint = nullptr,
+ PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+ return createLayer(mClient, name, width, height, flags, parent, outTransformHint, format);
}
sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 2fd2579..d666d7e 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -365,6 +365,67 @@
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) {
+ int32_t width = 100;
+ int32_t height = 100;
+ Rect crop = Rect(0, 0, width, height);
+
+ sp<SurfaceControl> behindLayer = createColorLayer("Behind layer", Color::RED);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, 0, nullptr, nullptr,
+ PIXEL_FORMAT_RGBX_8888));
+
+ Transaction()
+ .setLayer(layer, INT32_MAX - 1)
+ .show(layer)
+ .setLayerStack(behindLayer, mDisplayLayerStack)
+ .setCrop_legacy(behindLayer, crop)
+ .setLayer(behindLayer, INT32_MAX - 2)
+ .show(behindLayer)
+ .apply();
+
+ sp<Surface> surface = layer->getSurface();
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+ if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+ Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+ } else {
+ Transaction().setBuffer(layer, buffer).apply();
+ }
+
+ {
+ SCOPED_TRACE("Buffer Opaque Format");
+ auto shot = screenshot();
+ shot->expectColor(crop, Color::BLACK);
+ }
+
+ buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ ASSERT_NO_FATAL_FAILURE(
+ TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+ if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+ Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+ } else {
+ Transaction().setBuffer(layer, buffer).apply();
+ }
+
+ {
+ SCOPED_TRACE("Buffer Transparent Format");
+ auto shot = screenshot();
+ shot->expectColor(crop, Color::RED);
+ }
+}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 040852f..f0af363 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -88,13 +88,14 @@
sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
uint32_t flags = 0, SurfaceControl* parent = nullptr,
- uint32_t* outTransformHint = nullptr) {
+ uint32_t* outTransformHint = nullptr,
+ PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
// if the flags already have a layer type specified, return an error
if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
return nullptr;
}
return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
- outTransformHint);
+ outTransformHint, format);
}
void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1cd8731..2f2fb20 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -50,6 +50,7 @@
"PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "SetFrameRateTest.cpp",
"RefreshRateConfigsTest.cpp",
"RefreshRateSelectionTest.cpp",
"RefreshRateStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 680b0a0..2dcaf63 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -305,14 +305,14 @@
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
ceDisplayArgs);
- test->mDisplay =
- FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- DisplayConnectionType::Internal, true /* isPrimary */)
- .setDisplaySurface(test->mDisplaySurface)
- .setNativeWindow(test->mNativeWindow)
- .setSecure(Derived::IS_SECURE)
- .setPowerMode(Derived::INIT_POWER_MODE)
- .inject();
+ test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+ DisplayConnectionType::Internal, HWC_DISPLAY,
+ true /* isPrimary */)
+ .setDisplaySurface(test->mDisplaySurface)
+ .setNativeWindow(test->mNativeWindow)
+ .setSecure(Derived::IS_SECURE)
+ .setPowerMode(Derived::INIT_POWER_MODE)
+ .inject();
Mock::VerifyAndClear(test->mNativeWindow);
test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index a023367..c2ddfce 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <functional>
+#include <string_view>
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -124,6 +127,10 @@
return DisplayIdentificationData(bytes, bytes + N - 1);
}
+uint32_t hash(const char* str) {
+ return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+}
+
} // namespace
const DisplayIdentificationData& getInternalEdid() {
@@ -173,7 +180,8 @@
EXPECT_EQ(0x4ca3u, edid->manufacturerId);
EXPECT_STREQ("SEC", edid->pnpId.data());
// ASCII text should be used as fallback if display name and serial number are missing.
- EXPECT_EQ("121AT11-801", edid->displayName);
+ EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
EXPECT_EQ(12610, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
@@ -182,6 +190,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(0x22f0u, edid->manufacturerId);
EXPECT_STREQ("HWP", edid->pnpId.data());
+ EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
EXPECT_EQ("HP ZR30w", edid->displayName);
EXPECT_EQ(10348, edid->productId);
EXPECT_EQ(22, edid->manufactureOrModelYear);
@@ -191,6 +200,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(0x4c2du, edid->manufacturerId);
EXPECT_STREQ("SAM", edid->pnpId.data());
+ EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
EXPECT_EQ("SAMSUNG", edid->displayName);
EXPECT_EQ(2302, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
@@ -200,6 +210,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(13481, edid->manufacturerId);
EXPECT_STREQ("MEI", edid->pnpId.data());
+ EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
EXPECT_EQ("Panasonic-TV", edid->displayName);
EXPECT_EQ(41622, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -209,6 +220,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(8355, edid->manufacturerId);
EXPECT_STREQ("HEC", edid->pnpId.data());
+ EXPECT_EQ(hash("Hisense"), edid->modelHash);
EXPECT_EQ("Hisense", edid->displayName);
EXPECT_EQ(0, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -218,6 +230,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(3724, edid->manufacturerId);
EXPECT_STREQ("CTL", edid->pnpId.data());
+ EXPECT_EQ(hash("LP2361"), edid->modelHash);
EXPECT_EQ("LP2361", edid->displayName);
EXPECT_EQ(9373, edid->productId);
EXPECT_EQ(23, edid->manufactureOrModelYear);
@@ -234,13 +247,15 @@
auto edid = parseEdid(data);
ASSERT_TRUE(edid);
// Serial number should be used as fallback if display name is invalid.
- EXPECT_EQ("CN4202137Q", edid->displayName);
+ const auto modelHash = hash("CN4202137Q");
+ EXPECT_EQ(modelHash, edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
// Parsing should succeed even if EDID is truncated.
data.pop_back();
edid = parseEdid(data);
ASSERT_TRUE(edid);
- EXPECT_EQ("CN4202137Q", edid->displayName);
+ EXPECT_EQ(modelHash, edid->modelHash);
}
TEST(DisplayIdentificationTest, getPnpId) {
@@ -278,7 +293,7 @@
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("121AT11-801", info.name.data());
+ EXPECT_STREQ("", info.name.data());
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
EXPECT_STREQ("12610", info.productId.data());
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 6d00ccc..24eeac7 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -246,6 +246,7 @@
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+ constexpr hwc2_display_t DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
// The DisplayDevice is required to have a framebuffer (behind the
// ANativeWindow interface) which uses the actual hardware display
@@ -270,7 +271,7 @@
auto injector =
FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
- true /* isPrimary */);
+ DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
injector.setNativeWindow(mNativeWindow);
if (injectExtra) {
@@ -373,6 +374,23 @@
static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
};
+template <typename>
+struct HwcDisplayIdGetter {
+ static constexpr std::optional<hwc2_display_t> value;
+};
+
+constexpr hwc2_display_t HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <DisplayId::Type displayId>
+struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
+ static constexpr std::optional<hwc2_display_t> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+ static constexpr std::optional<hwc2_display_t> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
// DisplayIdType can be:
// 1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
// 2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
@@ -382,6 +400,7 @@
struct DisplayVariant {
using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+ using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
// The display width and height
static constexpr int WIDTH = width;
@@ -418,9 +437,9 @@
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
ceDisplayArgs.build());
- auto injector =
- FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- CONNECTION_TYPE::value, static_cast<bool>(PRIMARY));
+ auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+ CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
+ static_cast<bool>(PRIMARY));
injector.setSecure(static_cast<bool>(SECURE));
injector.setNativeWindow(test->mNativeWindow);
@@ -603,12 +622,11 @@
constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-template <hwc2_display_t hwcDisplayId, typename PhysicalDisplay, int width, int height,
- Critical critical>
+template <typename PhysicalDisplay, int width, int height, Critical critical>
struct PhysicalDisplayVariant
: DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
+ HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, HWC2::DisplayType::Physical,
DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
critical, Async::FALSE, Secure::TRUE,
PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
@@ -619,6 +637,7 @@
static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
static constexpr Primary PRIMARY = Primary::TRUE;
static constexpr uint8_t PORT = 255;
+ static constexpr hwc2_display_t HWC_DISPLAY_ID = 1001;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
@@ -628,6 +647,7 @@
static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 254;
+ static constexpr hwc2_display_t HWC_DISPLAY_ID = 1002;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
};
@@ -635,19 +655,19 @@
struct TertiaryDisplay {
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 253;
+ static constexpr hwc2_display_t HWC_DISPLAY_ID = 1003;
static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
};
// A primary display is a physical display that is critical
using PrimaryDisplayVariant =
- PhysicalDisplayVariant<1001, PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+ PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
// An external display is physical display that is not critical.
using ExternalDisplayVariant =
- PhysicalDisplayVariant<1002, ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+ PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
-using TertiaryDisplayVariant =
- PhysicalDisplayVariant<1003, TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -696,7 +716,7 @@
: DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
HwcDisplayVariant<
- 1010, HWC2::DisplayType::Virtual,
+ HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, HWC2::DisplayType::Virtual,
DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
@@ -1767,7 +1787,9 @@
if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
const auto displayId = Case::Display::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
- state.physical = {*displayId, *connectionType};
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ state.physical = {*displayId, *connectionType, *hwcDisplayId};
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1941,7 +1963,9 @@
if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
const auto displayId = Case::Display::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
- expectedPhysical = {*displayId, *connectionType};
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
}
// The display should have been set up in the current display state
@@ -2124,11 +2148,11 @@
ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectPrimaryDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectExternalDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 65b3e35..ba5c0c2 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 18e9941..7557faf 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -84,7 +84,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -113,7 +113,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -137,15 +137,15 @@
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = mTime;
EXPECT_EQ(3, layerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 37da76b..8559a5e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -91,7 +91,7 @@
TEST_F(LayerHistoryTestV2, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -122,7 +122,7 @@
TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -146,7 +146,7 @@
TEST_F(LayerHistoryTestV2, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -167,7 +167,7 @@
TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
@@ -194,7 +194,7 @@
TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
@@ -222,7 +222,7 @@
TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
@@ -250,7 +250,7 @@
TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate())
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(
Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
@@ -273,7 +273,8 @@
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_TRUE(history().summarize(time).empty());
- // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+ // value > 0
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -281,7 +282,7 @@
TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate())
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(Return(
Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
@@ -305,7 +306,8 @@
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_TRUE(history().summarize(time).empty());
- // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+ // value > 0
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -316,13 +318,13 @@
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = systemTime();
@@ -456,7 +458,7 @@
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = systemTime();
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
appEarlyGlDuration) {}
};
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : public testing::Test {
protected:
- PhaseOffsetsTest()
- : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
- 38'000'000) {}
+ PhaseDurationTest()
+ : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
- ~PhaseOffsetsTest() = default;
+ ~PhaseDurationTest() = default;
- TestablePhaseOffsetsAsDurations mPhaseOffsets;
+ TestablePhaseOffsetsAsDurations mPhaseDurations;
};
namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseOffsets.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseDurations.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseOffsets.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseDurations.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
validateOffsets(offsets);
}
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+ EXPECT_EQ(offsets.late.app, 37'027'208);
+
+ EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+ EXPECT_EQ(offsets.early.app, 18'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
new file mode 100644
index 0000000..b069085
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+
+class LayerFactory {
+public:
+ virtual ~LayerFactory() = default;
+
+ virtual std::string name() = 0;
+ virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferQueueLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "BufferQueueLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferQueueLayer(args);
+ }
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "BufferStateLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+ }
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "EffectLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+ LayerMetadata());
+ return new EffectLayer(args);
+ }
+};
+
+std::string PrintToStringParamName(
+ const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+ return info.param->name();
+}
+
+/**
+ * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
+ */
+class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(67.f, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 = FrameRate(14.f, FrameRateCompatibility::ExactOrMultiple);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(99.f, FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_TREE = FrameRate(0, FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_NO_VOTE = FrameRate(0, FrameRateCompatibility::Default);
+
+ SetFrameRateTest();
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+
+ void addChild(sp<Layer> layer, sp<Layer> child);
+ void removeChild(sp<Layer> layer, sp<Layer> child);
+ void reparentChildren(sp<Layer> layer, sp<Layer> child);
+ void commitTransaction();
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+
+ std::vector<sp<Layer>> mLayers;
+};
+
+SetFrameRateTest::SetFrameRateTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ mFlinger.mutableUseFrameRateApi() = true;
+
+ setupScheduler();
+ setupComposer(0);
+
+ mFlinger.mutableEventQueue().reset(mMessageQueue);
+}
+void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
+ layer.get()->addChild(child.get());
+}
+
+void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+ layer.get()->removeChild(child.get());
+}
+
+void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
+ parent.get()->reparentChildren(newParent);
+}
+
+void SetFrameRateTest::commitTransaction() {
+ for (auto layer : mLayers) {
+ layer.get()->commitTransaction(layer.get()->getCurrentState());
+ }
+}
+
+void SetFrameRateTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+ EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*primaryDispSync, getPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(primaryDispSync),
+ std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+ std::move(sfEventThread));
+}
+
+void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_P(SetFrameRateTest, SetAndGet) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ layer->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParent) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ child1->setFrameRate(FRAME_RATE_VOTE2);
+ parent->setFrameRate(FRAME_RATE_VOTE3);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChild) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ child1->setFrameRate(FRAME_RATE_VOTE2);
+ parent->setFrameRate(FRAME_RATE_VOTE3);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+ addChild(child1, child2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ removeChild(child1, child2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+ addChild(child1, child2_1);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ reparentChildren(parent, parent2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
+ testing::Values(std::make_shared<BufferQueueLayerFactory>(),
+ std::make_shared<BufferStateLayerFactory>(),
+ std::make_shared<EffectLayerFactory>()),
+ PrintToStringParamName);
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 84e55ae..058c5cc 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -394,6 +394,7 @@
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+ auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
auto fromHandle(const sp<IBinder>& handle) {
Mutex::Autolock _l(mFlinger->mStateLock);
@@ -548,9 +549,10 @@
FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
std::shared_ptr<compositionengine::Display> compositionDisplay,
std::optional<DisplayConnectionType> connectionType,
- bool isPrimary)
+ std::optional<hwc2_display_t> hwcDisplayId, bool isPrimary)
: mFlinger(flinger),
- mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay) {
+ mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay),
+ mHwcDisplayId(hwcDisplayId) {
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
}
@@ -618,7 +620,8 @@
DisplayDeviceState state;
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
- state.physical = {*displayId, *type};
+ LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
+ state.physical = {*displayId, *type, *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
@@ -639,6 +642,7 @@
TestableSurfaceFlinger& mFlinger;
sp<BBinder> mDisplayToken = new BBinder();
DisplayDeviceCreationArgs mCreationArgs;
+ const std::optional<hwc2_display_t> mHwcDisplayId;
};
surfaceflinger::test::Factory mFactory;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 45f7ed3..1f04673 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -152,9 +152,9 @@
struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
return mEvent;
}
- void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata*, void* cookie) override {
+ void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) override {
mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
@@ -164,7 +164,7 @@
return (*mCallback)(atom_tag, nullptr, cookie);
}
- MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+ MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
@@ -261,18 +261,18 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
+TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
mTimeStats->onBootFinished();
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
}
-TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
mTimeStats.reset();
}
@@ -1056,8 +1056,7 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
- // Now make sure that TimeStats flushes global stats to register the
- // callback.
+ // Now make sure that TimeStats flushes global stats to set the callback.
mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index caac61d..be49ef3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
nsecs_t const mPeriod;
@@ -85,6 +86,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 3ab38e4..3543361 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index f834af8..bf2a889 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -204,7 +204,7 @@
};
auto idealPeriod = 2000000;
auto expectedPeriod = 1999892;
- auto expectedIntercept = 175409;
+ auto expectedIntercept = 86342;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -335,8 +335,8 @@
158929706370359,
};
auto const idealPeriod = 11111111;
- auto const expectedPeriod = 11113500;
- auto const expectedIntercept = -395335;
+ auto const expectedPeriod = 11113919;
+ auto const expectedIntercept = -1195945;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -355,6 +355,32 @@
EXPECT_THAT(prediction, Ge(timePoint));
}
+// See b/151146131
+TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
+ VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
+ 840923581635, 840940161584, 840956868096,
+ 840973702473, 840990256277, 841007116851,
+ 841023722530, 841040452167, 841057073002,
+ 841073800920, 841090474360, 841107278632,
+ 841123898634, 841140750875, 841157287127,
+ 841591357014, 840856664232
+
+ };
+ auto const idealPeriod = 16666666;
+ auto const expectedPeriod = 16698426;
+ auto const expectedIntercept = 58055;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
auto const idealPeriod = 10000;
auto const realPeriod = 10500;
@@ -390,6 +416,22 @@
}
}
+constexpr nsecs_t operator""_years(unsigned long long years) noexcept {
+ using namespace std::chrono_literals;
+ return years * 365 * 24 * 3600 *
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+}
+TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) {
+ constexpr nsecs_t timeBase = 100_years;
+
+ for (auto i = 0; i < kHistorySize; i++) {
+ tracker.addVsyncTimestamp(timeBase + i * mPeriod);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(mPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, Eq(0));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ac95938..4f150ef 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncTrackerWrapper : public VSyncTracker {
@@ -56,6 +57,7 @@
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
void resetModel() final { mTracker->resetModel(); }
+ void dump(std::string& result) const final { mTracker->dump(result); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
@@ -83,6 +85,7 @@
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncDispatchWrapper : public VSyncDispatch {
@@ -102,6 +105,8 @@
CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+ void dump(std::string& result) const final { return mDispatch->dump(result); }
+
private:
std::shared_ptr<VSyncDispatch> const mDispatch;
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fe57c98..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index e2f6abd..119f580 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
- MOCK_CONST_METHOD0(getFrameRate, FrameRate());
+ MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
};
} // namespace android::mock
diff --git a/vulkan/TEST_MAPPING b/vulkan/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/vulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index aae72db..5b9affd 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -29,6 +29,8 @@
#include <algorithm>
#include <mutex>
#include <new>
+#include <string>
+#include <unordered_set>
#include <utility>
#include <android-base/strings.h>
@@ -1194,6 +1196,23 @@
return initialized;
}
+template <typename Functor>
+void ForEachLayerFromSettings(Functor functor) {
+ const std::string layersSetting =
+ android::GraphicsEnv::getInstance().getDebugLayers();
+ if (!layersSetting.empty()) {
+ std::vector<std::string> layers =
+ android::base::Split(layersSetting, ":");
+ for (uint32_t i = 0; i < layers.size(); i++) {
+ const Layer* layer = FindLayer(layers[i].c_str());
+ if (!layer) {
+ continue;
+ }
+ functor(layer);
+ }
+ }
+}
+
} // anonymous namespace
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
@@ -1280,9 +1299,56 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- return vulkan::driver::EnumerateInstanceExtensionProperties(
- nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all instance extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerInstanceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ uint32_t count = 0;
+ VkResult result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
@@ -1334,10 +1400,57 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- const InstanceData& data = GetData(physicalDevice);
- return data.dispatch.EnumerateDeviceExtensionProperties(
- physicalDevice, nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all device extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerDeviceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ const InstanceData& data = GetData(physicalDevice);
+ uint32_t count = 0;
+ VkResult result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {