Merge "Add support for multi-plane YUV HardwareBuffers"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 99f3739..a282424 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -107,6 +107,10 @@
chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable
chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable
chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable
+ chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
+ chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 9aa1075..ee32cb4 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -57,7 +57,6 @@
export_aidl_headers: true,
},
srcs: [
- "binder/android/os/DumpstateOptions.cpp",
":dumpstate_aidl",
],
export_include_dirs: ["binder"],
@@ -68,7 +67,6 @@
srcs: [
"binder/android/os/IDumpstateListener.aidl",
"binder/android/os/IDumpstateToken.aidl",
- //"binder/android/os/DumpstateOptions.aidl",
"binder/android/os/IDumpstate.aidl",
],
path: "binder",
@@ -99,8 +97,9 @@
"utils.cpp",
],
static_libs: [
+ "libincidentcompanion",
"libdumpsys",
- "libserviceutils"
+ "libserviceutils",
],
}
@@ -158,3 +157,22 @@
],
static_libs: ["libgmock"],
}
+
+
+// =======================#
+// dumpstate_test_fixture #
+// =======================#
+cc_test {
+
+ name: "dumpstate_test_fixture",
+ test_suites: ["device-tests"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+ srcs: ["tests/dumpstate_test_fixture.cpp"],
+ data: ["tests/testdata/**/*"],
+}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
deleted file mode 100644
index ea5fbf1..0000000
--- a/cmds/dumpstate/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# =======================#
-# dumpstate_test_fixture #
-# =======================#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := dumpstate_test_fixture
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SRC_FILES := \
- tests/dumpstate_test_fixture.cpp
-
-LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata)
-
-include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba9a967..bb089e6 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -18,8 +18,9 @@
#include "DumpstateService.h"
-#include <android-base/stringprintf.h>
+#include <memory>
+#include <android-base/stringprintf.h>
#include "android/os/BnDumpstate.h"
#include "DumpstateInternal.h"
@@ -31,6 +32,13 @@
namespace {
+struct DumpstateInfo {
+ public:
+ Dumpstate* ds = nullptr;
+ int32_t calling_uid = -1;
+ std::string calling_package;
+};
+
static binder::Status exception(uint32_t code, const std::string& msg) {
MYLOGE("%s (%d) ", msg.c_str(), code);
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
@@ -41,17 +49,19 @@
return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
}
+// Takes ownership of data.
static void* callAndNotify(void* data) {
- Dumpstate& ds = *static_cast<Dumpstate*>(data);
- ds.Run();
- MYLOGE("Finished Run()\n");
+ std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
+ ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
+ MYLOGD("Finished Run()\n");
return nullptr;
}
class DumpstateToken : public BnDumpstateToken {};
-}
-DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+} // namespace
+
+DumpstateService::DumpstateService() : ds_(nullptr) {
}
char const* DumpstateService::getServiceName() {
@@ -70,6 +80,8 @@
return android::OK;
}
+// Note: this method is part of the old flow and is not expected to be used in combination
+// with startBugreport.
binder::Status DumpstateService::setListener(const std::string& name,
const sp<IDumpstateListener>& listener,
bool getSectionDetails,
@@ -84,29 +96,28 @@
return binder::Status::ok();
}
std::lock_guard<std::mutex> lock(lock_);
- if (ds_.listener_ != nullptr) {
- MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+ if (ds_ == nullptr) {
+ ds_ = &(Dumpstate::GetInstance());
+ }
+ if (ds_->listener_ != nullptr) {
+ MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_->listener_name_.c_str());
return binder::Status::ok();
}
- ds_.listener_name_ = name;
- ds_.listener_ = listener;
- ds_.report_section_ = getSectionDetails;
+ ds_->listener_name_ = name;
+ ds_->listener_ = listener;
+ ds_->report_section_ = getSectionDetails;
*returned_token = new DumpstateToken();
return binder::Status::ok();
}
-// TODO(b/111441001): Hook up to consent service & copy final br only if user approves.
-binder::Status DumpstateService::startBugreport(int32_t /* calling_uid */,
- const std::string& /* calling_package */,
+binder::Status DumpstateService::startBugreport(int32_t calling_uid,
+ const std::string& calling_package,
const android::base::unique_fd& bugreport_fd,
const android::base::unique_fd& screenshot_fd,
int bugreport_mode,
const sp<IDumpstateListener>& listener) {
- // TODO(b/111441001):
- // 1. check DUMP permission (again)?
- // 2. check if primary user? If non primary user the consent service will reject anyway.
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
@@ -128,48 +139,73 @@
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
screenshot_fd);
+ // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
+ // time.
std::lock_guard<std::mutex> lock(lock_);
- // TODO(b/111441001): Disallow multiple simultaneous bugreports.
- ds_.SetOptions(std::move(options));
+ if (ds_ != nullptr) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC,
+ "There is already a bugreport in progress");
+ }
+ ds_ = &(Dumpstate::GetInstance());
+ ds_->SetOptions(std::move(options));
if (listener != nullptr) {
- ds_.listener_ = listener;
+ ds_->listener_ = listener;
}
+ DumpstateInfo* ds_info = new DumpstateInfo();
+ ds_info->ds = ds_;
+ ds_info->calling_uid = calling_uid;
+ ds_info->calling_package = calling_package;
+
pthread_t thread;
- status_t err = pthread_create(&thread, nullptr, callAndNotify, &ds_);
+ status_t err = pthread_create(&thread, nullptr, callAndNotify, ds_info);
if (err != 0) {
+ delete ds_info;
+ ds_info = nullptr;
return error(err, "Could not create a background thread.");
}
return binder::Status::ok();
}
+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.
+ return binder::Status::ok();
+}
+
status_t DumpstateService::dump(int fd, const Vector<String16>&) {
- std::string destination = ds_.options_->bugreport_fd.get() != -1
- ? StringPrintf("[fd:%d]", ds_.options_->bugreport_fd.get())
- : ds_.bugreport_internal_dir_.c_str();
- dprintf(fd, "id: %d\n", ds_.id_);
- dprintf(fd, "pid: %d\n", ds_.pid_);
- dprintf(fd, "update_progress: %s\n", ds_.options_->do_progress_updates ? "true" : "false");
- dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
- dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+ if (ds_ == nullptr) {
+ dprintf(fd, "Bugreport not in progress yet");
+ return NO_ERROR;
+ }
+ std::string destination = ds_->options_->bugreport_fd.get() != -1
+ ? StringPrintf("[fd:%d]", ds_->options_->bugreport_fd.get())
+ : ds_->bugreport_internal_dir_.c_str();
+ dprintf(fd, "id: %d\n", ds_->id_);
+ dprintf(fd, "pid: %d\n", ds_->pid_);
+ dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false");
+ dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_);
+ dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_);
dprintf(fd, "progress:\n");
- ds_.progress_->Dump(fd, " ");
- dprintf(fd, "args: %s\n", ds_.options_->args.c_str());
- dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str());
- dprintf(fd, "version: %s\n", ds_.version_.c_str());
+ ds_->progress_->Dump(fd, " ");
+ dprintf(fd, "args: %s\n", ds_->options_->args.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
+ dprintf(fd, "version: %s\n", ds_->version_.c_str());
dprintf(fd, "bugreport_dir: %s\n", destination.c_str());
- dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
- dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
- dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
- dprintf(fd, "path: %s\n", ds_.path_.c_str());
- dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str());
- dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
- dprintf(fd, "name: %s\n", ds_.name_.c_str());
- dprintf(fd, "now: %ld\n", ds_.now_);
- dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
- dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
- dprintf(fd, "notification title: %s\n", ds_.options_->notification_title.c_str());
- dprintf(fd, "notification description: %s\n", ds_.options_->notification_description.c_str());
+ dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str());
+ dprintf(fd, "log_path: %s\n", ds_->log_path_.c_str());
+ dprintf(fd, "tmp_path: %s\n", ds_->tmp_path_.c_str());
+ dprintf(fd, "path: %s\n", ds_->path_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
+ dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str());
+ dprintf(fd, "name: %s\n", ds_->name_.c_str());
+ dprintf(fd, "now: %ld\n", ds_->now_);
+ dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false");
+ dprintf(fd, "listener: %s\n", ds_->listener_name_.c_str());
+ dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str());
+ dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str());
return NO_ERROR;
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index b6ba32d..68eda47 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -47,8 +47,15 @@
const android::base::unique_fd& screenshot_fd, int bugreport_mode,
const sp<IDumpstateListener>& listener) override;
+ // No-op
+ binder::Status cancelBugreport();
+
private:
- Dumpstate& ds_;
+ // Dumpstate object which contains all the bugreporting logic.
+ // Note that dumpstate is a oneshot service, so this object is meant to be used at most for
+ // one bugreport.
+ // This service does not own this object.
+ Dumpstate* ds_;
std::mutex lock_;
};
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp b/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp
deleted file mode 100644
index 5654190..0000000
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/os/DumpstateOptions.h>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-
-namespace android {
-namespace os {
-
-status_t DumpstateOptions::readFromParcel(const ::android::Parcel* parcel) {
- if (status_t err = parcel->readBool(&get_section_details)) {
- return err;
- }
- if (status_t err = parcel->readUtf8FromUtf16(&name)) {
- return err;
- }
- return android::OK;
-}
-
-status_t DumpstateOptions::writeToParcel(::android::Parcel* parcel) const {
- if (status_t err = parcel->writeBool(get_section_details)) {
- return err;
- }
- if (status_t err = parcel->writeUtf8AsUtf16(name)) {
- return err;
- }
- return android::OK;
-}
-
-} // namespace os
-} // namespace android
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.h b/cmds/dumpstate/binder/android/os/DumpstateOptions.h
deleted file mode 100644
index a748e3c..0000000
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_OS_DUMPSTATE_OPTIONS_H_
-#define ANDROID_OS_DUMPSTATE_OPTIONS_H_
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace os {
-
-struct DumpstateOptions : public android::Parcelable {
- // If true the caller can get callbacks with per-section progress details.
- bool get_section_details = false;
-
- // Name of the caller.
- std::string name;
-
- status_t writeToParcel(android::Parcel* parcel) const override;
- status_t readFromParcel(const android::Parcel* parcel) override;
-};
-
-} // namespace os
-} // namespace android
-
-#endif // ANDROID_OS_DUMPSTATE_OPTIONS_H_
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 2635157..b1005d3 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -18,7 +18,6 @@
import android.os.IDumpstateListener;
import android.os.IDumpstateToken;
-import android.os.DumpstateOptions;
/**
* Binder interface for the currently running dumpstate process.
@@ -79,4 +78,9 @@
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
int bugreportMode, IDumpstateListener listener);
+
+ /*
+ * Cancels the bugreport currently in progress.
+ */
+ void cancelBugreport();
}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index bcd0cb7..907a67c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -38,20 +38,18 @@
/* User denied consent to share the bugreport with the specified app */
const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3;
+ /* The request to get user consent timed out */
+ const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+
/**
* Called on an error condition with one of the error codes listed above.
*/
oneway void onError(int errorCode);
/**
- * Called when taking bugreport finishes successfully
- *
- * @param durationMs time capturing bugreport took in milliseconds
- * @param title title for the bugreport; helpful in reminding the user why they took it
- * @param description detailed description for the bugreport
+ * Called when taking bugreport finishes successfully.
*/
- oneway void onFinished(long durationMs, @utf8InCpp String title,
- @utf8InCpp String description);
+ oneway void onFinished();
// TODO(b/111441001): Remove old methods when not used anymore.
void onProgressUpdated(int progress);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 4634ecd..db8848a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -51,6 +51,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/os/IIncidentCompanion.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <dumpsys.h>
@@ -83,15 +84,19 @@
using android::UNKNOWN_ERROR;
using android::Vector;
using android::base::StringPrintf;
+using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
using android::os::dumpstate::DumpstateSectionReporter;
using android::os::dumpstate::GetPidByName;
using android::os::dumpstate::PropertiesHelper;
+typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult;
+
/* read before root is shed */
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = nullptr;
+static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
// TODO: variables and functions below should be part of dumpstate object
@@ -165,6 +170,13 @@
return false;
}
+static bool UnlinkAndLogOnError(const std::string& file) {
+ if (unlink(file.c_str()) != -1) {
+ MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
+ return false;
+ }
+ return true;
+}
} // namespace
} // namespace os
@@ -657,6 +669,32 @@
return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}
+Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportApproved() {
+ std::lock_guard<std::mutex> lock(lock_);
+ result_ = APPROVED;
+ MYLOGD("User approved consent to share bugreport\n");
+ return android::binder::Status::ok();
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportDenied() {
+ std::lock_guard<std::mutex> lock(lock_);
+ result_ = DENIED;
+ MYLOGW("User denied consent to share bugreport\n");
+ return android::binder::Status::ok();
+}
+
+UserConsentResult Dumpstate::ConsentCallback::getResult() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return result_;
+}
+
+uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
+ return Nanotime() - start_time_;
+}
+
void Dumpstate::PrintHeader() const {
std::string build, fingerprint, radio, bootloader, network;
char date[80];
@@ -943,6 +981,8 @@
AddAnrTraceDir(add_to_zip, anr_traces_dir);
+ RunCommand("ANR FILES", {"ls", "-lt", ANR_DIR});
+
// Slow traces for slow operations.
struct stat st;
int i = 0;
@@ -1660,6 +1700,7 @@
"progress (requires -o and -B)\n"
" -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
"shouldn't be used with -P)\n"
+ " -w: start binder service and make it wait for a call to startBugreport\n"
" -v: prints the dumpstate header and exit\n");
}
@@ -1891,14 +1932,6 @@
ds.path_ = new_path;
}
}
- // The zip file lives in an internal directory. Copy it over to output.
- if (ds.options_->bugreport_fd.get() != -1) {
- bool copy_succeeded =
- android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get());
- if (!copy_succeeded && remove(ds.path_.c_str())) {
- MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
- }
- } // else - the file just remains in the internal directory.
}
}
if (do_text_file) {
@@ -2118,7 +2151,7 @@
Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
RunStatus status = RunStatus::OK;
int c;
- while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) {
switch (c) {
// clang-format off
case 'd': do_add_date = true; break;
@@ -2135,6 +2168,9 @@
case 'R': is_remote_mode = true; break;
case 'B': do_broadcast = true; break;
case 'V': break; // compatibility no-op
+ case 'w':
+ // This was already processed
+ break;
case 'h':
status = RunStatus::HELP;
break;
@@ -2188,22 +2224,26 @@
options_ = std::move(options);
}
-Dumpstate::RunStatus Dumpstate::Run() {
- Dumpstate::RunStatus status = RunInternal();
+Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
+ Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
if (listener_ != nullptr) {
switch (status) {
case Dumpstate::RunStatus::OK:
- // TODO(b/111441001): duration argument does not make sense. Remove.
- listener_->onFinished(0 /* duration */, options_->notification_title,
- options_->notification_description);
+ listener_->onFinished();
break;
case Dumpstate::RunStatus::HELP:
break;
case Dumpstate::RunStatus::INVALID_INPUT:
- listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
break;
case Dumpstate::RunStatus::ERROR:
- listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+ break;
+ case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);
+ break;
+ case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
break;
}
}
@@ -2231,7 +2271,8 @@
* Bugreports are first generated in a local directory and later copied to the caller's fd if
* supplied.
*/
-Dumpstate::RunStatus Dumpstate::RunInternal() {
+Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
+ const std::string& calling_package) {
LogDumpOptions(*options_);
if (!options_->ValidateOptions()) {
MYLOGE("Invalid options specified\n");
@@ -2269,6 +2310,12 @@
return RunStatus::OK;
}
+ if (options_->bugreport_fd.get() != -1) {
+ // If the output needs to be copied over to the caller's fd, get user consent.
+ android::String16 package(calling_package.c_str());
+ CheckUserConsent(calling_uid, package);
+ }
+
// Redirect output if needed
bool is_redirecting = options_->OutputToFile();
@@ -2420,11 +2467,31 @@
TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
}
- /* rename or zip the (now complete) .tmp file to its final location */
+ // Rename, and/or zip the (now complete) .tmp file within the internal directory.
if (options_->OutputToFile()) {
FinalizeFile();
}
+ // Share the final file with the caller if the user has consented.
+ Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
+ if (options_->bugreport_fd.get() != -1) {
+ status = CopyBugreportIfUserConsented();
+ if (status != Dumpstate::RunStatus::OK &&
+ status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+ // Do an early return if there were errors. We make an exception for consent
+ // timing out because it's possible the user got distracted. In this case the
+ // bugreport is not shared but made available for manual retrieval.
+ return status;
+ }
+ if (options_->screenshot_fd.get() != -1) {
+ bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
+ options_->screenshot_fd.get());
+ if (copy_succeeded) {
+ android::os::UnlinkAndLogOnError(screenshot_path_);
+ }
+ }
+ }
+
/* vibrate a few but shortly times to let user know it's finished */
if (options_->do_vibrate) {
for (int i = 0; i < 3; i++) {
@@ -2456,7 +2523,73 @@
tombstone_data_.clear();
anr_data_.clear();
- return RunStatus::OK;
+ return (consent_callback_ != nullptr &&
+ consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
+ ? USER_CONSENT_TIMED_OUT
+ : RunStatus::OK;
+}
+
+void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
+ consent_callback_ = new ConsentCallback();
+ const String16 incidentcompanion("incidentcompanion");
+ sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+ if (ics != nullptr) {
+ MYLOGD("Checking user consent via incidentcompanion service\n");
+ android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+ calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
+ consent_callback_.get());
+ } else {
+ MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
+ }
+}
+
+void Dumpstate::CleanupFiles() {
+ android::os::UnlinkAndLogOnError(tmp_path_);
+ android::os::UnlinkAndLogOnError(screenshot_path_);
+ android::os::UnlinkAndLogOnError(path_);
+}
+
+Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
+ MYLOGD("User denied consent; deleting files and returning\n");
+ CleanupFiles();
+ return USER_CONSENT_DENIED;
+}
+
+Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() {
+ // If the caller has asked to copy the bugreport over to their directory, we need explicit
+ // user consent.
+ UserConsentResult consent_result = consent_callback_->getResult();
+ if (consent_result == UserConsentResult::UNAVAILABLE) {
+ // User has not responded yet.
+ uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
+ if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
+ uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+ MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
+ sleep(delay_seconds);
+ }
+ consent_result = consent_callback_->getResult();
+ }
+ if (consent_result == UserConsentResult::DENIED) {
+ // User has explicitly denied sharing with the app. To be safe delete the
+ // internal bugreport & tmp files.
+ return HandleUserConsentDenied();
+ }
+ if (consent_result == UserConsentResult::APPROVED) {
+ bool copy_succeeded = android::os::CopyFileToFd(path_, options_->bugreport_fd.get());
+ if (copy_succeeded) {
+ android::os::UnlinkAndLogOnError(path_);
+ }
+ return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
+ } else if (consent_result == UserConsentResult::UNAVAILABLE) {
+ // consent_result is still UNAVAILABLE. The user has likely not responded yet.
+ // Since we do not have user consent to share the bugreport it does not get
+ // copied over to the calling app but remains in the internal directory from
+ // where the user can manually pull it.
+ return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
+ }
+ // Unknown result; must be a programming error.
+ MYLOGE("Unknown user consent result:%d\n", consent_result);
+ return Dumpstate::RunStatus::ERROR;
}
/* Main entry point for dumpstate binary. */
@@ -2465,7 +2598,14 @@
Dumpstate::RunStatus status = options->Initialize(argc, argv);
if (status == Dumpstate::RunStatus::OK) {
ds.SetOptions(std::move(options));
- status = ds.Run();
+ // When directly running dumpstate binary, the output is not expected to be written
+ // to any external file descriptor.
+ assert(ds.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.
+ status = ds.Run(-1 /* calling_uid */, "" /* calling_package */);
}
switch (status) {
@@ -2479,9 +2619,10 @@
ShowUsage();
exit(1);
case Dumpstate::RunStatus::ERROR:
- exit(2);
- default:
- fprintf(stderr, "Unknown status: %d\n", status);
+ FALLTHROUGH_INTENDED;
+ case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+ FALLTHROUGH_INTENDED;
+ case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
exit(2);
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3dfe4e9..7fb2f3b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
#include <android-base/macros.h>
#include <android-base/unique_fd.h>
+#include <android/os/BnIncidentAuthListener.h>
#include <android/os/IDumpstate.h>
#include <android/os/IDumpstateListener.h>
#include <utils/StrongPointer.h>
@@ -192,7 +193,7 @@
friend class DumpstateTest;
public:
- enum RunStatus { OK, HELP, INVALID_INPUT, ERROR };
+ enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
// The mode under which the bugreport should be run. Each mode encapsulates a few options.
enum BugreportMode {
@@ -319,7 +320,7 @@
struct DumpOptions;
/* Main entry point for running a complete bugreport. */
- RunStatus Run();
+ RunStatus Run(int32_t calling_uid, const std::string& calling_package);
/* Sets runtime options. */
void SetOptions(std::unique_ptr<DumpOptions> options);
@@ -346,7 +347,6 @@
// File descriptor to output zip file.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
- // TODO(b/111441001): Use this fd.
android::base::unique_fd screenshot_fd;
// TODO: rename to MODE.
// Extra options passed as system property.
@@ -447,12 +447,47 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A callback to IncidentCompanion service, which checks user consent for sharing the
+ // bugreport with the calling app. If the user has not responded yet to the dialog it will
+ // be neither confirmed nor denied.
+ class ConsentCallback : public android::os::BnIncidentAuthListener {
+ public:
+ ConsentCallback();
+ android::binder::Status onReportApproved() override;
+ android::binder::Status onReportDenied() override;
+
+ enum ConsentResult { APPROVED, DENIED, UNAVAILABLE };
+
+ ConsentResult getResult();
+
+ // Returns the time since creating this listener
+ uint64_t getElapsedTimeMs() const;
+
+ private:
+ ConsentResult result_;
+ uint64_t start_time_;
+ std::mutex lock_;
+ };
+
private:
- RunStatus RunInternal();
+ RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
+
+ void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+
+ // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
+ // but leaves the log file alone.
+ void CleanupFiles();
+
+ RunStatus HandleUserConsentDenied();
+
+ // Copies bugreport artifacts over to the caller's directories provided there is user consent.
+ RunStatus CopyBugreportIfUserConsented();
// Used by GetInstance() only.
explicit Dumpstate(const std::string& version = VERSION_CURRENT);
+ android::sp<ConsentCallback> consent_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 2e72574..14937b8 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,3 +17,9 @@
class main
disabled
oneshot
+
+# bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
+service bugreportd /system/bin/dumpstate -w
+ class main
+ disabled
+ oneshot
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index 78aad11..68d3733 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -14,8 +14,55 @@
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
+
+#include <binder/IPCThreadState.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
#include "dumpstate.h"
+namespace {
+
+// Returns true if we should start the service and wait for a listener
+// to bind with bugreport options.
+bool ShouldStartServiceAndWait(int argc, char* argv[]) {
+ bool do_wait = false;
+ int c;
+ // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
+ while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) {
+ switch (c) {
+ case 'w':
+ do_wait = true;
+ break;
+ default:
+ // Ignore all other options
+ break;
+ }
+ }
+
+ // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to
+ // parse bugreport options.
+ optind = 1;
+ return do_wait;
+}
+
+} // namespace
+
int main(int argc, char* argv[]) {
- return run_main(argc, argv);
+ if (ShouldStartServiceAndWait(argc, argv)) {
+ int ret;
+ if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+ MYLOGE("Unable to start 'dumpstate' service: %d", ret);
+ exit(1);
+ }
+ MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+
+ // Waits forever for an incoming connection.
+ // TODO(b/111441001): should this time out?
+ android::IPCThreadState::self()->joinThreadPool();
+ return 0;
+ } else {
+ return run_main(argc, argv);
+ }
}
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c57775f..570c6c9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -61,9 +61,8 @@
dprintf(outFd_, "\rError %d", error_code);
return binder::Status::ok();
}
- binder::Status onFinished(int64_t duration_ms, const ::std::string&,
- const ::std::string&) override {
- dprintf(outFd_, "\rFinished in %lld", (long long) duration_ms);
+ binder::Status onFinished() override {
+ dprintf(outFd_, "\rFinished");
return binder::Status::ok();
}
binder::Status onProgressUpdated(int32_t progress) override {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 0302c46..eb73d41 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -61,8 +61,7 @@
public:
MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
MOCK_METHOD1(onError, binder::Status(int32_t error_code));
- MOCK_METHOD3(onFinished, binder::Status(int64_t duration_ms, const ::std::string& title,
- const ::std::string& description));
+ MOCK_METHOD0(onFinished, binder::Status());
MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status,
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
index f68b862..f99588f 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -20,8 +20,6 @@
static_libs: [
"libserviceutils",
],
-
- clang: true,
}
//
@@ -36,7 +34,6 @@
export_include_dirs: ["."],
}
-
//
// Executable
//
@@ -51,4 +48,15 @@
],
}
-subdirs = ["tests"]
+cc_binary {
+ name: "dumpsys_vendor",
+ stem: "dumpsys",
+
+ vendor: true,
+
+ defaults: ["dumpsys_defaults"],
+
+ srcs: [
+ "main.cpp",
+ ],
+}
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 62d2fa1..d398559 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -222,9 +222,9 @@
}
bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
- sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0);
+ const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken();
if (dpy == nullptr) {
- fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n");
+ fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n");
return false;
}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index a6f77aa..e33b2a8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
"dexopt.cpp",
"globals.cpp",
"utils.cpp",
+ "utils_default.cpp",
"view_compiler.cpp",
":installd_aidl",
],
@@ -138,10 +139,23 @@
],
clang: true,
- srcs: ["otapreopt_chroot.cpp"],
+ srcs: [
+ "otapreopt_chroot.cpp",
+ "otapreopt_utils.cpp",
+ ],
shared_libs: [
"libbase",
"liblog",
+ "libprotobuf-cpp-full",
+ "libselinux",
+ "libziparchive",
+ ],
+ static_libs: [
+ "libapex",
+ "libapexd",
+ "lib_apex_manifest_proto",
+ "libavb",
+ "libdm",
],
}
@@ -190,7 +204,9 @@
"dexopt.cpp",
"globals.cpp",
"otapreopt.cpp",
+ "otapreopt_utils.cpp",
"utils.cpp",
+ "utils_default.cpp",
"view_compiler.cpp",
],
@@ -213,3 +229,23 @@
"libutils",
],
}
+
+// OTA slot script
+sh_binary {
+ name: "otapreopt_slot",
+ src: "otapreopt_slot.sh",
+ init_rc: ["otapreopt.rc"],
+}
+
+// OTA postinstall script
+sh_binary {
+ name: "otapreopt_script",
+ src: "otapreopt_script.sh",
+ // Let this depend on otapreopt, the chroot tool and the slot script,
+ // so we just have to mention one in a configuration.
+ required: [
+ "otapreopt",
+ "otapreopt_chroot",
+ "otapreopt_slot",
+ ],
+}
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
deleted file mode 100644
index 30de0b3..0000000
--- a/cmds/installd/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# OTA slot script
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= otapreopt_slot
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := otapreopt_slot.sh
-LOCAL_INIT_RC := otapreopt.rc
-
-include $(BUILD_PREBUILT)
-
-# OTA postinstall script
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= otapreopt_script
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := otapreopt_script.sh
-
-# Let this depend on otapreopt, the chroot tool and the slot script, so we just have to mention one
-# in a configuration.
-LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot
-
-include $(BUILD_PREBUILT)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 51f30da..49383e5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,6 +77,9 @@
namespace android {
namespace installd {
+// An uuid used in unit tests.
+static constexpr const char* kTestUuid = "TEST";
+
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
@@ -155,6 +158,15 @@
}
}
+binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
+ if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
+ return ok();
+ } else {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str()));
+ }
+}
+
binder::Status checkArgumentPackageName(const std::string& packageName) {
if (is_valid_package_name(packageName.c_str())) {
return ok();
@@ -207,6 +219,13 @@
} \
}
+#define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) { \
+ auto status = checkArgumentUuidTestOrNull(uuid); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+} \
+
#define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \
binder::Status status = \
checkArgumentPackageName((packageName)); \
@@ -801,23 +820,12 @@
return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
}
-// TODO(narayan): We should pass through the ceDataInode so that we can call
-// clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence
-// the copy.
-//
-// TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we
-// should validate that volumeUuid is either nullptr or TEST, we won't support
-// anything else.
-//
-// TODO(narayan): We need to be clearer about the expected behaviour for the
-// case where a snapshot already exists. We either need to clear the contents
-// of the snapshot directory before we make a copy, or we need to ensure that
-// the caller always clears it before requesting a snapshot.
binder::Status InstalldNativeService::snapshotAppData(
const std::unique_ptr<std::string>& volumeUuid,
- const std::string& packageName, int32_t user, int32_t storageFlags) {
+ const std::string& packageName, int32_t user, int32_t storageFlags,
+ int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
- CHECK_ARGUMENT_UUID(volumeUuid);
+ CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -825,6 +833,10 @@
const char* package_name = packageName.c_str();
binder::Status res = ok();
+ // Default result to 0, it will be populated with inode of ce data snapshot
+ // if FLAG_STORAGE_CE has been passed.
+ if (_aidl_return != nullptr) *_aidl_return = 0;
+
bool clear_ce_on_exit = false;
bool clear_de_on_exit = false;
@@ -853,10 +865,34 @@
return ok();
}
+ // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set.
+ binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user,
+ storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
+ if (!clear_cache_result.isOk()) {
+ // It should be fine to continue snapshot if we for some reason failed
+ // to clear cache.
+ LOG(WARNING) << "Failed to clear cache of app " << packageName;
+ }
+
+ // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set.
+ binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user,
+ storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0);
+ if (!clear_code_cache_result.isOk()) {
+ // It should be fine to continue snapshot if we for some reason failed
+ // to clear code_cache.
+ LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
+ }
+
if (storageFlags & FLAG_STORAGE_DE) {
auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
auto to = create_data_misc_de_rollback_path(volume_uuid, user);
+ int rd = delete_dir_contents(to, true /* ignore_if_missing */);
+ if (rd != 0) {
+ res = error(rd, "Failed clearing existing snapshot " + to);
+ return res;
+ }
+
int rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
res = error(rc, "Failed copying " + from + " to " + to);
@@ -868,12 +904,29 @@
if (storageFlags & FLAG_STORAGE_CE) {
auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
auto to = create_data_misc_ce_rollback_path(volume_uuid, user);
+
+ int rd = delete_dir_contents(to, true /* ignore_if_missing */);
+ if (rd != 0) {
+ res = error(rd, "Failed clearing existing snapshot " + to);
+ return res;
+ }
+
int rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
res = error(rc, "Failed copying " + from + " to " + to);
clear_ce_on_exit = true;
return res;
}
+ if (_aidl_return != nullptr) {
+ auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
+ package_name);
+ rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
+ if (rc != 0) {
+ res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
+ clear_ce_on_exit = true;
+ return res;
+ }
+ }
}
return res;
@@ -884,7 +937,7 @@
const int32_t appId, const int64_t ceDataInode, const std::string& seInfo,
const int32_t user, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
- CHECK_ARGUMENT_UUID(volumeUuid);
+ CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -929,9 +982,13 @@
auto to_de = create_data_user_de_path(volume_uuid, user);
int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str());
if (rc != 0) {
- // TODO(narayan): Should we clear clear the rolled back CE data if
- // something goes wrong here ? We're choosing between leaving the
- // app devoid of all its data or with just its ce data installed.
+ if (needs_ce_rollback) {
+ auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name);
+ LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data;
+ if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) {
+ LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data;
+ }
+ }
res = error(rc, "Failed copying " + from_de + " to " + to_de);
return res;
}
@@ -941,6 +998,39 @@
return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
}
+binder::Status InstalldNativeService::destroyAppDataSnapshot(
+ const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
+ const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+ const char* package_name = packageName.c_str();
+
+ if (storageFlags & FLAG_STORAGE_DE) {
+ auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
+ user, package_name);
+
+ int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
+ if (res != 0) {
+ return error(res, "Failed clearing snapshot " + de_snapshot_path);
+ }
+ }
+
+ if (storageFlags & FLAG_STORAGE_CE) {
+ auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
+ user, package_name, ceSnapshotInode);
+ int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
+ if (res != 0) {
+ return error(res, "Failed clearing snapshot " + ce_snapshot_path);
+ }
+ }
+ return ok();
+}
+
+
binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
const std::string& dataAppName, int32_t appId, const std::string& seInfo,
@@ -2056,8 +2146,14 @@
return error("Failed to stat " + _pkgdir);
}
+ char *con = nullptr;
+ if (lgetfilecon(pkgdir, &con) < 0) {
+ return error("Failed to lgetfilecon " + _pkgdir);
+ }
+
if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
- return error("Failed to chown " + _pkgdir);
+ res = error("Failed to chown " + _pkgdir);
+ goto out;
}
if (chmod(pkgdir, 0700) < 0) {
@@ -2089,7 +2185,13 @@
goto out;
}
+ if (lsetfilecon(libsymlink, con) < 0) {
+ res = error("Failed to lsetfilecon " + _libsymlink);
+ goto out;
+ }
+
out:
+ free(con);
if (chmod(pkgdir, s.st_mode) < 0) {
auto msg = "Failed to cleanup chmod " + _pkgdir;
if (res.isOk()) {
@@ -2377,7 +2479,7 @@
if (validate_apk_path(packageDir.c_str())) {
return error("Invalid path " + packageDir);
}
- if (delete_dir_contents_and_dir(packageDir) != 0) {
+ if (rm_package_dir(packageDir) != 0) {
return error("Failed to delete " + packageDir);
}
return ok();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 098a0c2..578132d 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -61,10 +61,14 @@
binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
- const std::string& packageName, const int32_t user, int32_t storageFlags);
+ const std::string& packageName, const int32_t user, int32_t storageFlags,
+ int64_t* _aidl_return);
binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
const std::string& packageName, const int32_t appId, const int64_t ceDataInode,
const std::string& seInfo, const int32_t user, int32_t storageFlags);
+ binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
+ const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
+ int32_t storageFlags);
binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 3d093b8..b345210 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -105,14 +105,12 @@
int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
@nullable @utf8InCpp String dexMetadata);
- void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+ long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
int userId, int storageFlags);
void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags);
-
- // TODO(narayan) we need an API to delete the app data snapshot as well.
- // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid,
- // in @utf8InCpp String packageName, int userId, int storageFlags);
+ void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+ int userId, long ceSnapshotInode, int storageFlags);
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index e3a35c7..852aa79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -314,9 +314,15 @@
bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
vold_decrypt == "1";
- const std::string resolve_startup_string_arg =
+ std::string resolve_startup_string_arg =
+ MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
+ "--resolve-startup-const-strings=%s");
+ if (resolve_startup_string_arg.empty()) {
+ // If empty, fall back to system property.
+ resolve_startup_string_arg =
MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
"--resolve-startup-const-strings=%s");
+ }
const std::string image_block_size_arg =
MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
@@ -333,8 +339,7 @@
MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
// If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
- const char* dex2oat_bin = "/system/bin/dex2oat";
- constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
+ const char* dex2oat_bin = kDex2oatPath;
// Do not use dex2oatd for release candidates (give dex2oat more soak time).
bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
if (is_debug_runtime() ||
@@ -642,8 +647,7 @@
const std::vector<std::string>& dex_locations,
bool copy_and_update,
bool store_aggregation_counters) {
- const char* profman_bin =
- is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
+ const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath;
if (copy_and_update) {
CHECK_EQ(1u, profile_fds.size());
@@ -1459,9 +1463,7 @@
const char* class_loader_context) {
CHECK_GE(zip_fd, 0);
const char* dexoptanalyzer_bin =
- is_debug_runtime()
- ? "/system/bin/dexoptanalyzerd"
- : "/system/bin/dexoptanalyzer";
+ is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath;
std::string dex_file_arg = "--dex-file=" + dex_file;
std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
@@ -1956,6 +1958,11 @@
/* child -- drop privileges before continuing */
drop_capabilities(uid);
+ // Clear BOOTCLASSPATH.
+ // Let dex2oat use the BCP from boot image, excluding updatable BCP
+ // modules for AOT to avoid app recompilation after their upgrades.
+ unsetenv("BOOTCLASSPATH");
+
SetDex2OatScheduling(boot_complete);
if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 0db11e1..5902659 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -32,6 +32,16 @@
static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2;
static constexpr int DEX2OAT_FOR_FILTER = 3;
+#define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin"
+// Location of binaries in the Android Runtime APEX.
+static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat";
+static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd";
+static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer";
+static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd";
+#undef ANDROID_RUNTIME_APEX_BIN
+
// Clear the reference profile identified by the given profile name.
bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name);
// Clear the current profile identified by the given profile name (for single user).
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index b2e7047..de7b249 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,7 +26,6 @@
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
-#include <sys/wait.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -58,7 +57,6 @@
#define REPLY_MAX 256 /* largest reply allowed */
using android::base::EndsWith;
-using android::base::Join;
using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
@@ -440,7 +438,7 @@
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("/system/bin/dex2oat");
+ 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()));
@@ -619,61 +617,6 @@
// Helpers, mostly taken from ART //
////////////////////////////////////
- // Wrapper on fork/execv to run a command in a subprocess.
- static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
- const std::string command_line = Join(arg_vector, ' ');
-
- CHECK_GE(arg_vector.size(), 1U) << command_line;
-
- // Convert the args to char pointers.
- const char* program = arg_vector[0].c_str();
- std::vector<char*> args;
- for (size_t i = 0; i < arg_vector.size(); ++i) {
- const std::string& arg = arg_vector[i];
- char* arg_str = const_cast<char*>(arg.c_str());
- CHECK(arg_str != nullptr) << i;
- args.push_back(arg_str);
- }
- args.push_back(nullptr);
-
- // Fork and exec.
- pid_t pid = fork();
- if (pid == 0) {
- // No allocation allowed between fork and exec.
-
- // Change process groups, so we don't get reaped by ProcessManager.
- setpgid(0, 0);
-
- execv(program, &args[0]);
-
- PLOG(ERROR) << "Failed to execv(" << command_line << ")";
- // _exit to avoid atexit handlers in child.
- _exit(1);
- } else {
- if (pid == -1) {
- *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
- command_line.c_str(), strerror(errno));
- return false;
- }
-
- // wait for subprocess to finish
- int status;
- pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (got_pid != pid) {
- *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
- "wanted %d, got %d: %s",
- command_line.c_str(), pid, got_pid, strerror(errno));
- return false;
- }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
- command_line.c_str());
- return false;
- }
- }
- return true;
- }
-
// Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
constexpr size_t kPageSize = PAGE_SIZE;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index e90cf3b..670abea 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <linux/unistd.h>
#include <sys/mount.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <sstream>
@@ -24,6 +25,9 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <selinux/android.h>
+
+#include <apexd.h>
#include "installd_constants.h"
#include "otapreopt_utils.h"
@@ -37,6 +41,23 @@
namespace android {
namespace installd {
+// Configuration for bind-mounted Bionic artifacts.
+
+static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker";
+static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
+
+static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/";
+static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
+
+static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64";
+static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
+
+static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/";
+static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
+
+static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
+
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -54,6 +75,62 @@
}
}
+static std::vector<apex::ApexFile> ActivateApexPackages() {
+ // The logic here is (partially) copied and adapted from
+ // system/apex/apexd/apexd_main.cpp.
+ //
+ // Only scan the APEX directory under /system (within the chroot dir).
+ apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir);
+ return apex::getActivePackages();
+}
+
+static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
+ for (const apex::ApexFile& apex_file : active_packages) {
+ const std::string& package_path = apex_file.GetPath();
+ apex::Status status = apex::deactivatePackage(package_path);
+ if (!status.Ok()) {
+ LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage();
+ }
+ }
+}
+
+// Copied from system/core/init/mount_namespace.cpp.
+static bool BindMount(const std::string& source, const std::string& mount_point,
+ bool recursive = false) {
+ unsigned long mountflags = MS_BIND;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point;
+ return false;
+ }
+ return true;
+}
+
+// Copied from system/core/init/mount_namespace.cpp and and adjusted (bind
+// mounts are not made private, as the /postinstall is already private (see
+// `android::installd::otapreopt_chroot`).
+static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source,
+ const std::string& linker_mount_point,
+ const std::string& lib_mount_dir) {
+ if (access(linker_source.c_str(), F_OK) != 0) {
+ PLOG(INFO) << linker_source << " does not exist. Skipping mounting Bionic there.";
+ return true;
+ }
+ if (!BindMount(linker_source, linker_mount_point)) {
+ return false;
+ }
+ for (const auto& libname : kBionicLibFileNames) {
+ std::string mount_point = lib_mount_dir + libname;
+ std::string source = lib_dir_source + libname;
+ if (!BindMount(source, mount_point)) {
+ return false;
+ }
+ }
+ return true;
+}
+
// Entry for otapreopt_chroot. Expected parameters are:
// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
@@ -138,6 +215,44 @@
UNUSED(product_result);
}
+ // Setup APEX mount point and its security context.
+ static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
+ // The following logic is similar to the one in system/core/rootdir/init.rc:
+ //
+ // mount tmpfs tmpfs /apex nodev noexec nosuid
+ // chmod 0755 /apex
+ // chown root root /apex
+ // restorecon /apex
+ //
+ // except we perform the `restorecon` step just after mounting the tmpfs
+ // filesystem in /postinstall/apex, so that this directory is correctly
+ // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in
+ // following operations (`chmod`, `chown`, etc.) following policies
+ // restricted to `postinstall_apex_mnt_dir`:
+ //
+ // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid
+ // restorecon /postinstall/apex
+ // chmod 0755 /postinstall/apex
+ // chown root root /postinstall/apex
+ //
+ if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
+ != 0) {
+ PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
+ exit(209);
+ }
+ if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
+ PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
+ exit(214);
+ }
+ if (chmod(kPostinstallApexDir, 0755) != 0) {
+ PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755";
+ exit(210);
+ }
+ if (chown(kPostinstallApexDir, 0, 0) != 0) {
+ PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
+ exit(211);
+ }
+
// Chdir into /postinstall.
if (chdir("/postinstall") != 0) {
PLOG(ERROR) << "Unable to chdir into /postinstall.";
@@ -155,22 +270,55 @@
exit(205);
}
- // Now go on and run otapreopt.
+ // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
+ // the Android Runtime APEX, as it is required by otapreopt to run dex2oat.
+ std::vector<apex::ApexFile> active_packages = ActivateApexPackages();
- // Incoming: cmd + status-fd + target-slot + cmd... + null | Incoming | = argc + 1
- // Outgoing: cmd + target-slot + cmd... + null | Outgoing | = argc
- const char** argv = new const char*[argc];
-
- argv[0] = "/system/bin/otapreopt";
-
- // The first parameter is the status file descriptor, skip.
- for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) {
- argv[i - 1] = arg[i];
+ // Bind-mount Bionic artifacts from the Runtime APEX.
+ // This logic is copied and adapted from system/core/init/mount_namespace.cpp.
+ if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
+ kBionicLibsMountPointDir)) {
+ LOG(ERROR) << "Failed to mount 32-bit Bionic artifacts from the Runtime APEX.";
+ // Clean up and exit.
+ DeactivateApexPackages(active_packages);
+ exit(215);
+ }
+ if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64,
+ kBionicLibsMountPointDir64)) {
+ LOG(ERROR) << "Failed to mount 64-bit Bionic artifacts from the Runtime APEX.";
+ // Clean up and exit.
+ DeactivateApexPackages(active_packages);
+ exit(216);
}
- execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv)));
- PLOG(ERROR) << "execv(OTAPREOPT) failed.";
- exit(99);
+ // Now go on and run otapreopt.
+
+ // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc
+ // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1
+ std::vector<std::string> cmd;
+ cmd.reserve(argc);
+ cmd.push_back("/system/bin/otapreopt");
+
+ // The first parameter is the status file descriptor, skip.
+ for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
+ cmd.push_back(arg[i]);
+ }
+
+ // Fork and execute otapreopt in its own process.
+ std::string error_msg;
+ bool exec_result = Exec(cmd, &error_msg);
+ if (!exec_result) {
+ LOG(ERROR) << "Running otapreopt failed: " << error_msg;
+ }
+
+ // Tear down the work down by the apexd logic. (i.e. deactivate packages).
+ DeactivateApexPackages(active_packages);
+
+ if (!exec_result) {
+ exit(213);
+ }
+
+ return 0;
}
} // namespace installd
diff --git a/cmds/installd/otapreopt_utils.cpp b/cmds/installd/otapreopt_utils.cpp
new file mode 100644
index 0000000..124f726
--- /dev/null
+++ b/cmds/installd/otapreopt_utils.cpp
@@ -0,0 +1,88 @@
+/*
+ ** Copyright 2019, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "otapreopt_utils.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
+ const std::string command_line = Join(arg_vector, ' ');
+
+ CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+ // Convert the args to char pointers.
+ const char* program = arg_vector[0].c_str();
+ std::vector<char*> args;
+ for (size_t i = 0; i < arg_vector.size(); ++i) {
+ const std::string& arg = arg_vector[i];
+ char* arg_str = const_cast<char*>(arg.c_str());
+ CHECK(arg_str != nullptr) << i;
+ args.push_back(arg_str);
+ }
+ args.push_back(nullptr);
+
+ // Fork and exec.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // No allocation allowed between fork and exec.
+
+ // Change process groups, so we don't get reaped by ProcessManager.
+ setpgid(0, 0);
+
+ execv(program, &args[0]);
+
+ PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+ // _exit to avoid atexit handlers in child.
+ _exit(1);
+ } else {
+ if (pid == -1) {
+ *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+ command_line.c_str(), strerror(errno));
+ return false;
+ }
+
+ // wait for subprocess to finish
+ int status;
+ pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+ if (got_pid != pid) {
+ *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+ "wanted %d, got %d: %s",
+ command_line.c_str(), pid, got_pid, strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+ command_line.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h
index 436e554..03a6d87 100644
--- a/cmds/installd/otapreopt_utils.h
+++ b/cmds/installd/otapreopt_utils.h
@@ -18,6 +18,8 @@
#define OTAPREOPT_UTILS_H_
#include <regex>
+#include <string>
+#include <vector>
namespace android {
namespace installd {
@@ -28,6 +30,9 @@
return std::regex_match(input, slot_suffix_match, slot_suffix_regex);
}
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/tests/binder_test_utils.h b/cmds/installd/tests/binder_test_utils.h
new file mode 100644
index 0000000..efd1391
--- /dev/null
+++ b/cmds/installd/tests/binder_test_utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Status.h>
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+
+#define ASSERT_BINDER_SUCCESS(expr) \
+ ({ \
+ binder::Status expect_status = (expr); \
+ ASSERT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \
+ expect_status; \
+ })
+#define ASSERT_BINDER_FAIL(expr) \
+ ({ \
+ binder::Status expect_status = (expr); \
+ ASSERT_FALSE(expect_status.isOk()); \
+ expect_status; \
+ })
+#define EXPECT_BINDER_SUCCESS(expr) \
+ ({ \
+ binder::Status expect_status = (expr); \
+ EXPECT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \
+ expect_status; \
+ })
+#define EXPECT_BINDER_FAIL(expr) \
+ ({ \
+ binder::Status expect_status = (expr); \
+ EXPECT_FALSE(expect_status.isOk()); \
+ expect_status; \
+ })
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 79e6859..78edce0 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -23,9 +23,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-
+#include <binder/Status.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
@@ -33,6 +35,7 @@
#include <selinux/android.h>
#include <selinux/avc.h>
+#include "binder_test_utils.h"
#include "dexopt.h"
#include "InstalldNativeService.h"
#include "globals.h"
@@ -84,6 +87,23 @@
system(cmd.c_str());
}
+template <typename Visitor>
+static void run_cmd_and_process_output(const std::string& cmd, const Visitor& visitor) {
+ FILE* file = popen(cmd.c_str(), "r");
+ CHECK(file != nullptr) << "Failed to ptrace " << cmd;
+ char* line = nullptr;
+ while (true) {
+ size_t n = 0u;
+ ssize_t value = getline(&line, &n, file);
+ if (value == -1) {
+ break;
+ }
+ visitor(line);
+ }
+ free(line);
+ fclose(file);
+}
+
static int mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
int ret = ::mkdir(path.c_str(), mode);
if (ret != 0) {
@@ -221,7 +241,8 @@
::testing::AssertionResult create_mock_app() {
// Create the oat dir.
app_oat_dir_ = app_apk_dir_ + "/oat";
- if (mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755) != 0) {
+ // For debug mode, the directory might already exist. Avoid erroring out.
+ if (mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755) != 0 && !kDebug) {
return ::testing::AssertionFailure() << "Could not create app dir " << app_apk_dir_
<< " : " << strerror(errno);
}
@@ -451,11 +472,9 @@
std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
bool prof_result;
- binder::Status prof_binder_result = service_->prepareAppProfile(
+ ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
- /*dex_metadata*/ nullptr, &prof_result);
-
- ASSERT_TRUE(prof_binder_result.isOk()) << prof_binder_result.toString8().c_str();
+ /*dex_metadata*/ nullptr, &prof_result));
ASSERT_TRUE(prof_result);
binder::Status result = service_->dexopt(apk_path_,
@@ -629,6 +648,50 @@
DEX2OAT_FROM_SCRATCH);
}
+TEST_F(DexoptTest, ResolveStartupConstStrings) {
+ LOG(INFO) << "DexoptDex2oatResolveStartupStrings";
+ const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings";
+ const std::string previous_value = android::base::GetProperty(property, "");
+ auto restore_property = android::base::make_scope_guard([=]() {
+ android::base::SetProperty(property, previous_value);
+ });
+ std::string odex = GetPrimaryDexArtifact(app_oat_dir_.c_str(), apk_path_, "odex");
+ // Disable the property to start.
+ bool found_disable = false;
+ ASSERT_TRUE(android::base::SetProperty(property, "false")) << property;
+ CompilePrimaryDexOk("speed-profile",
+ DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED |
+ DEXOPT_GENERATE_APP_IMAGE,
+ app_oat_dir_.c_str(),
+ kTestAppGid,
+ DEX2OAT_FROM_SCRATCH);
+ run_cmd_and_process_output(
+ "oatdump --header-only --oat-file=" + odex,
+ [&](const std::string& line) {
+ if (line.find("--resolve-startup-const-strings=false") != std::string::npos) {
+ found_disable = true;
+ }
+ });
+ EXPECT_TRUE(found_disable);
+ // Enable the property and inspect that .art artifact is larger.
+ bool found_enable = false;
+ ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
+ CompilePrimaryDexOk("speed-profile",
+ DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED |
+ DEXOPT_GENERATE_APP_IMAGE,
+ app_oat_dir_.c_str(),
+ kTestAppGid,
+ DEX2OAT_FROM_SCRATCH);
+ run_cmd_and_process_output(
+ "oatdump --header-only --oat-file=" + odex,
+ [&](const std::string& line) {
+ if (line.find("--resolve-startup-const-strings=true") != std::string::npos) {
+ found_enable = true;
+ }
+ });
+ EXPECT_TRUE(found_enable);
+}
+
class PrimaryDexReCompilationTest : public DexoptTest {
public:
virtual void SetUp() {
@@ -760,9 +823,8 @@
void createProfileSnapshot(int32_t appid, const std::string& package_name,
bool expected_result) {
bool result;
- binder::Status binder_result = service_->createProfileSnapshot(
- appid, package_name, kPrimaryProfile, apk_path_, &result);
- ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+ ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot(
+ appid, package_name, kPrimaryProfile, apk_path_, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
@@ -802,9 +864,8 @@
const std::string& code_path,
bool expected_result) {
bool result;
- binder::Status binder_result = service_->mergeProfiles(
- kTestAppUid, package_name, code_path, &result);
- ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+ ASSERT_BINDER_SUCCESS(service_->mergeProfiles(
+ kTestAppUid, package_name, code_path, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
@@ -830,10 +891,9 @@
void preparePackageProfile(const std::string& package_name, const std::string& profile_name,
bool expected_result) {
bool result;
- binder::Status binder_result = service_->prepareAppProfile(
+ ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
- /*dex_metadata*/ nullptr, &result);
- ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+ /*dex_metadata*/ nullptr, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
@@ -919,8 +979,7 @@
SetupProfiles(/*setup_ref*/ true);
createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
- binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, kPrimaryProfile);
- ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+ ASSERT_BINDER_SUCCESS(service_->destroyProfileSnapshot(package_name_, kPrimaryProfile));
struct stat st;
ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st));
ASSERT_EQ(ENOENT, errno);
@@ -978,7 +1037,7 @@
ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700));
// Run createAppData again which will offer to fix-up the profile directories.
- ASSERT_TRUE(service_->createAppData(
+ ASSERT_BINDER_SUCCESS(service_->createAppData(
volume_uuid_,
package_name_,
kTestUserId,
@@ -986,7 +1045,7 @@
kTestAppUid,
se_info_,
kOSdkVersion,
- &ce_data_inode_).isOk());
+ &ce_data_inode_));
// Check the file access.
CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
@@ -1032,9 +1091,8 @@
void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) {
bool result;
- binder::Status binder_result = service_->createProfileSnapshot(
- -1, "android", "android.prof", classpath, &result);
- ASSERT_TRUE(binder_result.isOk());
+ ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot(
+ -1, "android", "android.prof", classpath, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index e6017cb..48b07c4 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -15,18 +15,24 @@
*/
#include <sstream>
+#include <string>
+
+#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
+#include <sys/stat.h>
#include <sys/xattr.h>
-#include <android-base/logging.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
+#include "binder_test_utils.h"
#include "InstalldNativeService.h"
#include "dexopt.h"
#include "globals.h"
@@ -65,27 +71,28 @@
static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
const std::string fullPath = get_full_path(path);
- ::mkdir(fullPath.c_str(), mode);
- ::chown(fullPath.c_str(), owner, group);
- ::chmod(fullPath.c_str(), mode);
+ EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
+ EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
+ EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
}
static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
- ::fchown(fd, owner, group);
- ::fchmod(fd, mode);
- ::close(fd);
+ EXPECT_NE(fd, -1);
+ EXPECT_EQ(::fchown(fd, owner, group), 0);
+ EXPECT_EQ(::fchmod(fd, mode), 0);
+ EXPECT_EQ(::close(fd), 0);
}
static int stat_gid(const char* path) {
struct stat buf;
- ::stat(get_full_path(path).c_str(), &buf);
+ EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
return buf.st_gid;
}
static int stat_mode(const char* path) {
struct stat buf;
- ::stat(get_full_path(path).c_str(), &buf);
+ EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
}
@@ -164,8 +171,8 @@
std::vector<uint8_t> result;
std::string dexPath = get_full_path("com.example/foo/file");
- EXPECT_TRUE(service->hashSecondaryDexFile(
- dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+ EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+ dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
EXPECT_EQ(result.size(), 32U);
@@ -184,8 +191,8 @@
std::vector<uint8_t> result;
std::string dexPath = get_full_path("com.example/foo/file");
- EXPECT_TRUE(service->hashSecondaryDexFile(
- dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+ EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+ dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
EXPECT_EQ(result.size(), 0U);
}
@@ -199,8 +206,8 @@
std::vector<uint8_t> result;
std::string dexPath = get_full_path("com.example/foo/file");
- EXPECT_TRUE(service->hashSecondaryDexFile(
- dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+ EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile(
+ dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result));
EXPECT_EQ(result.size(), 0U);
}
@@ -214,8 +221,8 @@
std::vector<uint8_t> result;
std::string dexPath = get_full_path("com.example/foo/file");
- EXPECT_FALSE(service->hashSecondaryDexFile(
- dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+ EXPECT_BINDER_FAIL(service->hashSecondaryDexFile(
+ dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result));
}
TEST_F(ServiceTest, CalculateOat) {
@@ -287,8 +294,13 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of the CE content but not the DE content.
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_CE).isOk());
+ int64_t ce_snapshot_inode;
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf));
+ ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino);
std::string ce_content, de_content;
// At this point, we should have the CE content but not the DE content.
@@ -305,8 +317,10 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of the DE content but not the CE content.
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE).isOk());
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode));
+ // Only DE content snapshot was requested.
+ ASSERT_EQ(ce_snapshot_inode, 0);
// At this point, both the CE as well as the DE content should be fully
// populated.
@@ -324,8 +338,8 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of both the CE as well as the DE content.
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
ASSERT_TRUE(android::base::ReadFileToString(
rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
@@ -351,10 +365,13 @@
auto scope_guard = android::base::make_scope_guard(deleter);
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_CE).isOk());
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE).isOk());
+ int64_t ce_snapshot_inode;
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+ // No CE content snapshot was performed.
+ ASSERT_EQ(ce_snapshot_inode, 0);
// The snapshot calls must succeed but there should be no snapshot
// created.
@@ -363,6 +380,126 @@
ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
}
+TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+ &fake_package_ce_path, &fake_package_de_path]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ // Simulate presence of an existing snapshot
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_CE", rollback_ce_dir + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_DE", rollback_de_dir + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+
+ // Create app data.
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_2_CE", fake_package_ce_path + "/file2",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_2_DE", fake_package_de_path + "/file2",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+
+ // Previous snapshot (with data for file1) must be cleared.
+ struct stat sb;
+ ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo/file1").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) {
+ // Setup app data to make sure that fails due to wrong volumeUuid being
+ // passed, not because of some other reason.
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+ "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+ auto fake_package_ce_cache_path = fake_package_ce_path + "/cache";
+ auto fake_package_ce_code_cache_path = fake_package_ce_path + "/code_cache";
+ auto fake_package_de_cache_path = fake_package_de_path + "/cache";
+ auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache";
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 700));
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto deleter = [&fake_package_ce_path, &fake_package_de_path,
+ &rollback_ce_dir, &rollback_de_dir]() {
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ delete_dir_contents_and_dir(rollback_ce_dir, true);
+ delete_dir_contents_and_dir(rollback_de_dir, true);
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_CE", fake_package_ce_code_cache_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_DE", fake_package_de_cache_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
+ // The snapshot call must clear cache.
+ struct stat sb;
+ ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((fake_package_ce_code_cache_path + "/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((fake_package_de_cache_path + "/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb));
+}
+
TEST_F(ServiceTest, RestoreAppDataSnapshot) {
auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
@@ -404,8 +541,8 @@
"TEST_CONTENT_DE", fake_package_de_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
- "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
std::string ce_content, de_content;
ASSERT_TRUE(android::base::ReadFileToString(
@@ -416,6 +553,163 @@
ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
}
+TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+ &fake_package_ce_path, &fake_package_de_path]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ // Prepare data for snapshot.
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_DE", fake_package_de_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+
+ int64_t ce_snapshot_inode;
+ // Request a snapshot of both the CE as well as the DE content.
+ ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
+ // Because CE data snapshot was requested, ce_snapshot_inode can't be null.
+ ASSERT_NE(0, ce_snapshot_inode);
+ // Check snapshot is there.
+ struct stat sb;
+ ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+ ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+
+
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ // Check snapshot is deleted.
+ ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+ ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+ &fake_package_ce_path, &fake_package_de_path]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ // Create a snapshot
+ ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+ // Check snapshot is deleted.
+ struct stat sb;
+ ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
+ ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
+
+ // Check that deleting already deleted snapshot is no-op.
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+}
+
+TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) {
+ // Setup rollback data to make sure that test fails due to wrong volumeUuid
+ // being passed, not because of some other reason.
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+ &fake_package_ce_path, &fake_package_de_path]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ "com.foo", 0, 0, FLAG_STORAGE_DE).isOk());
+}
+
+TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) {
+ // Setup rollback data to make sure that fails due to wrong volumeUuid being
+ // passed, not because of some other reason.
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+
+ auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+
+ auto deleter = [&rollback_ce_dir, &rollback_de_dir,
+ &fake_package_ce_path, &fake_package_de_path]() {
+ delete_dir_contents(rollback_ce_dir, true);
+ delete_dir_contents(rollback_de_dir, true);
+ delete_dir_contents(fake_package_ce_path, true);
+ delete_dir_contents(fake_package_de_path, true);
+ rmdir(rollback_ce_dir.c_str());
+ rmdir(rollback_de_dir.c_str());
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE));
+}
} // namespace installd
} // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ce99fff..1782aa2 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -18,6 +18,7 @@
#include <string.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
#include <gtest/gtest.h>
#include "InstalldNativeService.h"
@@ -565,6 +566,29 @@
EXPECT_EQ("/data/misc_de/10/rollback",
create_data_misc_de_rollback_path(nullptr, 10));
+ EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0));
+ EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239));
+
+ auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo");
+ auto deleter = [&rollback_ce_package_path]() {
+ delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */);
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700));
+
+ ino_t ce_data_inode;
+ ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode));
+
+ EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode));
+ // Check that path defined by inode is picked even if it's not the same as
+ // the fallback one.
+ EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode));
+
// These last couple of cases are never exercised in production because we
// only snapshot apps in the primary data partition. Exercise them here for
// the sake of completeness.
diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h
index a7ef674..70eefe2 100644
--- a/cmds/installd/tests/test_utils.h
+++ b/cmds/installd/tests/test_utils.h
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 24f5eab..5b487bb 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -70,6 +70,35 @@
CHECK(is_valid_package_name(package_name));
}
+static std::string resolve_ce_path_by_inode_or_fallback(const std::string& root_path,
+ ino_t ce_data_inode, const std::string& fallback) {
+ if (ce_data_inode != 0) {
+ DIR* dir = opendir(root_path.c_str());
+ if (dir == nullptr) {
+ PLOG(ERROR) << "Failed to opendir " << root_path;
+ return fallback;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(dir))) {
+ if (ent->d_ino == ce_data_inode) {
+ auto resolved = StringPrintf("%s/%s", root_path.c_str(), ent->d_name);
+ if (resolved != fallback) {
+ LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
+ << " instead of " << fallback;
+ }
+ closedir(dir);
+ return resolved;
+ }
+ }
+ LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
+ closedir(dir);
+ return fallback;
+ } else {
+ return fallback;
+ }
+}
+
/**
* Create the path name where package app contents should be stored for
* the given volume UUID and package name. An empty UUID is assumed to
@@ -113,34 +142,8 @@
// For testing purposes, rely on the inode when defined; this could be
// optimized to use access() in the future.
auto fallback = create_data_user_ce_package_path(volume_uuid, user, package_name);
- if (ce_data_inode != 0) {
- auto user_path = create_data_user_ce_path(volume_uuid, user);
- DIR* dir = opendir(user_path.c_str());
- if (dir == nullptr) {
- PLOG(ERROR) << "Failed to opendir " << user_path;
- return fallback;
- }
-
- struct dirent* ent;
- while ((ent = readdir(dir))) {
- if (ent->d_ino == ce_data_inode) {
- auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
-#if DEBUG_XATTRS
- if (resolved != fallback) {
- LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
- << " instead of " << fallback;
- }
-#endif
- closedir(dir);
- return resolved;
- }
- }
- LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
- closedir(dir);
- return fallback;
- } else {
- return fallback;
- }
+ auto user_path = create_data_user_ce_path(volume_uuid, user);
+ return resolve_ce_path_by_inode_or_fallback(user_path, ce_data_inode, fallback);
}
std::string create_data_user_de_package_path(const char* volume_uuid,
@@ -209,6 +212,13 @@
create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name);
}
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+ userid_t user, const char* package_name, ino_t ce_rollback_inode) {
+ auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
+ auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user);
+ return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback);
+}
+
std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
userid_t user, const char* package_name) {
return StringPrintf("%s/%s",
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 430f515..0711b34 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -65,6 +65,8 @@
std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user);
std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
userid_t user, const char* package_name);
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+ userid_t user, const char* package_name, ino_t ce_rollback_inode);
std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
userid_t user, const char* package_name);
@@ -117,6 +119,8 @@
int delete_dir_contents_fd(int dfd, const char *name);
+int rm_package_dir(const std::string& package_dir);
+
int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group);
int64_t data_disk_free(const std::string& data_path);
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
new file mode 100644
index 0000000..a6025e6
--- /dev/null
+++ b/cmds/installd/utils_default.cpp
@@ -0,0 +1,30 @@
+/*
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "utils.h"
+
+namespace android {
+namespace installd {
+
+// In this file are default definitions of the functions that may contain
+// platform dependent logic.
+
+int rm_package_dir(const std::string& package_dir) {
+ return delete_dir_contents_and_dir(package_dir);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/data/etc/android.hardware.biometrics.fingerprint.xml b/data/etc/android.hardware.biometrics.fingerprint.xml
deleted file mode 100644
index e5af541..0000000
--- a/data/etc/android.hardware.biometrics.fingerprint.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<!-- This is the standard set of features for a biometric fingerprint sensor. -->
-<permissions>
- <feature name="android.hardware.biometrics.fingerprint" />
-</permissions>
diff --git a/data/etc/android.hardware.telephony.ims.xml b/data/etc/android.hardware.telephony.ims.xml
new file mode 100644
index 0000000..eeb7b00
--- /dev/null
+++ b/data/etc/android.hardware.telephony.ims.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Feature for devices that support IMS via ImsService APIs. -->
+<permissions>
+ <feature name="android.hardware.telephony.ims" />
+</permissions>
diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml
new file mode 100644
index 0000000..3464487
--- /dev/null
+++ b/data/etc/android.software.secure_lock_screen.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <feature name="android.software.secure_lock_screen" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index d6021c0..6cbe4ae 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -45,6 +45,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 8b5a461..915e579 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -43,6 +43,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 060a334..619d017 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,6 +51,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 6db2627..52524ca 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -51,6 +51,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index e2ab71a..0f364c1 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -35,6 +35,7 @@
<!-- basic system services -->
<feature name="android.software.home_screen" />
+ <feature name="android.software.secure_lock_screen" />
<!-- input management and third-party input method editors -->
<feature name="android.software.input_methods" />
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
index 4de314d..c87ee56 100644
--- a/headers/media_plugin/media/cas/CasAPI.h
+++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -48,6 +48,14 @@
uint8_t *data,
size_t size);
+typedef void (*CasPluginCallbackExt)(
+ void *appData,
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size,
+ const CasSessionId *sessionId);
+
struct CasFactory {
CasFactory() {}
virtual ~CasFactory() {}
@@ -67,6 +75,13 @@
CasPluginCallback callback,
CasPlugin **plugin) = 0;
+ // Construct a new extend instance of a CasPlugin given a CA_system_id
+ virtual status_t createPlugin(
+ int32_t CA_system_id,
+ void *appData,
+ CasPluginCallbackExt callback,
+ CasPlugin **plugin) = 0;
+
private:
CasFactory(const CasFactory &);
CasFactory &operator=(const CasFactory &); /* NOLINT */
@@ -110,7 +125,15 @@
int32_t arg,
const CasData &eventData) = 0;
- // Native implementation of the MediaCas Java API provision method.
+ // Deliver an session event to the CasPlugin. The format of the event is
+ // specific to the CA scheme and is opaque to the framework.
+ virtual status_t sendSessionEvent(
+ const CasSessionId &sessionId,
+ int32_t event,
+ int32_t arg,
+ const CasData &eventData) = 0;
+
+ // Native implementation of the MediaCas Java API provision method.
virtual status_t provision(
const String8 &provisionString) = 0;
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index 0ce50f2..59bd292 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -183,10 +183,10 @@
kOfflineLicenseStateUnknown,
// Offline license state is usable, the keys may be used for decryption.
kOfflineLicenseStateUsable,
- // Offline license state is inactive, the keys have been marked for
+ // Offline license state is released, the keys have been marked for
// release using getKeyRequest() with kKeyType_Release but the
// key response has not been received.
- kOfflineLicenseStateInactive
+ kOfflineLicenseStateReleased
};
DrmPlugin() {}
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index 152015b..ce30b41 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -911,6 +911,9 @@
case OMX_VIDEO_AVCLevel5: return "Level5";
case OMX_VIDEO_AVCLevel51: return "Level51";
case OMX_VIDEO_AVCLevel52: return "Level52";
+ case OMX_VIDEO_AVCLevel6: return "Level6";
+ case OMX_VIDEO_AVCLevel61: return "Level61";
+ case OMX_VIDEO_AVCLevel62: return "Level62";
default: return def;
}
}
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index 9fd2fd2..b6edaa9 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -832,6 +832,9 @@
OMX_VIDEO_AVCLevel5 = 0x4000, /**< Level 5 */
OMX_VIDEO_AVCLevel51 = 0x8000, /**< Level 5.1 */
OMX_VIDEO_AVCLevel52 = 0x10000, /**< Level 5.2 */
+ OMX_VIDEO_AVCLevel6 = 0x20000, /**< Level 6 */
+ OMX_VIDEO_AVCLevel61 = 0x40000, /**< Level 6.1 */
+ OMX_VIDEO_AVCLevel62 = 0x80000, /**< Level 6.2 */
OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 1a1b010..e9d5c16 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -495,6 +495,7 @@
* - ASensorEventQueue_hasEvents()
* - ASensorEventQueue_getEvents()
* - ASensorEventQueue_setEventRate()
+ * - ASensorEventQueue_requestAdditionalInfoEvents()
*/
typedef struct ASensorEventQueue ASensorEventQueue;
@@ -779,6 +780,29 @@
*/
ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
+ * the given {@link ASensorEventQueue}.
+ *
+ * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ *
+ * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
+ * {@link ASensorEventQueue_getEvents}. The client is responsible for checking
+ * {@link ASensorEvent#type} to determine the event type prior to handling of
+ * the event.
+ *
+ * The client must be tolerant of any value for
+ * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future
+ * and may delivered to the client.
+ *
+ * \param queue {@link ASensorEventQueue} to configure
+ * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events,
+ * false to stop receiving events
+ * \return 0 on success or a negative error code on failure
+ */
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable);
+#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */
/*****************************************************************************/
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 13b630b..0e79239 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -28,7 +28,9 @@
#include <sys/cdefs.h>
+#include <android/data_space.h>
#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
#include <android/native_window.h>
__BEGIN_DECLS
@@ -60,10 +62,11 @@
__INTRODUCED_IN(29);
/**
- * Destroys the |surface_control| object. After releasing the ASurfaceControl the caller no longer
- * has ownership of the AsurfaceControl.
+ * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
+ * as their parent remains on display.
*/
-void ASurfaceControl_destroy(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
struct ASurfaceTransaction;
@@ -94,24 +97,94 @@
void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
/**
+ * An opaque handle returned during a callback that can be used to query general stats and stats for
+ * surfaces which were either removed or for which buffers were updated after this transaction was
+ * applied.
+ */
+typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
+
+/**
* Since the transactions are applied asynchronously, the
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
* |context| is the optional context provided by the client that is passed into
* the callback.
- * |present_fence| is the sync fence that signals when the transaction has been presented.
- * The recipient of the callback takes ownership of the present_fence and is responsible for closing
- * it.
*
- * It is safe to assume that once the present fence singals, that reads for all buffers,
- * submitted in previous transactions, which are not in the surface tree after a transaction is
- * applied, are finished and the buffers may be reused.
+ * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the the callback.
*
* THREADING
* The transaction completed callback can be invoked on any thread.
*/
-typedef void (*ASurfaceTransaction_OnComplete)(void* context, int32_t present_fence);
+typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the frame was latched by the framework. Once a frame is
+ * latched by the framework, it is presented at the following hardware vsync.
+ */
+int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns a sync fence that signals when the transaction has been presented.
+ * The recipient of the callback takes ownership of the fence and is responsible for closing
+ * it.
+ */
+int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
+ * When the client is done using the array, it must release it by calling
+ * ASurfaceTransactionStats_releaseASurfaceControls.
+ *
+ * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ */
+void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl*** outASurfaceControls,
+ size_t* outASurfaceControlsSize)
+ __INTRODUCED_IN(29);
+/**
+ * Releases the array of ASurfaceControls that were returned by
+ * ASurfaceTransactionStats_getASurfaceControls.
+ */
+void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
+ * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until
+ * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
+ */
+int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl* surface_control)
+ __INTRODUCED_IN(29);
+
+/**
+ * The returns the fence used to signal the release of the PREVIOUS buffer set on
+ * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * buffer is already released. The recipient of the callback takes ownership of the
+ * previousReleaseFenceFd and is responsible for closing it.
+ *
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
+ * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * addition of a buffer to a particular surface as a unique ref. When a transaction updates or
+ * removes a buffer from a surface, or removes the surface itself from the tree, this ref is
+ * guaranteed to be released in the OnComplete callback for this transaction. The
+ * ASurfaceControlStats provided in the callback for this surface may contain an optional fence
+ * which must be signaled before the ref is assumed to be released.
+ *
+ * The client must ensure that all pending refs on a buffer are released before attempting to reuse
+ * this buffer, otherwise synchronization errors may occur.
+ */
+int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
+ ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl* surface_control)
+ __INTRODUCED_IN(29);
/**
* Sets the callback that will be invoked when the updates from this transaction
@@ -121,6 +194,16 @@
void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
+/**
+ * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
+ * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ *
+ * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ */
+void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, ASurfaceControl* new_parent)
+ __INTRODUCED_IN(29);
+
/* Parameter for ASurfaceTransaction_setVisibility */
enum {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
@@ -148,15 +231,26 @@
/**
* Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
- * fence_fd should be a file descriptor that is signaled when all pending work
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
* for the buffer is complete and the buffer can be safely read.
*
- * The frameworks takes ownership of the |fence_fd| passed and is responsible
+ * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
* for closing it.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, AHardwareBuffer* buffer,
- int fence_fd = -1) __INTRODUCED_IN(29);
+ int acquire_fence_fd = -1) __INTRODUCED_IN(29);
+
+/**
+ * Updates the color for |surface_control|. This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
+ * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
+ * will be the dataspace and alpha set for the background color layer.
+ */
+void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float r, float g, float b,
+ float alpha, ADataSpace dataspace)
+ __INTRODUCED_IN(29);
/**
* |source| the sub-rect within the buffer's content to be rendered inside the surface's area
@@ -189,8 +283,9 @@
* opaque or visual errors can occur.
*/
void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int8_t transparency)
- __INTRODUCED_IN(29);
+ ASurfaceControl* surface_control,
+ int8_t transparency)
+ __INTRODUCED_IN(29);
/**
* Updates the region for the content on this surface updated in this
@@ -200,6 +295,59 @@
ASurfaceControl* surface_control, const ARect rects[],
uint32_t count) __INTRODUCED_IN(29);
+/**
+ * Specifies a desiredPresentTime for the transaction. The framework will try to present
+ * the transaction at or after the time specified.
+ *
+ * Transactions will not be presented until all of their acquire fences have signaled even if the
+ * app requests an earlier present time.
+ *
+ * If an earlier transaction has a desired present time of x, and a later transaction has a desired
+ * present time that is before x, the later transaction will not preempt the earlier transaction.
+ */
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
+ int64_t desiredPresentTime) __INTRODUCED_IN(29);
+
+/**
+ * Sets the alpha for the buffer. It uses a premultiplied blending.
+ *
+ * The |alpha| must be between 0.0 and 1.0.
+ */
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float alpha)
+ __INTRODUCED_IN(29);
+
+/**
+ * Sets the data space of the surface_control's buffers.
+ *
+ * If no data space is set, the surface control defaults to ADATASPACE_SRGB.
+ */
+void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, ADataSpace data_space)
+ __INTRODUCED_IN(29);
+
+/*
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ *
+ * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ struct AHdrMetadata_smpte2086* metadata)
+ __INTRODUCED_IN(29);
+
+/*
+ * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
+ *
+ * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ struct AHdrMetadata_cta861_3* metadata)
+ __INTRODUCED_IN(29);
+
#endif // __ANDROID_API__ >= 29
__END_DECLS
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 34d164c..ce8db91 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -165,6 +165,13 @@
extern std::string getInputDeviceConfigurationFilePathByName(
const std::string& name, InputDeviceConfigurationFileType type);
+enum ReservedInputDeviceId : int32_t {
+ // Device id of a special "virtual" keyboard that is always present.
+ VIRTUAL_KEYBOARD_ID = -1,
+ // Device id of the "built-in" keyboard if there is one.
+ BUILT_IN_KEYBOARD_ID = 0,
+};
+
} // namespace android
#endif // _LIBINPUT_INPUT_DEVICE_H
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 2b8cc57..a065a4c 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -163,6 +163,7 @@
int32_t ownerUid;
int32_t inputFeatures;
int32_t displayId;
+ int32_t portalToDisplayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
void addTouchableRegion(const Rect& region);
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index d68f274..640bf1f 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -35,6 +35,14 @@
mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) {
}
+ bool operator==(const TouchVideoFrame& rhs) const {
+ return mWidth == rhs.mWidth
+ && mHeight == rhs.mHeight
+ && mData == rhs.mData
+ && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec
+ && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec;
+ }
+
/**
* Width of the frame
*/
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 96ee295..cb0e08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -218,7 +218,7 @@
if (!e) return; // out of memory
}
- e->mRequestingSid = true;
+ e->mRequestingSid = requestingSid;
}
BBinder::~BBinder()
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f779d6e..a595c01 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -181,7 +181,10 @@
if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
int size = ashmem_get_size_region(obj.handle);
if (size > 0) {
- *outAshmemSize -= size;
+ // ashmem size might have changed since last time it was accounted for, e.g.
+ // in acquire_object(). Value of *outAshmemSize is not critical since we are
+ // releasing the object anyway. Check for integer overflow condition.
+ *outAshmemSize -= std::min(*outAshmemSize, static_cast<size_t>(size));
}
}
@@ -599,17 +602,53 @@
return mHasFds;
}
+void Parcel::updateWorkSourceRequestHeaderPosition() const {
+ // Only update the request headers once. We only want to point
+ // to the first headers read/written.
+ if (!mRequestHeaderPresent) {
+ mWorkSourceRequestHeaderPosition = dataPosition();
+ mRequestHeaderPresent = true;
+ }
+}
+
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+ updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ?
threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
+bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
+{
+ if (!mRequestHeaderPresent) {
+ return false;
+ }
+
+ const size_t initialPosition = dataPosition();
+ setDataPosition(mWorkSourceRequestHeaderPosition);
+ status_t err = writeInt32(uid);
+ setDataPosition(initialPosition);
+ return err == NO_ERROR;
+}
+
+uid_t Parcel::readCallingWorkSourceUid()
+{
+ if (!mRequestHeaderPresent) {
+ return IPCThreadState::kUnsetWorkSource;
+ }
+
+ const size_t initialPosition = dataPosition();
+ setDataPosition(mWorkSourceRequestHeaderPosition);
+ uid_t uid = readInt32();
+ setDataPosition(initialPosition);
+ return uid;
+}
+
bool Parcel::checkInterface(IBinder* binder) const
{
return enforceInterface(binder->getInterfaceDescriptor());
@@ -634,6 +673,7 @@
threadState->setStrictModePolicy(strictPolicy);
}
// WorkSource.
+ updateWorkSourceRequestHeaderPosition();
int32_t workSource = readInt32();
threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
// Interface descriptor.
@@ -2889,6 +2929,8 @@
mAllowFds = true;
mOwner = nullptr;
mOpenAshmemSize = 0;
+ mWorkSourceRequestHeaderPosition = 0;
+ mRequestHeaderPresent = false;
// racing multiple init leads only to multiple identical write
if (gMaxFds == 0) {
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 26dafd0..7b086d0 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,23 +46,24 @@
PROCESS_STATE_PERSISTENT = 0,
PROCESS_STATE_PERSISTENT_UI = 1,
PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE = 3,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 5,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 6,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 7,
- PROCESS_STATE_BACKUP = 8,
- PROCESS_STATE_SERVICE = 9,
- PROCESS_STATE_RECEIVER = 10,
- PROCESS_STATE_TOP_SLEEPING = 11,
- PROCESS_STATE_HEAVY_WEIGHT = 12,
- PROCESS_STATE_HOME = 13,
- PROCESS_STATE_LAST_ACTIVITY = 14,
- PROCESS_STATE_CACHED_ACTIVITY = 15,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16,
- PROCESS_STATE_CACHED_RECENT = 17,
- PROCESS_STATE_CACHED_EMPTY = 18,
- PROCESS_STATE_NONEXISTENT = 19,
+ PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 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();
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f6381f7..0d30560 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -54,6 +54,7 @@
virtual const String16& getInterfaceDescriptor() const;
protected:
+ typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
@@ -66,6 +67,7 @@
explicit BpInterface(const sp<IBinder>& remote);
protected:
+ typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 240b708..afdfe4f 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -395,6 +395,12 @@
static size_t getGlobalAllocSize();
static size_t getGlobalAllocCount();
+ bool replaceCallingWorkSourceUid(uid_t uid);
+ // Returns the work source provided by the caller. This can only be trusted for trusted calling
+ // uid.
+ uid_t readCallingWorkSourceUid();
+ void readRequestHeaders() const;
+
private:
typedef void (*release_func)(Parcel* parcel,
const uint8_t* data, size_t dataSize,
@@ -429,6 +435,7 @@
void initState();
void scanForFds() const;
status_t validateReadData(size_t len) const;
+ void updateWorkSourceRequestHeaderPosition() const;
template<class T>
status_t readAligned(T *pArg) const;
@@ -477,6 +484,9 @@
mutable size_t mNextObjectHint;
mutable bool mObjectsSorted;
+ mutable bool mRequestHeaderPresent;
+ mutable size_t mWorkSourceRequestHeaderPosition;
+
mutable bool mFdsKnown;
mutable bool mHasFds;
bool mAllowFds;
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 3bfd462..5fa2ff6 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,12 @@
return callParcel("writeParcelableVector",
[&]() { return parcel->writeParcelableVector(v); });
}
+ status_t read(const Parcel& parcel, float* f) const {
+ return callParcel("readFloat", [&]() { return parcel.readFloat(f); });
+ }
+ status_t write(Parcel* parcel, float f) const {
+ return callParcel("writeFloat", [&]() { return parcel->writeFloat(f); });
+ }
// Templates to handle integral types. We use a struct template to require that the called
// function exactly matches the signedness and size of the argument (e.g., the argument isn't
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 05655c1..a96c9a0 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -16,7 +16,7 @@
cc_library {
name: "libbinder_ndk",
- vendor_available: false,
+ vendor_available: true,
export_include_dirs: [
"include_ndk",
@@ -39,12 +39,24 @@
],
shared_libs: [
- "libandroid_runtime",
"libbase",
"libbinder",
"libutils",
],
+ required: [
+ // libbinder_ndk may be used by Java and non-Java things. When lower-level things use it,
+ // they shouldn't have to take on the cost of loading libandroid_runtime.
+ "libandroid_runtime",
+ ],
+
+ header_libs: [
+ "jni_headers",
+ ],
+ export_header_lib_headers: [
+ "jni_headers",
+ ],
+
version_script: "libbinder_ndk.map.txt",
stubs: {
symbol_file: "libbinder_ndk.map.txt",
@@ -60,12 +72,10 @@
"include_ndk/android/*.h",
],
license: "NOTICE",
- draft: true,
}
ndk_library {
name: "libbinder_ndk",
symbol_file: "libbinder_ndk.map.txt",
first_version: "29",
- draft: true,
}
diff --git a/libs/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp
index baea2e8..4a31080 100644
--- a/libs/binder/ndk/ibinder_jni.cpp
+++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -17,15 +17,68 @@
#include <android/binder_ibinder_jni.h>
#include "ibinder_internal.h"
-#include <android_util_Binder.h>
+#include <android-base/logging.h>
+#include <binder/IBinder.h>
+
+#include <mutex>
+
+#include <dlfcn.h>
using ::android::IBinder;
-using ::android::ibinderForJavaObject;
-using ::android::javaObjectForIBinder;
using ::android::sp;
+struct LazyAndroidRuntime {
+ typedef sp<IBinder> (*FromJava)(JNIEnv* env, jobject obj);
+ typedef jobject (*ToJava)(JNIEnv* env, const sp<IBinder>& val);
+
+ static FromJava ibinderForJavaObject;
+ static ToJava javaObjectForIBinder;
+
+ static void load() {
+ std::call_once(mLoadFlag, []() {
+ void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
+ if (handle == nullptr) {
+ LOG(WARNING) << "Could not open libandroid_runtime.";
+ return;
+ }
+
+ ibinderForJavaObject = reinterpret_cast<FromJava>(
+ dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (ibinderForJavaObject == nullptr) {
+ LOG(WARNING) << "Could not find ibinderForJavaObject.";
+ // no return
+ }
+
+ javaObjectForIBinder = reinterpret_cast<ToJava>(dlsym(
+ handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
+ if (javaObjectForIBinder == nullptr) {
+ LOG(WARNING) << "Could not find javaObjectForIBinder.";
+ // no return
+ }
+ });
+ }
+
+ private:
+ static std::once_flag mLoadFlag;
+
+ LazyAndroidRuntime(){};
+};
+
+LazyAndroidRuntime::FromJava LazyAndroidRuntime::ibinderForJavaObject = nullptr;
+LazyAndroidRuntime::ToJava LazyAndroidRuntime::javaObjectForIBinder = nullptr;
+std::once_flag LazyAndroidRuntime::mLoadFlag;
+
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
- sp<IBinder> ibinder = ibinderForJavaObject(env, binder);
+ if (binder == nullptr) {
+ return nullptr;
+ }
+
+ LazyAndroidRuntime::load();
+ if (LazyAndroidRuntime::ibinderForJavaObject == nullptr) {
+ return nullptr;
+ }
+
+ sp<IBinder> ibinder = (LazyAndroidRuntime::ibinderForJavaObject)(env, binder);
sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder);
AIBinder_incStrong(cbinder.get());
@@ -38,5 +91,10 @@
return nullptr;
}
- return javaObjectForIBinder(env, binder->getBinder());
+ LazyAndroidRuntime::load();
+ if (LazyAndroidRuntime::javaObjectForIBinder == nullptr) {
+ return nullptr;
+ }
+
+ return (LazyAndroidRuntime::javaObjectForIBinder)(env, binder->getBinder());
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 80773f3..c6868b0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -109,6 +109,8 @@
AIBinder* mBinder = nullptr;
};
+namespace impl {
+
/**
* This baseclass owns a single object, used to make various classes RAII.
*/
@@ -169,10 +171,12 @@
T mT;
};
+} // namespace impl
+
/**
* Convenience wrapper. See AParcel.
*/
-class ScopedAParcel : public ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
+class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -185,7 +189,7 @@
/**
* Convenience wrapper. See AStatus.
*/
-class ScopedAStatus : public ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
+class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -209,8 +213,8 @@
* Convenience wrapper. See AIBinder_DeathRecipient.
*/
class ScopedAIBinder_DeathRecipient
- : public ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
- nullptr> {
+ : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
+ nullptr> {
public:
/**
* Takes ownership of a.
@@ -225,7 +229,7 @@
* Convenience wrapper. See AIBinder_Weak.
*/
class ScopedAIBinder_Weak
- : public ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
+ : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -243,7 +247,7 @@
/**
* Convenience wrapper for a file descriptor.
*/
-class ScopedFileDescriptor : public ScopedAResource<int, int, close, -1> {
+class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> {
public:
/**
* Takes ownership of a.
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 6a16e24..3b1db27 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -229,6 +229,7 @@
IncrementUint32,
IncrementInt64,
IncrementUint64,
+ IncrementFloat,
IncrementTwo,
Last,
};
@@ -259,6 +260,7 @@
virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0;
virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0;
virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0;
+ virtual status_t increment(float a, float* aPlusOne) const = 0;
// This tests that input/output parameter interleaving works correctly
virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b,
@@ -353,6 +355,11 @@
using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne);
}
+ status_t increment(float a, float* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const;
+ return callRemote<Signature>(Tag::IncrementFloat, a, aPlusOne);
+ }
status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
using Signature =
@@ -474,6 +481,11 @@
*aPlusOne = a + 1;
return NO_ERROR;
}
+ status_t increment(float a, float* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1.0f;
+ return NO_ERROR;
+ }
status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
*aPlusOne = a + 1;
@@ -555,6 +567,10 @@
using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
}
+ case ISafeInterfaceTest::Tag::IncrementFloat: {
+ using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
case ISafeInterfaceTest::Tag::IncrementTwo: {
using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t,
int32_t*) const;
@@ -804,6 +820,14 @@
ASSERT_EQ(a + 1, aPlusOne);
}
+TEST_F(SafeInterfaceTest, TestIncrementFloat) {
+ const float a = 1.0f;
+ float aPlusOne = 0.0f;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1.0f, aPlusOne);
+}
+
TEST_F(SafeInterfaceTest, TestIncrementTwo) {
const int32_t a = 1;
int32_t aPlusOne = 0;
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
new file mode 100644
index 0000000..28cb138
--- /dev/null
+++ b/libs/cputimeinstate/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+ name: "libtimeinstate",
+ srcs: ["cputimeinstate.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "liblog",
+ "libnetdutils"
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
+
+cc_test {
+ name: "libtimeinstate_test",
+ srcs: ["testtimeinstate.cpp"],
+ shared_libs: [
+ "libtimeinstate",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
+
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
new file mode 100644
index 0000000..4cddf94
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libtimeinstate"
+
+#include "cputimeinstate.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bpf/BpfMap.h>
+#include <libbpf.h>
+#include <log/log.h>
+
+#define BPF_FS_PATH "/sys/fs/bpf/"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace bpf {
+
+typedef struct {
+ uint32_t uid;
+ uint32_t freq;
+} time_key_t;
+
+typedef struct {
+ uint64_t ar[100];
+} val_t;
+
+static std::mutex gInitializedMutex;
+static bool gInitialized = false;
+static uint32_t gNPolicies = 0;
+static std::vector<std::vector<uint32_t>> gPolicyFreqs;
+static std::vector<std::vector<uint32_t>> gPolicyCpus;
+static std::set<uint32_t> gAllFreqs;
+static unique_fd gMapFd;
+
+static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
+ std::string data;
+
+ if (!android::base::ReadFileToString(path, &data)) {
+ ALOGD("Failed to read file %s", path.c_str());
+ return false;
+ }
+
+ auto strings = android::base::Split(data, " \n");
+ for (const auto &s : strings) {
+ if (s.empty()) continue;
+ uint32_t n;
+ if (!android::base::ParseUint(s, &n)) {
+ ALOGD("Failed to parse file %s", path.c_str());
+ return false;
+ }
+ out->emplace_back(n);
+ }
+ return true;
+}
+
+static int isPolicyFile(const struct dirent *d) {
+ return android::base::StartsWith(d->d_name, "policy");
+}
+
+static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) {
+ uint32_t policyN1, policyN2;
+ if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 ||
+ sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1)
+ return 0;
+ return policyN1 - policyN2;
+}
+
+static bool initGlobals() {
+ std::lock_guard<std::mutex> guard(gInitializedMutex);
+ if (gInitialized) return true;
+
+ struct dirent **dirlist;
+ const char basepath[] = "/sys/devices/system/cpu/cpufreq";
+ int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles);
+ if (ret == -1) return false;
+ gNPolicies = ret;
+
+ std::vector<std::string> policyFileNames;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ policyFileNames.emplace_back(dirlist[i]->d_name);
+ free(dirlist[i]);
+ }
+ free(dirlist);
+
+ for (const auto &policy : policyFileNames) {
+ std::vector<uint32_t> freqs;
+ for (const auto &name : {"available", "boost"}) {
+ std::string path =
+ StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name);
+ if (!readNumbersFromFile(path, &freqs)) return false;
+ }
+ std::sort(freqs.begin(), freqs.end());
+ gPolicyFreqs.emplace_back(freqs);
+
+ for (auto freq : freqs) gAllFreqs.insert(freq);
+
+ std::vector<uint32_t> cpus;
+ std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
+ if (!readNumbersFromFile(path, &cpus)) return false;
+ gPolicyCpus.emplace_back(cpus);
+ }
+
+ gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")};
+ if (gMapFd < 0) return false;
+
+ gInitialized = true;
+ return true;
+}
+
+static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
+ std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
+ eventType.c_str(), eventName.c_str());
+ int prog_fd = bpf_obj_get(path.c_str());
+ if (prog_fd < 0) {
+ ALOGD("bpf_obj_get() failed for program %s", path.c_str());
+ return false;
+ }
+ if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) {
+ ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(),
+ eventName.c_str());
+ return false;
+ }
+ return true;
+}
+
+// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
+// Returns true on success, false otherwise.
+// Tracking is active only once a live process has successfully called this function; if the calling
+// process dies then it must be called again to resume tracking.
+// This function should *not* be called while tracking is already active; doing so is unnecessary
+// and can lead to accounting errors.
+bool startTrackingUidCpuFreqTimes() {
+ return attachTracepointProgram("sched", "sched_switch") &&
+ attachTracepointProgram("power", "cpu_frequency");
+}
+
+// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
+// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors
+// using the format:
+// [[t0_0, t0_1, ...],
+// [t1_0, t1_1, ...], ...]
+// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq.
+bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) {
+ if (!gInitialized && !initGlobals()) return false;
+ time_key_t key = {.uid = uid, .freq = 0};
+
+ freqTimes->clear();
+ freqTimes->resize(gNPolicies);
+ std::vector<uint32_t> idxs(gNPolicies, 0);
+
+ val_t value;
+ for (uint32_t freq : gAllFreqs) {
+ key.freq = freq;
+ int ret = findMapEntry(gMapFd, &key, &value);
+ if (ret) {
+ if (errno == ENOENT)
+ memset(&value.ar, 0, sizeof(value.ar));
+ else
+ return false;
+ }
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue;
+ uint64_t time = 0;
+ for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu];
+ idxs[i] += 1;
+ (*freqTimes)[i].emplace_back(time);
+ }
+ }
+
+ return true;
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
+// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to
+// vectors of vectors using the format:
+// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+// uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
+bool getUidsCpuFreqTimes(
+ std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) {
+ if (!gInitialized && !initGlobals()) return false;
+
+ int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times");
+ if (fd < 0) return false;
+ BpfMap<time_key_t, val_t> m(fd);
+
+ std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ std::unordered_map<uint32_t, uint32_t> freqIdxs;
+ for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j;
+ policyFreqIdxs.emplace_back(freqIdxs);
+ }
+
+ auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
+ const BpfMap<time_key_t, val_t> &) {
+ if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
+ std::vector<std::vector<uint64_t>> v;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0);
+ v.emplace_back(v2);
+ }
+ (*freqTimeMap)[key.uid] = v;
+ }
+
+ for (size_t policy = 0; policy < gNPolicies; ++policy) {
+ for (const auto &cpu : gPolicyCpus[policy]) {
+ uint32_t cpuTime = val.ar[cpu];
+ if (cpuTime == 0) continue;
+ auto freqIdx = policyFreqIdxs[policy][key.freq];
+ (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime;
+ }
+ }
+ return android::netdutils::status::ok;
+ };
+ return isOk(m.iterateWithValue(fn));
+}
+
+// Clear all time in state data for a given uid. Returns false on error, true otherwise.
+bool clearUidCpuFreqTimes(uint32_t uid) {
+ if (!gInitialized && !initGlobals()) return false;
+ time_key_t key = {.uid = uid, .freq = 0};
+
+ std::vector<uint32_t> idxs(gNPolicies, 0);
+ for (auto freq : gAllFreqs) {
+ key.freq = freq;
+ if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+ }
+ return true;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
new file mode 100644
index 0000000..0205452
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace bpf {
+
+bool startTrackingUidCpuFreqTimes();
+bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes);
+bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap);
+bool clearUidCpuFreqTimes(unsigned int uid);
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
new file mode 100644
index 0000000..9837865
--- /dev/null
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -0,0 +1,58 @@
+
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <cputimeinstate.h>
+
+namespace android {
+namespace bpf {
+
+using std::vector;
+
+TEST(TimeInStateTest, SingleUid) {
+ vector<vector<uint64_t>> times;
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
+ EXPECT_FALSE(times.empty());
+}
+
+TEST(TimeInStateTest, AllUid) {
+ vector<size_t> sizes;
+ std::unordered_map<uint32_t, vector<vector<uint64_t>>> map;
+ ASSERT_TRUE(getUidsCpuFreqTimes(&map));
+
+ ASSERT_FALSE(map.empty());
+
+ auto firstEntry = map.begin()->second;
+ for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+
+ for (const auto &vec : map) {
+ ASSERT_EQ(vec.second.size(), sizes.size());
+ for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+ }
+}
+
+TEST(TimeInStateTest, RemoveUid) {
+ vector<vector<uint64_t>> times, times2;
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
+ ASSERT_FALSE(times.empty());
+
+ uint64_t sum = 0;
+ for (size_t i = 0; i < times.size(); ++i) {
+ for (auto x : times[i]) sum += x;
+ }
+ ASSERT_GT(sum, (uint64_t)0);
+
+ ASSERT_TRUE(clearUidCpuFreqTimes(0));
+
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×2));
+ ASSERT_EQ(times2.size(), times.size());
+ for (size_t i = 0; i < times.size(); ++i) {
+ ASSERT_EQ(times2[i].size(), times[i].size());
+ for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]);
+ }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 280c14a..d940752 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -17,14 +17,17 @@
srcs: [
"GraphicsEnv.cpp",
+ "IGpuService.cpp"
],
cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
+ "libutils",
],
export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 9dc7431..75fe2d3 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
//#define LOG_NDEBUG 1
#define LOG_TAG "GraphicsEnv"
+
#include <graphicsenv/GraphicsEnv.h>
#include <dlfcn.h>
@@ -25,16 +28,16 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/dlext.h>
+#include <binder/IServiceManager.h>
#include <cutils/properties.h>
+#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <sys/prctl.h>
+#include <utils/Trace.h>
#include <memory>
-#include <mutex>
#include <string>
-#include <dlfcn.h>
-
// TODO(b/37049319) Get this from a header once one exists
extern "C" {
android_namespace_t* android_get_exported_namespace(const char*);
@@ -153,6 +156,53 @@
mDriverPath = path;
}
+void GraphicsEnv::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName,
+ const uint64_t driverVersionCode, const std::string& appPackageName) {
+ ATRACE_CALL();
+
+ ALOGV("setGpuStats:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%llu]\n"
+ "\tappPackageName[%s]\n",
+ driverPackageName.c_str(), driverVersionName.c_str(),
+ (unsigned long long)driverVersionCode, appPackageName.c_str());
+
+ mGpuStats = {
+ .driverPackageName = driverPackageName,
+ .driverVersionName = driverVersionName,
+ .driverVersionCode = driverVersionCode,
+ .appPackageName = appPackageName,
+ };
+}
+
+void GraphicsEnv::sendGpuStats() {
+ ATRACE_CALL();
+
+ // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
+ if (mGpuStats.appPackageName.empty()) return;
+
+ ALOGV("sendGpuStats:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%llu]\n"
+ "\tappPackageName[%s]\n",
+ mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(),
+ (unsigned long long)mGpuStats.driverVersionCode, mGpuStats.appPackageName.c_str());
+
+ const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
+ if (!binder) {
+ ALOGE("Failed to get gpu service for [%s]", mGpuStats.appPackageName.c_str());
+ return;
+ }
+
+ interface_cast<IGpuService>(binder)->setGpuStats(mGpuStats.driverPackageName,
+ mGpuStats.driverVersionName,
+ mGpuStats.driverVersionCode,
+ mGpuStats.appPackageName);
+}
+
void* GraphicsEnv::loadLibrary(std::string name) {
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
@@ -262,41 +312,32 @@
bool GraphicsEnv::shouldUseAngle() {
// Make sure we are init'ed
if (mAngleAppName.empty()) {
- ALOGE("App name is empty. setAngleInfo() must be called first to enable ANGLE.");
+ ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
return false;
}
- return mUseAngle;
+ return (mUseAngle == YES) ? true : false;
}
void GraphicsEnv::updateUseAngle() {
- mUseAngle = false;
+ mUseAngle = NO;
const char* ANGLE_PREFER_ANGLE = "angle";
const char* ANGLE_PREFER_NATIVE = "native";
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
ALOGV("User set \"Developer Options\" to force the use of ANGLE");
- mUseAngle = true;
+ mUseAngle = YES;
} else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
ALOGV("User set \"Developer Options\" to force the use of Native");
- mUseAngle = false;
+ mUseAngle = NO;
} else {
// The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily
// load ANGLE and call the updatable opt-in/out logic:
-
- // Check if ANGLE is enabled. Workaround for several bugs:
- // b/119305693 b/119322355 b/119305887
- // Something is not working correctly in the feature library
- char prop[PROPERTY_VALUE_MAX];
- property_get("debug.angle.enable", prop, "0");
- void* featureSo = nullptr;
- if (atoi(prop)) {
- featureSo = loadLibrary("feature_support");
- }
+ void* featureSo = loadLibrary("feature_support");
if (featureSo) {
ALOGV("loaded ANGLE's opt-in/out logic from namespace");
- mUseAngle = checkAngleRules(featureSo);
+ mUseAngle = checkAngleRules(featureSo) ? YES : NO;
dlclose(featureSo);
featureSo = nullptr;
} else {
@@ -308,6 +349,13 @@
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
const std::string developerOptIn, const int rulesFd,
const long rulesOffset, const long rulesLength) {
+ if (mUseAngle != UNKNOWN) {
+ // We've already figured out an answer for this app, so just return.
+ ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
+ (mUseAngle == YES) ? "true" : "false");
+ return;
+ }
+
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
ALOGV("setting ANGLE app name to '%s'", appName.c_str());
@@ -415,19 +463,26 @@
}
android_namespace_t* GraphicsEnv::getAngleNamespace() {
- static std::once_flag once;
- std::call_once(once, [this]() {
- if (mAnglePath.empty()) return;
+ std::lock_guard<std::mutex> lock(mNamespaceMutex);
- mAngleNamespace = android_create_namespace("ANGLE",
- nullptr, // ld_library_path
- mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
- nullptr, // permitted_when_isolated_path
- nullptr);
- if (!mAngleNamespace) ALOGD("Could not create ANGLE namespace from default");
- });
+ if (mAngleNamespace) {
+ return mAngleNamespace;
+ }
+
+ if (mAnglePath.empty()) {
+ ALOGV("mAnglePath is empty, not creating ANGLE namespace");
+ return nullptr;
+ }
+
+ mAngleNamespace = android_create_namespace("ANGLE",
+ nullptr, // ld_library_path
+ mAnglePath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_SHARED |
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ nullptr);
+
+ ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
return mAngleNamespace;
}
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
new file mode 100644
index 0000000..762a27b
--- /dev/null
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GpuService"
+
+#include <graphicsenv/IGpuService.h>
+
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class BpGpuService : public BpInterface<IGpuService> {
+public:
+ explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
+
+ virtual void setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ data.writeUtf8AsUtf16(driverPackageName);
+ data.writeUtf8AsUtf16(driverVersionName);
+ data.writeUint64(driverVersionCode);
+ data.writeUtf8AsUtf16(appPackageName);
+
+ remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
+
+status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ ALOGV("onTransact code[0x%X]", code);
+
+ status_t status;
+ switch (code) {
+ case SET_GPU_STATS: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPackageName;
+ if ((status = data.readUtf8FromUtf16(&driverPackageName)) != OK) return status;
+
+ std::string driverVersionName;
+ if ((status = data.readUtf8FromUtf16(&driverVersionName)) != OK) return status;
+
+ uint64_t driverVersionCode;
+ if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+ std::string appPackageName;
+ if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+ setGpuStats(driverPackageName, driverVersionName, driverVersionCode, appPackageName);
+
+ return OK;
+ }
+ case SHELL_COMMAND_TRANSACTION: {
+ int in = data.readFileDescriptor();
+ int out = data.readFileDescriptor();
+ int err = data.readFileDescriptor();
+
+ std::vector<String16> args;
+ data.readString16Vector(&args);
+
+ sp<IBinder> unusedCallback;
+ if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK) return status;
+
+ sp<IResultReceiver> resultReceiver;
+ if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK) return status;
+
+ status = shellCommand(in, out, err, args);
+ if (resultReceiver != nullptr) resultReceiver->send(status);
+
+ return OK;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 429ce0e..c4482b7 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_GRAPHICS_ENV_H
#define ANDROID_UI_GRAPHICS_ENV_H 1
+#include <mutex>
#include <string>
#include <vector>
@@ -27,6 +28,13 @@
struct NativeLoaderNamespace;
class GraphicsEnv {
+ struct GpuStats {
+ std::string driverPackageName;
+ std::string driverVersionName;
+ uint64_t driverVersionCode;
+ std::string appPackageName;
+ };
+
public:
static GraphicsEnv& getInstance();
@@ -39,6 +47,9 @@
// /data/app/com.example.driver/base.apk!/lib/arm64-v8a
void setDriverPath(const std::string path);
android_namespace_t* getDriverNamespace();
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ const uint64_t versionCode, const std::string& appPackageName);
+ void sendGpuStats();
bool shouldUseAngle(std::string appName);
bool shouldUseAngle();
@@ -63,20 +74,24 @@
const std::string& getDebugLayersGLES();
private:
+ enum UseAngle { UNKNOWN, YES, NO };
+
void* loadLibrary(std::string name);
bool checkAngleRules(void* so);
void updateUseAngle();
GraphicsEnv() = default;
std::string mDriverPath;
+ GpuStats mGpuStats;
std::string mAnglePath;
std::string mAngleAppName;
std::string mAngleDeveloperOptIn;
std::vector<char> mRulesBuffer;
- bool mUseAngle;
+ UseAngle mUseAngle = UNKNOWN;
std::string mDebugLayers;
std::string mDebugLayersGLES;
std::string mLayerPaths;
+ std::mutex mNamespaceMutex;
android_namespace_t* mDriverNamespace = nullptr;
android_namespace_t* mAngleNamespace = nullptr;
NativeLoaderNamespace* mAppNamespace = nullptr;
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
new file mode 100644
index 0000000..1e74d60
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IInterface.h>
+#include <cutils/compiler.h>
+
+#include <vector>
+
+namespace android {
+
+/*
+ * This class defines the Binder IPC interface for GPU-related queries and
+ * control.
+ */
+class IGpuService : public IInterface {
+public:
+ DECLARE_META_INTERFACE(GpuService);
+
+ // set GPU stats from GraphicsEnvironment.
+ virtual void setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) = 0;
+};
+
+class BnGpuService : public BnInterface<IGpuService> {
+public:
+ enum IGpuServiceTag {
+ SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
+ // Always append new enum to the end.
+ };
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+
+protected:
+ virtual status_t shellCommand(int in, int out, int err, std::vector<String16>& args) = 0;
+};
+
+} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3521e89..0510492 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -104,10 +104,12 @@
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
+ "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
"LayerDebugInfo.cpp",
+ "LayerMetadata.cpp",
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
@@ -141,8 +143,6 @@
"libhidltransport",
"android.hidl.token@1.0-utils",
"android.hardware.graphics.bufferqueue@1.0",
- "android.hardware.configstore@1.0",
- "android.hardware.configstore-utils",
],
// bufferhub is not used when building libgui for vendors
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
new file mode 100644
index 0000000..40cbfce
--- /dev/null
+++ b/libs/gui/IRegionSamplingListener.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IRegionSamplingListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/IRegionSamplingListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
+ LAST = ON_SAMPLE_COLLECTED,
+};
+
+} // Anonymous namespace
+
+class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
+public:
+ explicit BpRegionSamplingListener(const sp<IBinder>& impl)
+ : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
+
+ ~BpRegionSamplingListener() override;
+
+ void onSampleCollected(float medianLuma) override {
+ callRemoteAsync<decltype(
+ &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
+ }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpRegionSamplingListener::~BpRegionSamplingListener() = default;
+
+IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
+
+status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_SAMPLE_COLLECTED:
+ return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
+ }
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ad2dc14..f77eeb2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -275,12 +276,25 @@
remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply);
}
- virtual sp<IBinder> getBuiltInDisplay(int32_t id)
- {
+ virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeInt32(id);
- remote()->transact(BnSurfaceComposer::GET_BUILT_IN_DISPLAY, data, &reply);
+ if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) ==
+ NO_ERROR) {
+ std::vector<PhysicalDisplayId> displayIds;
+ if (reply.readUint64Vector(&displayIds) == NO_ERROR) {
+ return displayIds;
+ }
+ }
+
+ return {};
+ }
+
+ virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeUint64(displayId);
+ remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply);
return reply.readStrongBinder();
}
@@ -395,6 +409,32 @@
return result;
}
+ virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& primaries) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)),
+ sizeof(ui::DisplayPrimaries));
+ }
+ return result;
+ }
+
virtual ColorMode getActiveColorMode(const sp<IBinder>& display) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -687,49 +727,84 @@
virtual status_t getProtectedContentSupport(bool* outSupported) const {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
- bool result;
- status_t err = reply.readBool(&result);
- if (err == NO_ERROR) {
- *outSupported = result;
+ status_t error =
+ remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
}
- return err;
+ error = reply.readBool(outSupported);
+ return error;
}
- virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
- int32_t* outBufferId) {
+ virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+ bool* outIsWideColorDisplay) const {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-
- data.writeStrongBinder(token);
- if (buffer) {
- data.writeBool(true);
- data.write(*buffer);
- } else {
- data.writeBool(false);
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = data.writeStrongBinder(token);
+ if (error != NO_ERROR) {
+ return error;
}
- status_t result = remote()->transact(BnSurfaceComposer::CACHE_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
+ error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
}
-
- int32_t id = -1;
- result = reply.readInt32(&id);
- if (result == NO_ERROR) {
- *outBufferId = id;
- }
- return result;
+ error = reply.readBool(outIsWideColorDisplay);
+ return error;
}
- virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.write(samplingArea);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write sampling area");
+ return error;
+ }
+ error = data.writeStrongBinder(stopLayerHandle);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write stop layer handle");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to transact");
+ }
+ return error;
+ }
- data.writeStrongBinder(token);
- data.writeInt32(bufferId);
-
- return remote()->transact(BnSurfaceComposer::UNCACHE_BUFFER, data, &reply);
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data,
+ &reply);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to transact");
+ }
+ return error;
}
};
@@ -886,10 +961,10 @@
destroyDisplay(display);
return NO_ERROR;
}
- case GET_BUILT_IN_DISPLAY: {
+ case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t id = data.readInt32();
- sp<IBinder> display(getBuiltInDisplay(id));
+ PhysicalDisplayId displayId = data.readUint64();
+ sp<IBinder> display = getPhysicalDisplayToken(displayId);
reply->writeStrongBinder(display);
return NO_ERROR;
}
@@ -954,6 +1029,26 @@
}
return NO_ERROR;
}
+ case GET_DISPLAY_NATIVE_PRIMARIES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ ui::DisplayPrimaries primaries;
+ sp<IBinder> display = nullptr;
+
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result);
+ return result;
+ }
+
+ result = getDisplayNativePrimaries(display, primaries);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries,
+ sizeof(ui::DisplayPrimaries));
+ }
+
+ return NO_ERROR;
+ }
case GET_ACTIVE_COLOR_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1172,47 +1267,55 @@
}
return error;
}
- case CACHE_BUFFER: {
+ case IS_WIDE_COLOR_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> token;
- status_t result = data.readStrongBinder(&token);
- if (result != NO_ERROR) {
- ALOGE("cache buffer failure in reading token: %d", result);
- return result;
+ sp<IBinder> display = nullptr;
+ status_t error = data.readStrongBinder(&display);
+ if (error != NO_ERROR) {
+ return error;
}
-
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- if (data.readBool()) {
- result = data.read(*buffer);
- if (result != NO_ERROR) {
- ALOGE("cache buffer failure in reading buffer: %d", result);
- return result;
- }
- }
- int32_t bufferId = -1;
- status_t error = cacheBuffer(token, buffer, &bufferId);
+ bool result;
+ error = isWideColorDisplay(display, &result);
if (error == NO_ERROR) {
- reply->writeInt32(bufferId);
+ reply->writeBool(result);
}
return error;
}
- case UNCACHE_BUFFER: {
+ case GET_PHYSICAL_DISPLAY_IDS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> token;
- status_t result = data.readStrongBinder(&token);
+ return reply->writeUint64Vector(getPhysicalDisplayIds());
+ }
+ case ADD_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ Rect samplingArea;
+ status_t result = data.read(samplingArea);
if (result != NO_ERROR) {
- ALOGE("uncache buffer failure in reading token: %d", result);
+ ALOGE("addRegionSamplingListener: Failed to read sampling area");
return result;
}
-
- int32_t bufferId = -1;
- result = data.readInt32(&bufferId);
+ sp<IBinder> stopLayerHandle;
+ result = data.readNullableStrongBinder(&stopLayerHandle);
if (result != NO_ERROR) {
- ALOGE("uncache buffer failure in reading buffer id: %d", result);
+ ALOGE("addRegionSamplingListener: Failed to read stop layer handle");
return result;
}
-
- return uncacheBuffer(token, bufferId);
+ sp<IRegionSamplingListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return addRegionSamplingListener(samplingArea, stopLayerHandle, listener);
+ }
+ case REMOVE_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IRegionSamplingListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return removeRegionSamplingListener(listener);
}
default: {
return BBinder::onTransact(code, data, reply, flags);
@@ -1220,6 +1323,4 @@
}
}
-// ----------------------------------------------------------------------------
-
-};
+} // namespace android
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 928ef95..129558b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -47,26 +47,26 @@
~BpSurfaceComposerClient() override;
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
- int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) override {
+ uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
- windowType, ownerUid,
+ std::move(metadata),
handle, gbp);
}
status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
PixelFormat format, uint32_t flags,
- const sp<IGraphicBufferProducer>& parent, int32_t windowType,
- int32_t ownerUid, sp<IBinder>* handle,
+ const sp<IGraphicBufferProducer>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) override {
return callRemote<decltype(
&ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
name, width, height, format,
- flags, parent, windowType,
- ownerUid, handle, gbp);
+ flags, parent,
+ std::move(metadata), handle,
+ gbp);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
new file mode 100644
index 0000000..745433a
--- /dev/null
+++ b/libs/gui/LayerMetadata.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <gui/LayerMetadata.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+LayerMetadata::LayerMetadata() = default;
+
+LayerMetadata::LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map)
+ : mMap(std::move(map)) {}
+
+LayerMetadata::LayerMetadata(const LayerMetadata& other) = default;
+
+LayerMetadata::LayerMetadata(LayerMetadata&& other) = default;
+
+void LayerMetadata::merge(const LayerMetadata& other) {
+ for (const auto& entry : other.mMap) {
+ mMap[entry.first] = entry.second;
+ }
+}
+
+status_t LayerMetadata::writeToParcel(Parcel* parcel) const {
+ parcel->writeInt32(static_cast<int>(mMap.size()));
+ status_t status = OK;
+ for (const auto& entry : mMap) {
+ status = parcel->writeUint32(entry.first);
+ if (status != OK) {
+ break;
+ }
+ status = parcel->writeByteVector(entry.second);
+ if (status != OK) {
+ break;
+ }
+ }
+ return status;
+}
+
+status_t LayerMetadata::readFromParcel(const Parcel* parcel) {
+ int size = parcel->readInt32();
+ status_t status = OK;
+ mMap.clear();
+ for (int i = 0; i < size; ++i) {
+ uint32_t key = parcel->readUint32();
+ status = parcel->readByteVector(&mMap[key]);
+ if (status != OK) {
+ break;
+ }
+ }
+ return status;
+}
+
+LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) {
+ mMap = other.mMap;
+ return *this;
+}
+
+LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) {
+ mMap = std::move(other.mMap);
+ return *this;
+}
+
+bool LayerMetadata::has(uint32_t key) const {
+ return mMap.count(key);
+}
+
+int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const {
+ if (!has(key)) return fallback;
+ const std::vector<uint8_t>& data = mMap.at(key);
+ if (data.size() < sizeof(uint32_t)) return fallback;
+ Parcel p;
+ p.setData(data.data(), data.size());
+ return p.readInt32();
+}
+
+void LayerMetadata::setInt32(uint32_t key, int32_t value) {
+ std::vector<uint8_t>& data = mMap[key];
+ Parcel p;
+ p.writeInt32(value);
+ data.resize(p.dataSize());
+ memcpy(data.data(), p.data(), p.dataSize());
+}
+
+std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
+ if (!has(key)) return std::string();
+ switch (key) {
+ case METADATA_OWNER_UID:
+ return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
+ case METADATA_WINDOW_TYPE:
+ return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
+ case METADATA_TASK_ID:
+ return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+ default:
+ return StringPrintf("%d%s%dbytes", key, separator,
+ static_cast<int>(mMap.at(key).size()));
+ }
+}
+
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 40b55fa..206bc30 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -96,6 +96,10 @@
output.writeStrongBinder(cachedBuffer.token);
output.writeInt32(cachedBuffer.bufferId);
+ output.writeParcelable(metadata);
+
+ output.writeFloat(bgColorAlpha);
+ output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
return NO_ERROR;
}
@@ -169,6 +173,10 @@
cachedBuffer.token = input.readStrongBinder();
cachedBuffer.bufferId = input.readInt32();
+ input.readParcelable(&metadata);
+
+ bgColorAlpha = input.readFloat();
+ bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
return NO_ERROR;
}
@@ -382,6 +390,16 @@
what |= eCachedBufferChanged;
cachedBuffer = other.cachedBuffer;
}
+ if (other.what & eBackgroundColorChanged) {
+ what |= eBackgroundColorChanged;
+ color = other.color;
+ bgColorAlpha = other.bgColorAlpha;
+ bgColorDataspace = other.bgColorDataspace;
+ }
+ if (other.what & eMetadataChanged) {
+ what |= eMetadataChanged;
+ metadata.merge(other.metadata);
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 00e23f0..3affa23 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -39,9 +39,6 @@
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
namespace android {
using ui::ColorMode;
@@ -321,48 +318,27 @@
return NO_ERROR;
}
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
status_t Surface::getWideColorSupport(bool* supported) {
ATRACE_CALL();
- sp<IBinder> display(
- composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- Vector<ColorMode> colorModes;
- status_t err =
- composerService()->getDisplayColorModes(display, &colorModes);
-
- if (err)
- return err;
-
- bool wideColorBoardConfig =
- getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
-
- *supported = false;
- for (ColorMode colorMode : colorModes) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::ADOBE_RGB:
- case ColorMode::DCI_P3:
- if (wideColorBoardConfig) {
- *supported = true;
- }
- break;
- default:
- break;
- }
+ const sp<IBinder> display = composerService()->getInternalDisplayToken();
+ if (display == nullptr) {
+ return NAME_NOT_FOUND;
}
- return NO_ERROR;
+ *supported = false;
+ status_t error = composerService()->isWideColorDisplay(display, supported);
+ return error;
}
status_t Surface::getHdrSupport(bool* supported) {
ATRACE_CALL();
- sp<IBinder> display(
- composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const sp<IBinder> display = composerService()->getInternalDisplayToken();
+ if (display == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
HdrCapabilities hdrCapabilities;
status_t err =
composerService()->getHdrCapabilities(display, &hdrCapabilities);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a82054b..46b9128 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -48,6 +48,9 @@
#include <private/gui/ComposerService.h>
+// This server size should always be smaller than the server cache size
+#define BUFFER_CACHE_MAX_SIZE 64
+
namespace android {
using ui::ColorMode;
@@ -102,6 +105,27 @@
mDeathObserver = nullptr;
}
+class DefaultComposerClient: public Singleton<DefaultComposerClient> {
+ Mutex mLock;
+ sp<SurfaceComposerClient> mClient;
+ friend class Singleton<ComposerService>;
+public:
+ static sp<SurfaceComposerClient> getComposerClient() {
+ DefaultComposerClient& dc = DefaultComposerClient::getInstance();
+ Mutex::Autolock _l(dc.mLock);
+ if (dc.mClient == nullptr) {
+ dc.mClient = new SurfaceComposerClient;
+ }
+ return dc.mClient;
+ }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient);
+
+
+sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
+ return DefaultComposerClient::getComposerClient();
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -132,26 +156,76 @@
mListening = true;
}
-CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) {
+CallbackId TransactionCompletedListener::addCallbackFunction(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls) {
std::lock_guard<std::mutex> lock(mMutex);
startListeningLocked();
CallbackId callbackId = getNextIdLocked();
- mCallbacks.emplace(callbackId, callback);
+ mCallbacks[callbackId].callbackFunction = callbackFunction;
+
+ auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
+
+ for (const auto& surfaceControl : surfaceControls) {
+ callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
+ }
+
return callbackId;
}
+void TransactionCompletedListener::addSurfaceControlToCallbacks(
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId>& callbackIds) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ for (auto callbackId : callbackIds) {
+ mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
+ std::forward_as_tuple(
+ surfaceControl->getHandle()),
+ std::forward_as_tuple(surfaceControl));
+ }
+}
+
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::lock_guard lock(mMutex);
+ /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+ * callbackIds, except for when Transactions are merged together. This probably cannot be
+ * solved before this point because the Transactions could be merged together and applied in a
+ * different process.
+ *
+ * Fortunately, we get all the callbacks for this listener for the same frame together at the
+ * same time. This means if any Transactions were merged together, we will get their callbacks
+ * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
+ * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
+ * that could possibly exist for the callbacks.
+ */
+ std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
for (auto callbackId : callbackIds) {
- const auto& callback = mCallbacks[callbackId];
- if (!callback) {
+ auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+ }
+ }
+
+ for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+ for (auto callbackId : callbackIds) {
+ auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
continue;
}
- callback(transactionStats);
+ std::vector<SurfaceControlStats> surfaceControlStats;
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
+ surfaceStats.acquireTime,
+ surfaceStats.previousReleaseFence);
+ }
+
+ callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+ surfaceControlStats);
mCallbacks.erase(callbackId);
}
}
@@ -159,6 +233,113 @@
// ---------------------------------------------------------------------------
+class BufferCache : public Singleton<BufferCache> {
+public:
+ BufferCache() : token(new BBinder()) {}
+
+ sp<IBinder> getToken() {
+ return IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ }
+
+ int32_t getId(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard lock(mMutex);
+
+ auto itr = mBuffers.find(buffer);
+ if (itr == mBuffers.end()) {
+ return -1;
+ }
+ itr->second.counter = getCounter();
+ return itr->second.id;
+ }
+
+ int32_t cache(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard lock(mMutex);
+
+ int32_t bufferId = getNextAvailableId();
+
+ mBuffers[buffer].id = bufferId;
+ mBuffers[buffer].counter = getCounter();
+ return bufferId;
+ }
+
+private:
+ int32_t evictDestroyedBuffer() REQUIRES(mMutex) {
+ auto itr = mBuffers.begin();
+ while (itr != mBuffers.end()) {
+ auto& buffer = itr->first;
+ if (buffer == nullptr || buffer.promote() == nullptr) {
+ int32_t bufferId = itr->second.id;
+ mBuffers.erase(itr);
+ return bufferId;
+ }
+ itr++;
+ }
+ return -1;
+ }
+
+ int32_t evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+ if (mBuffers.size() < 0) {
+ return -1;
+ }
+ auto itr = mBuffers.begin();
+ uint64_t minCounter = itr->second.counter;
+ auto minBuffer = itr;
+ itr++;
+
+ while (itr != mBuffers.end()) {
+ uint64_t counter = itr->second.counter;
+ if (counter < minCounter) {
+ minCounter = counter;
+ minBuffer = itr;
+ }
+ itr++;
+ }
+ int32_t minBufferId = minBuffer->second.id;
+ mBuffers.erase(minBuffer);
+ return minBufferId;
+ }
+
+ int32_t getNextAvailableId() REQUIRES(mMutex) {
+ static int32_t id = 0;
+ if (id + 1 < BUFFER_CACHE_MAX_SIZE) {
+ return id++;
+ }
+
+ // There are no more valid cache ids. To set additional buffers, evict existing buffers
+ // and reuse their cache ids.
+ int32_t bufferId = evictDestroyedBuffer();
+ if (bufferId > 0) {
+ return bufferId;
+ }
+ return evictLeastRecentlyUsedBuffer();
+ }
+
+ uint64_t getCounter() REQUIRES(mMutex) {
+ static uint64_t counter = 0;
+ return counter++;
+ }
+
+ struct Metadata {
+ // The cache id of a buffer that can be set to ISurfaceComposer. When ISurfaceComposer
+ // recieves this id, it can retrieve the buffer from its cache. Caching GraphicBuffers
+ // is important because sending them across processes is expensive.
+ int32_t id = 0;
+ // When a buffer is set, a counter is incremented and stored in the cache's metadata.
+ // When an buffer must be evicted, the entry with the lowest counter value is chosen.
+ uint64_t counter = 0;
+ };
+
+ std::mutex mMutex;
+ std::map<wp<GraphicBuffer>, Metadata> mBuffers GUARDED_BY(mMutex);
+
+ // Used by ISurfaceComposer to identify which process is sending the cached buffer.
+ sp<IBinder> token;
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
+
+// ---------------------------------------------------------------------------
+
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mForceSynchronous(other.mForceSynchronous),
mTransactionNestCount(other.mTransactionNestCount),
@@ -303,8 +484,20 @@
return ComposerService::getComposerService()->destroyDisplay(display);
}
-sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) {
- return ComposerService::getComposerService()->getBuiltInDisplay(id);
+std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() {
+ return ComposerService::getComposerService()->getPhysicalDisplayIds();
+}
+
+std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
+ return ComposerService::getComposerService()->getInternalDisplayId();
+}
+
+sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) {
+ return ComposerService::getComposerService()->getPhysicalDisplayToken(displayId);
+}
+
+sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() {
+ return ComposerService::getComposerService()->getInternalDisplayToken();
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
@@ -329,7 +522,11 @@
void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
const sp<SurfaceControl>& sc) {
- mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc);
+ auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+ callbackInfo.surfaceControls.insert(sc);
+
+ TransactionCompletedListener::getInstance()
+ ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
@@ -464,6 +661,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata(
+ const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eMetadataChanged;
+ s->metadata.mMap[key] = std::move(data);
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatrix(
const sp<SurfaceControl>& sc, float dsdx, float dtdx,
float dtdy, float dsdy) {
@@ -589,6 +800,23 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundColor(
+ const sp<SurfaceControl>& sc, const half3& color, float alpha, ui::Dataspace dataspace) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBackgroundColorChanged;
+ s->color = color;
+ s->bgColorAlpha = alpha;
+ s->bgColorDataspace = dataspace;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform(
const sp<SurfaceControl>& sc, uint32_t transform) {
layer_state_t* s = getLayerState(sc);
@@ -653,22 +881,17 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eBufferChanged;
- s->buffer = buffer;
- registerSurfaceControlForCallback(sc);
- return *this;
-}
+ int32_t bufferId = BufferCache::getInstance().getId(buffer);
+ if (bufferId < 0) {
+ bufferId = BufferCache::getInstance().cache(buffer);
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachedBuffer(
- const sp<SurfaceControl>& sc, int32_t bufferId) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
+ s->what |= layer_state_t::eBufferChanged;
+ s->buffer = buffer;
}
+
s->what |= layer_state_t::eCachedBufferChanged;
- s->cachedBuffer.token = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ s->cachedBuffer.token = BufferCache::getInstance().getToken();
s->cachedBuffer.bufferId = bufferId;
registerSurfaceControlForCallback(sc);
@@ -770,9 +993,12 @@
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
auto listener = TransactionCompletedListener::getInstance();
- auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1);
+ auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ const auto& surfaceControls =
+ mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
- CallbackId callbackId = listener->addCallback(callbackWithContext);
+ CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
callbackId);
@@ -872,6 +1098,54 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
+ const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
+ setCrop_legacy(sc, source);
+
+ int x = dst.left;
+ int y = dst.top;
+ float xScale = dst.getWidth() / static_cast<float>(source.getWidth());
+ float yScale = dst.getHeight() / static_cast<float>(source.getHeight());
+ float matrix[4] = {1, 0, 0, 1};
+
+ switch (transform) {
+ case NATIVE_WINDOW_TRANSFORM_FLIP_H:
+ matrix[0] = -xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = yScale;
+ x += source.getWidth();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_FLIP_V:
+ matrix[0] = xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = -yScale;
+ y += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_90:
+ matrix[0] = 0; matrix[1] = -yScale;
+ matrix[2] = xScale; matrix[3] = 0;
+ x += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_180:
+ matrix[0] = -xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = -yScale;
+ x += source.getWidth();
+ y += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_270:
+ matrix[0] = 0; matrix[1] = yScale;
+ matrix[2] = -xScale; matrix[3] = 0;
+ y += source.getWidth();
+ break;
+ default:
+ matrix[0] = xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = yScale;
+ break;
+ }
+ setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
+ setPosition(sc, x, y);
+
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -985,26 +1259,19 @@
mStatus = NO_INIT;
}
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
- const String8& name,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- uint32_t flags,
- SurfaceControl* parent,
- int32_t windowType,
- int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags,
+ SurfaceControl* parent,
+ LayerMetadata metadata) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid);
+ createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
return s;
}
sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
uint32_t h, PixelFormat format,
uint32_t flags, Surface* parent,
- int32_t windowType,
- int32_t ownerUid) {
+ LayerMetadata metadata) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1013,8 +1280,8 @@
sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
sp<IGraphicBufferProducer> gbp;
- err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, windowType,
- ownerUid, &handle, &gbp);
+ err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
+ std::move(metadata), &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
if (err == NO_ERROR) {
return new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -1023,17 +1290,11 @@
return nullptr;
}
-status_t SurfaceComposerClient::createSurfaceChecked(
- const String8& name,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- sp<SurfaceControl>* outSurface,
- uint32_t flags,
- SurfaceControl* parent,
- int32_t windowType,
- int32_t ownerUid)
-{
+status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format,
+ sp<SurfaceControl>* outSurface, uint32_t flags,
+ SurfaceControl* parent,
+ LayerMetadata metadata) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -1045,8 +1306,9 @@
if (parent != nullptr) {
parentHandle = parent->getHandle();
}
- err = mClient->createSurface(name, w, h, format, flags, parentHandle,
- windowType, ownerUid, &handle, &gbp);
+
+ err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
+ &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
*outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -1072,26 +1334,6 @@
// ----------------------------------------------------------------------------
-status_t SurfaceComposerClient::cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- if (buffer == nullptr || outBufferId == nullptr) {
- return BAD_VALUE;
- }
- return sf->cacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
- buffer, outBufferId);
-}
-
-status_t SurfaceComposerClient::uncacheBuffer(int32_t bufferId) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- if (bufferId < 0) {
- return BAD_VALUE;
- }
- return sf->uncacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
- bufferId);
-}
-
-// ----------------------------------------------------------------------------
-
status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
return sf->enableVSyncInjections(enable);
@@ -1139,6 +1381,11 @@
return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
}
+status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& outPrimaries) {
+ return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries);
+}
+
ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
return ComposerService::getComposerService()->getActiveColorMode(display);
}
@@ -1205,6 +1452,12 @@
timestamp, outStats);
}
+status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display,
+ bool* outIsWideColorDisplay) {
+ return ComposerService::getComposerService()->isWideColorDisplay(display,
+ outIsWideColorDisplay);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 8e500a4..55488da 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -63,23 +63,25 @@
SurfaceControl::~SurfaceControl()
{
+ // Avoid reparenting the server-side surface to null if we are not the owner of it,
+ // meaning that we retrieved it from another process.
if (mClient != nullptr && mHandle != nullptr && mOwned) {
SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
}
- mClient.clear();
- mHandle.clear();
- mGraphicBufferProducer.clear();
- IPCThreadState::self()->flushCommands();
+ release();
}
void SurfaceControl::destroy()
{
- // Avoid destroying the server-side surface if we are not the owner of it, meaning that we
- // retrieved it from another process.
- if (isValid() && mOwned) {
+ if (isValid()) {
SurfaceComposerClient::Transaction().reparent(this, nullptr).apply();
}
- // clear all references and trigger an IPC now, to make sure things
+ release();
+}
+
+void SurfaceControl::release()
+{
+ // Trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
mClient.clear();
mHandle.clear();
@@ -87,17 +89,6 @@
IPCThreadState::self()->flushCommands();
}
-void SurfaceControl::clear()
-{
- // here, the window manager tells us explicitly that we should destroy
- // the surface's resource. Soon after this call, it will also release
- // its last reference (which will call the dtor); however, it is possible
- // that a client living in the same process still holds references which
- // would delay the call to the dtor -- that is why we need this explicit
- // "clear()" call.
- destroy();
-}
-
void SurfaceControl::disconnect() {
if (mGraphicBufferProducer != nullptr) {
mGraphicBufferProducer->disconnect(
@@ -177,6 +168,12 @@
return mHandle;
}
+sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
+{
+ Mutex::Autolock _l(mLock);
+ return mGraphicBufferProducer;
+}
+
sp<SurfaceComposerClient> SurfaceControl::getClient() const
{
return mClient;
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a4102df..8c3f463 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -58,7 +58,7 @@
struct Header {
uint32_t type;
- uint32_t id;
+ PhysicalDisplayId displayId;
nsecs_t timestamp __attribute__((aligned(8)));
};
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
new file mode 100644
index 0000000..1803d9a
--- /dev/null
+++ b/libs/gui/include/gui/IRegionSamplingListener.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+class IRegionSamplingListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(RegionSamplingListener)
+
+ virtual void onSampleCollected(float medianLuma) = 0;
+};
+
+class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
+public:
+ BnRegionSamplingListener()
+ : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 418d5fb..1a0b6bb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,12 +27,14 @@
#include <binder/IInterface.h>
+#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <optional>
#include <vector>
namespace android {
@@ -48,6 +50,7 @@
class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
+class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
@@ -70,11 +73,6 @@
eEarlyWakeup = 0x04
};
- enum {
- eDisplayIdMain = 0,
- eDisplayIdHdmi = 1
- };
-
enum Rotation {
eRotateNone = 0,
eRotate90 = 1,
@@ -87,7 +85,7 @@
eVsyncSourceSurfaceFlinger = 1
};
- /*
+ /*
* Create a connection with SurfaceFlinger.
*/
virtual sp<ISurfaceComposerClient> createConnection() = 0;
@@ -107,10 +105,26 @@
*/
virtual void destroyDisplay(const sp<IBinder>& display) = 0;
- /* get the token for the existing default displays. possible values
- * for id are eDisplayIdMain and eDisplayIdHdmi.
+ /* get stable IDs for connected physical displays.
*/
- virtual sp<IBinder> getBuiltInDisplay(int32_t id) = 0;
+ virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0;
+
+ // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
+ std::optional<PhysicalDisplayId> getInternalDisplayId() const {
+ const auto displayIds = getPhysicalDisplayIds();
+ return displayIds.empty() ? std::nullopt : std::make_optional(displayIds.front());
+ }
+
+ /* get token for a physical display given its stable ID obtained via getPhysicalDisplayIds or a
+ * DisplayEventReceiver hotplug event.
+ */
+ virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const = 0;
+
+ // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
+ sp<IBinder> getInternalDisplayToken() const {
+ const auto displayId = getInternalDisplayId();
+ return displayId ? getPhysicalDisplayToken(*displayId) : nullptr;
+ }
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual void setTransactionState(const Vector<ComposerState>& state,
@@ -161,6 +175,8 @@
virtual status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes) = 0;
+ virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& primaries) = 0;
virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0;
virtual status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode) = 0;
@@ -317,10 +333,32 @@
*/
virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
- virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
- int32_t* outBufferId) = 0;
+ /*
+ * Queries whether the given display is a wide color display.
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+ bool* outIsWideColorDisplay) const = 0;
- virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) = 0;
+ /* Registers a listener to stream median luma updates from SurfaceFlinger.
+ *
+ * The sampling area is bounded by both samplingArea and the given stopLayerHandle
+ * (i.e., only layers behind the stop layer will be captured and sampled).
+ *
+ * Multiple listeners may be provided so long as they have independent listeners.
+ * If multiple listeners are provided, the effective sampling region for each listener will
+ * be bounded by whichever stop layer has a lower Z value.
+ *
+ * Requires the same permissions as captureLayers and captureScreen.
+ */
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) = 0;
+
+ /*
+ * Removes a listener that was streaming median luma updates from SurfaceFlinger.
+ */
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
};
// ----------------------------------------------------------------------------
@@ -336,7 +374,7 @@
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
- GET_BUILT_IN_DISPLAY,
+ GET_PHYSICAL_DISPLAY_TOKEN,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
GET_SUPPORTED_FRAME_TIMESTAMPS,
@@ -363,8 +401,12 @@
SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
GET_DISPLAYED_CONTENT_SAMPLE,
GET_PROTECTED_CONTENT_SUPPORT,
- CACHE_BUFFER,
- UNCACHE_BUFFER,
+ IS_WIDE_COLOR_DISPLAY,
+ GET_DISPLAY_NATIVE_PRIMARIES,
+ GET_PHYSICAL_DISPLAY_IDS,
+ ADD_REGION_SAMPLING_LISTENER,
+ REMOVE_REGION_SAMPLING_LISTENER,
+ // Always append new enum to the end.
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index f443df8..32ac9e8 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -18,8 +18,11 @@
#include <binder/IInterface.h>
#include <binder/SafeInterface.h>
+#include <gui/LayerMetadata.h>
#include <ui/PixelFormat.h>
+#include <unordered_map>
+
namespace android {
class FrameStats;
@@ -51,8 +54,8 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
- int32_t ownerUid, sp<IBinder>* handle,
+ uint32_t flags, const sp<IBinder>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) = 0;
/*
@@ -61,8 +64,7 @@
virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
- int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle,
+ LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) = 0;
/*
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
new file mode 100644
index 0000000..3ae10e4
--- /dev/null
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+
+#include <unordered_map>
+
+namespace android {
+
+enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 };
+
+struct LayerMetadata : public Parcelable {
+ std::unordered_map<uint32_t, std::vector<uint8_t>> mMap;
+
+ LayerMetadata();
+ LayerMetadata(const LayerMetadata& other);
+ LayerMetadata(LayerMetadata&& other);
+ explicit LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map);
+ LayerMetadata& operator=(const LayerMetadata& other);
+ LayerMetadata& operator=(LayerMetadata&& other);
+
+ void merge(const LayerMetadata& other);
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ bool has(uint32_t key) const;
+ int32_t getInt32(uint32_t key, int32_t fallback) const;
+ void setInt32(uint32_t key, int32_t value);
+
+ std::string itemToString(uint32_t key, const char* separator) const;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index af31420..c780c07 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -30,6 +30,7 @@
#include <input/InputWindow.h>
#endif
+#include <gui/LayerMetadata.h>
#include <math/vec3.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -85,6 +86,8 @@
eCornerRadiusChanged = 0x80000000,
eFrameChanged = 0x1'00000000,
eCachedBufferChanged = 0x2'00000000,
+ eBackgroundColorChanged = 0x4'00000000,
+ eMetadataChanged = 0x8'00000000,
};
layer_state_t()
@@ -110,7 +113,9 @@
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
- colorTransform(mat4()) {
+ colorTransform(mat4()),
+ bgColorAlpha(0),
+ bgColorDataspace(ui::Dataspace::UNKNOWN) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -180,6 +185,13 @@
#endif
cached_buffer_t cachedBuffer;
+
+ LayerMetadata metadata;
+
+ // The following refer to the alpha, and dataspace, respectively of
+ // the background color layer
+ float bgColorAlpha;
+ ui::Dataspace bgColorDataspace;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index db315c2..aa99bd2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -30,6 +30,7 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
@@ -53,35 +54,24 @@
// ---------------------------------------------------------------------------
-using TransactionCompletedCallbackTakesContext =
- std::function<void(void* /*context*/, const TransactionStats&)>;
-using TransactionCompletedCallback = std::function<void(const TransactionStats&)>;
+struct SurfaceControlStats {
+ SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
+ const sp<Fence>& prevReleaseFence)
+ : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
-class TransactionCompletedListener : public BnTransactionCompletedListener {
- TransactionCompletedListener();
-
- CallbackId getNextIdLocked() REQUIRES(mMutex);
-
- std::mutex mMutex;
-
- bool mListening GUARDED_BY(mMutex) = false;
-
- CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
- std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex);
-
-public:
- static sp<TransactionCompletedListener> getInstance();
- static sp<ITransactionCompletedListener> getIInstance();
-
- void startListeningLocked() REQUIRES(mMutex);
-
- CallbackId addCallback(const TransactionCompletedCallback& callback);
-
- // Overrides BnTransactionCompletedListener's onTransactionCompleted
- void onTransactionCompleted(ListenerStats stats) override;
+ sp<SurfaceControl> surfaceControl;
+ nsecs_t acquireTime = -1;
+ sp<Fence> previousReleaseFence;
};
+using TransactionCompletedCallbackTakesContext =
+ std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
+ const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& /*stats*/)>;
+using TransactionCompletedCallback =
+ std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& /*stats*/)>;
+
// ---------------------------------------------------------------------------
class SurfaceComposerClient : public RefBase
@@ -125,6 +115,10 @@
static status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes);
+ // Get the coordinates of the display's native color primaries
+ static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& outPrimaries);
+
// Gets the active color mode for the given display
static ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
@@ -158,49 +152,42 @@
static void doDropReferenceTransaction(const sp<IBinder>& handle,
const sp<ISurfaceComposerClient>& client);
- // Caches a buffer with the ISurfaceComposer so the buffer does not need to be resent across
- // processes
- static status_t cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId);
- // Uncaches a buffer set by cacheBuffer
- static status_t uncacheBuffer(int32_t bufferId);
+ // Queries whether a given display is wide color display.
+ static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
// ------------------------------------------------------------------------
// surface creation / destruction
+ static sp<SurfaceComposerClient> getDefault();
+
//! Create a surface
- sp<SurfaceControl> createSurface(
- const String8& name,// name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
- int32_t ownerUid = -1 // UID of the task
+ sp<SurfaceControl> createSurface(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
);
- status_t createSurfaceChecked(
- const String8& name, // name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
- int32_t ownerUid = -1 // UID of the task
+ status_t createSurfaceChecked(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ sp<SurfaceControl>* outSurface,
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
);
//! Create a surface
- sp<SurfaceControl> createWithSurfaceParent(
- const String8& name, // name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- uint32_t flags = 0, // usage flags
- Surface* parent = nullptr, // parent
- int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
- int32_t ownerUid = -1 // UID of the task
+ sp<SurfaceControl> createWithSurfaceParent(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ Surface* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
);
//! Create a virtual display
@@ -209,9 +196,13 @@
//! Destroy a virtual display
static void destroyDisplay(const sp<IBinder>& display);
- //! Get the token for the existing default displays.
- //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi.
- static sp<IBinder> getBuiltInDisplay(int32_t id);
+ //! Get stable IDs for connected physical displays
+ static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
+ static std::optional<PhysicalDisplayId> getInternalDisplayId();
+
+ //! Get token for a physical display given its stable ID
+ static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
+ static sp<IBinder> getInternalDisplayToken();
static status_t enableVSyncInjections(bool enable);
@@ -311,6 +302,8 @@
Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+ Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key,
+ std::vector<uint8_t> data);
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber. If the Layer identified
// by handle is removed, then we will apply this transaction regardless of
@@ -335,6 +328,10 @@
Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
+ // Sets the background color of a layer with the specified color, alpha, and dataspace
+ Transaction& setBackgroundColor(const sp<SurfaceControl>& sc, const half3& color,
+ float alpha, ui::Dataspace dataspace);
+
Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
@@ -387,6 +384,9 @@
Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix,
const vec3& translation);
+ Transaction& setGeometry(const sp<SurfaceControl>& sc,
+ const Rect& source, const Rect& dst, int transform);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -465,6 +465,50 @@
// ---------------------------------------------------------------------------
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+ TransactionCompletedListener();
+
+ CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+ std::mutex mMutex;
+
+ bool mListening GUARDED_BY(mMutex) = false;
+
+ CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& iBinder) const {
+ return std::hash<IBinder*>{}(iBinder.get());
+ }
+ };
+
+ struct CallbackTranslation {
+ TransactionCompletedCallback callbackFunction;
+ std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+ };
+
+ std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+
+public:
+ static sp<TransactionCompletedListener> getInstance();
+ static sp<ITransactionCompletedListener> getIInstance();
+
+ void startListeningLocked() REQUIRES(mMutex);
+
+ CallbackId addCallbackFunction(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls);
+
+ void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId>& callbackIds);
+
+ // Overrides BnTransactionCompletedListener's onTransactionCompleted
+ void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
}; // namespace android
#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 9bba766..55efcbf 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -58,8 +58,12 @@
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
- // release surface data from java
- void clear();
+ // Release the handles assosciated with the SurfaceControl, without reparenting
+ // them off-screen. At the moment if this isn't executed before ~SurfaceControl
+ // is called then the destructor will reparent the layer off-screen for you.
+ void release();
+ // Reparent off-screen and release. This is invoked by the destructor.
+ void destroy();
// disconnect any api that's connected
void disconnect();
@@ -71,6 +75,8 @@
sp<Surface> createSurface() const;
sp<IBinder> getHandle() const;
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+
status_t clearLayerFrameStats() const;
status_t getLayerFrameStats(FrameStats* outStats) const;
@@ -96,7 +102,6 @@
sp<Surface> generateSurfaceLocked() const;
status_t validate() const;
- void destroy();
sp<SurfaceComposerClient> mClient;
sp<IBinder> mHandle;
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index 5443812..b647aab 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -32,7 +32,7 @@
void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(OK, mComposerClient->initCheck());
- mDisplayToken = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+ mDisplayToken = mComposerClient->getInternalDisplayToken();
ASSERT_TRUE(mDisplayToken);
}
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index ac97733..4a78480 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -233,9 +233,11 @@
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ const auto display = mComposerClient->getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 259ef9f..f127853 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -131,8 +131,10 @@
// Verify the screenshot works with no protected buffers.
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+
+ const sp<IBinder> display = sf->getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
sp<GraphicBuffer> outBuffer;
ASSERT_EQ(NO_ERROR,
sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
@@ -552,7 +554,8 @@
sp<IBinder> createDisplay(const String8& /*displayName*/,
bool /*secure*/) override { return nullptr; }
void destroyDisplay(const sp<IBinder>& /*display */) override {}
- sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+ std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
+ sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
void setTransactionState(const Vector<ComposerState>& /*state*/,
const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/,
@@ -598,6 +601,10 @@
Vector<ColorMode>* /*outColorModes*/) override {
return NO_ERROR;
}
+ status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/,
+ ui::DisplayPrimaries& /*primaries*/) override {
+ return NO_ERROR;
+ }
ColorMode getActiveColorMode(const sp<IBinder>& /*display*/)
override {
return ColorMode::NATIVE;
@@ -660,11 +667,17 @@
status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
- status_t cacheBuffer(const sp<IBinder>& /*token*/, const sp<GraphicBuffer>& /*buffer*/,
- int32_t* /*outBufferId*/) {
+ status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
+
+ status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
+ const sp<IBinder>& /*stopLayerHandle*/,
+ const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
- status_t uncacheBuffer(const sp<IBinder>& /*token*/, int32_t /*bufferId*/) { return NO_ERROR; }
+ status_t removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
new file mode 100644
index 0000000..45eab00
--- /dev/null
+++ b/libs/incidentcompanion/Android.bp
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+filegroup {
+ name: "incidentcompanion_aidl",
+ srcs: [
+ "binder/android/os/IIncidentAuthListener.aidl",
+ "binder/android/os/IIncidentCompanion.aidl",
+ ],
+ path: "binder",
+}
+
+cc_library_static {
+ name: "libincidentcompanion",
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ aidl: {
+ local_include_dirs: ["binder"],
+ export_aidl_headers: true,
+ },
+ srcs: [
+ ":incidentcompanion_aidl",
+ ],
+ export_include_dirs: ["binder"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+}
+
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
similarity index 66%
rename from cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
rename to libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
index c1a7f15..5484be8 100644
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -17,7 +17,18 @@
package android.os;
/**
- * Specifies arguments for IDumpstate.
- * {@hide}
- */
-parcelable DumpstateOptions cpp_header "android/os/DumpstateOptions.h";
+ * Callback for IIncidentCompanion.
+ *
+ * @hide
+ */
+oneway interface IIncidentAuthListener {
+ /**
+ * The user approved the incident or bug report to be sent.
+ */
+ void onReportApproved();
+
+ /**
+ * The user did not approve the incident or bug report to be sent.
+ */
+ void onReportDenied();
+}
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
new file mode 100644
index 0000000..6bf98d2
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IIncidentAuthListener;
+
+/**
+ * Helper service for incidentd and dumpstated to provide user feedback
+ * and authorization for bug and inicdent reports to be taken.
+ *
+ * @hide
+ */
+interface IIncidentCompanion {
+ /**
+ * Request an authorization for an incident or bug report.
+ * // TODO(b/111441001): Add the permission
+ * <p>
+ * This function requires the ___ permission.
+ *
+ * @param callingUid The original application that requested the report. This function
+ * returns via the callback whether the application should be trusted. It is up
+ * to the caller to actually implement the restriction to take or not take
+ * the incident or bug report.
+ * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
+ * a dialog will be shown as a notification.
+ * @param callback Interface to receive results. The results may not come back for
+ * a long (user's choice) time, or ever (if they never respond to the notification).
+ * Authorization requests are not persisted across reboot. It is up to the calling
+ * service to request another authorization after reboot if they still would like
+ * to send their report.
+ */
+ oneway void authorizeReport(int callingUid, String callingPackage,
+ int flags, IIncidentAuthListener callback);
+
+ /**
+ * Cancel an authorization.
+ */
+ oneway void cancelAuthorization(IIncidentAuthListener callback);
+
+ /**
+ * Return the list of pending approvals.
+ */
+ List<String> getPendingReports();
+
+ /**
+ * The user has authorized the report to be shared.
+ *
+ * @param uri the report.
+ */
+ void approveReport(String uri);
+
+ /**
+ * The user has denied the report from being shared.
+ *
+ * @param uri the report.
+ */
+ void denyReport(String uri);
+}
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index aa1371f..5c5613d 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -95,6 +95,7 @@
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
output.writeInt32(displayId);
+ output.writeInt32(portalToDisplayId);
applicationInfo.write(output);
output.write(touchableRegion);
@@ -136,6 +137,7 @@
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
ret.displayId = from.readInt32();
+ ret.portalToDisplayId = from.readInt32();
ret.applicationInfo = InputApplicationInfo::read(from);
from.read(ret.touchableRegion);
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 5e5893f..09dd72b 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -61,6 +61,7 @@
i.ownerUid = 24;
i.inputFeatures = 29;
i.displayId = 34;
+ i.portalToDisplayId = 2;
Parcel p;
i.write(p);
@@ -90,6 +91,7 @@
ASSERT_EQ(i.ownerUid, i2.ownerUid);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.displayId, i2.displayId);
+ ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
}
} // namespace test
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index ff64f61..994e953 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -311,8 +311,17 @@
if (!desc) return 0;
if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
- // Make a trial allocation.
- // TODO(b/115660272): add implementation that uses a HAL query.
+ bool supported = false;
+ GraphicBuffer* gBuffer = new GraphicBuffer();
+ status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
+ desc->usage, &supported);
+
+ if (err == NO_ERROR) {
+ return supported;
+ }
+
+ // function isSupported is not implemented on device or an error occurred during HAL
+ // query. Make a trial allocation.
AHardwareBuffer_Desc trialDesc = *desc;
trialDesc.width = 4;
trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4;
diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h
new file mode 100644
index 0000000..7e1313b
--- /dev/null
+++ b/libs/nativewindow/include/android/hdr_metadata.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file hdr_metadata.h
+ */
+
+#ifndef ANDROID_HDR_METADATA_H
+#define ANDROID_HDR_METADATA_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * These structures are used to define the display's capabilities for HDR content.
+ * They can be used to better tone map content to user's display.
+ */
+
+/**
+ * Color is defined in CIE XYZ coordinates.
+ */
+struct AColor_xy {
+ float x;
+ float y;
+};
+
+/**
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ */
+struct AHdrMetadata_smpte2086 {
+ struct AColor_xy displayPrimaryRed;
+ struct AColor_xy displayPrimaryGreen;
+ struct AColor_xy displayPrimaryBlue;
+ struct AColor_xy whitePoint;
+ float maxLuminance;
+ float minLuminance;
+};
+
+/**
+ * CTA 861.3 "HDR Static Metadata Extension" static metadata
+ */
+struct AHdrMetadata_cta861_3 {
+ float maxContentLightLevel;
+ float maxFrameAverageLightLevel;
+};
+
+__END_DECLS
+
+#endif // ANDROID_HDR_METADATA_H
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index f57bf9c..8f9071e 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -424,6 +424,7 @@
mTraceGpuCompletion = true;
mFlushTracer = std::make_unique<FlushTracer>(this);
}
+ mDrawingBuffer = createFramebuffer();
}
GLESRenderEngine::~GLESRenderEngine() {
@@ -439,6 +440,10 @@
return std::make_unique<GLImage>(*this);
}
+Framebuffer* GLESRenderEngine::getFramebufferForDrawing() {
+ return mDrawingBuffer.get();
+}
+
void GLESRenderEngine::primeCache() const {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mFeatureFlags & USE_COLOR_MANAGEMENT);
@@ -449,6 +454,7 @@
}
base::unique_fd GLESRenderEngine::flush() {
+ ATRACE_CALL();
if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
}
@@ -479,6 +485,7 @@
}
bool GLESRenderEngine::finish() {
+ ATRACE_CALL();
if (!GLExtensions::getInstance().hasFenceSync()) {
ALOGW("no synchronization support");
return false;
@@ -544,6 +551,8 @@
}
void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
+ ATRACE_CALL();
+ glDisable(GL_BLEND);
glClearColor(red, green, blue, alpha);
glClear(GL_COLOR_BUFFER_BIT);
}
@@ -594,6 +603,7 @@
}
void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ ATRACE_CALL();
const GLImage& glImage = static_cast<const GLImage&>(image);
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
@@ -608,7 +618,24 @@
}
status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> bufferFence) {
+ sp<Fence> bufferFence, bool readCache) {
+ return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache,
+ /*persistCache=*/false);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> bufferFence, bool readCache,
+ bool persistCache) {
+ ATRACE_CALL();
+ if (readCache) {
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage != mImageCache.end()) {
+ bindExternalTextureImage(texName, *cachedImage->second);
+ return NO_ERROR;
+ }
+ }
+
std::unique_ptr<Image> newImage = createImage();
bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
@@ -644,9 +671,36 @@
}
}
+ // We don't always want to persist to the cache, e.g. on older devices we
+ // might bind for synchronization purposes, but that might leak if we never
+ // call drawLayers again, so it's just better to recreate the image again
+ // if needed when we draw.
+ if (persistCache) {
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+ }
+
return NO_ERROR;
}
+void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) {
+ ATRACE_CALL();
+ // destroy old image references that we're not going to draw with.
+ std::unordered_set<uint64_t> bufIds;
+ for (auto layer : layers) {
+ if (layer.source.buffer.buffer != nullptr) {
+ bufIds.emplace(layer.source.buffer.buffer->getId());
+ }
+ }
+
+ for (auto it = mImageCache.begin(); it != mImageCache.end();) {
+ if (bufIds.count(it->first) == 0) {
+ it = mImageCache.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
// Translate win by the rounded corners rect coordinates, to have all values in
// layer coordinate space.
@@ -667,6 +721,7 @@
}
status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+ ATRACE_CALL();
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
EGLImageKHR eglImage = glFramebuffer->getEGLImage();
uint32_t textureName = glFramebuffer->getTextureName();
@@ -695,6 +750,7 @@
}
void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+ ATRACE_CALL();
mFboHeight = 0;
// back to main framebuffer
@@ -734,6 +790,7 @@
const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* const buffer,
base::unique_fd* drawFence) {
+ ATRACE_CALL();
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
return NO_ERROR;
@@ -748,6 +805,15 @@
return fbo.getStatus();
}
+ evictImages(layers);
+
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
setViewportAndProjection(display.physicalDisplay, display.clip);
setOutputDataSpace(display.outputDataspace);
@@ -756,6 +822,7 @@
mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
mState.projectionMatrix = projectionMatrix;
if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
}
@@ -775,28 +842,33 @@
bool usePremultipliedAlpha = true;
bool disableTexture = true;
+ bool isOpaque = false;
if (layer.source.buffer.buffer != nullptr) {
disableTexture = false;
+ isOpaque = layer.source.buffer.isOpaque;
sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+ bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE;
bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
+ layer.source.buffer.fence, readCache, /*persistCache=*/true);
usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- texture.setMatrix(layer.source.buffer.textureTransform.asArray());
+ mat4 texMatrix = layer.source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
texture.setFiltering(layer.source.buffer.useTextureFiltering);
texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
setSourceY410BT2020(layer.source.buffer.isY410BT2020);
renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 1.0);
- texCoords[1] = vec2(0.0, 0.0);
- texCoords[2] = vec2(1.0, 0.0);
- texCoords[3] = vec2(1.0, 1.0);
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
setupLayerTexturing(texture);
}
@@ -804,8 +876,11 @@
const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, layer.source.buffer.isOpaque, disableTexture,
- color, layer.geometry.roundedCornersRadius);
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
+ glDisable(GL_BLEND);
+ }
setSourceDataSpace(layer.sourceDataspace);
drawMesh(mesh);
@@ -864,6 +939,7 @@
}
void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
+ ATRACE_CALL();
mVpWidth = viewport.getWidth();
mVpHeight = viewport.getHeight();
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index b596242..7b72666 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -30,6 +30,7 @@
#include <GLES2/gl2.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/private/Description.h>
+#include <unordered_map>
#define EGL_NO_CONFIG ((EGLConfig)0)
@@ -70,6 +71,8 @@
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+ bool readCache);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
void checkErrors() const override;
@@ -85,6 +88,7 @@
EGLConfig getEGLConfig() const { return mEGLConfig; }
protected:
+ Framebuffer* getFramebufferForDrawing() override;
void dump(std::string& result) override;
void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
ui::Transform::orientation_flags rotation) override;
@@ -133,7 +137,10 @@
// Defines the viewport, and sets the projection matrix to the projection
// defined by the clip.
void setViewportAndProjection(Rect viewport, Rect clip);
- status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
+ status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+ bool readCache, bool persistCache);
+ // Evicts stale images from the buffer cache.
+ void evictImages(const std::vector<LayerSettings>& layers);
// Computes the cropping window for the layer and sets up cropping
// coordinates for the mesh.
FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
@@ -179,6 +186,14 @@
// supports sRGB, DisplayP3 color spaces.
const bool mUseColorManagement = false;
+ // Cache of GL images that we'll store per GraphicBuffer ID
+ // TODO: Layer should call back on destruction instead to clean this up,
+ // as it has better system utilization at the potential expense of a
+ // more complicated interface.
+ std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
+
+ std::unique_ptr<Framebuffer> mDrawingBuffer;
+
class FlushTracer {
public:
FlushTracer(GLESRenderEngine* engine);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 4a519bb..0e3b405 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "GLFramebuffer.h"
#include <GLES/gl.h>
@@ -21,6 +23,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
#include "GLESRenderEngine.h"
namespace android {
@@ -40,6 +43,7 @@
}
bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
+ ATRACE_CALL();
if (mEGLImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEGLDisplay, mEGLImage);
mEGLImage = EGL_NO_IMAGE_KHR;
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index 587cb31..77e648e 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "GLImage.h"
#include <vector>
#include <log/log.h>
+#include <utils/Trace.h>
#include "GLESRenderEngine.h"
#include "GLExtensions.h"
@@ -50,6 +53,7 @@
}
bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+ ATRACE_CALL();
if (mEGLImage != EGL_NO_IMAGE_KHR) {
if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
ALOGE("failed to destroy image: %#x", eglGetError());
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 4d53205..aa45ed8 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -32,6 +32,16 @@
// Metadata describing the input buffer to render from.
struct Buffer {
+ // Hint for whether to use the Image cache or not.
+ // If NO_CACHE is specified, then upload the contents of the GraphicBuffer
+ // to the GPU, without checking against any implementation defined cache.
+ // If USE_CACHE is specified, then check against an implementation defined
+ // cache first. If there is an Image cached for the given GraphicBuffer id,
+ // then use that instead of the provided buffer contents. If there is no
+ // cached image or the RenderEngine implementation does not support caching,
+ // then use the GraphicBuffer contents.
+ enum class CachingHint { NO_CACHE, USE_CACHE };
+
// Buffer containing the image that we will render.
// If buffer == nullptr, then the rest of the fields in this struct will be
// ignored.
@@ -40,6 +50,9 @@
// Fence that will fire when the buffer is ready to be bound.
sp<Fence> fence = nullptr;
+ // Caching hint to use when uploading buffer contents.
+ CachingHint cacheHint = CachingHint::NO_CACHE;
+
// Texture identifier to bind the external texture to.
// TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
uint32_t textureName = 0;
@@ -113,6 +126,9 @@
// Additional layer-specific color transform to be applied before the global
// transform.
mat4 colorTransform = mat4();
+
+ // True if blending will be forced to be disabled.
+ bool disableBlending = false;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 20dd996..b51ed22 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -108,6 +108,8 @@
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> fence, bool cleanCache) = 0;
// When binding a native buffer, it must be done before setViewportAndProjection
// Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
@@ -172,37 +174,37 @@
// fence.
// @return An error code indicating whether drawing was successful. For
// now, this always returns NO_ERROR.
- // TODO(alecmouri): Consider making this a multi-display API, so that the
- // caller deoes not need to handle multiple fences.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* buffer, base::unique_fd* drawFence) = 0;
- // TODO(alecmouri): Expose something like bindTexImage() so that devices
- // that don't support native sync fences can get rid of code duplicated
- // between BufferStateLayer and BufferQueueLayer for binding an external
- // texture.
-
- // TODO(alecmouri): Add API to help with managing a texture pool.
+protected:
+ // Gets a framebuffer to render to. This framebuffer may or may not be
+ // cached depending on the implementation.
+ //
+ // Note that this method does not transfer ownership, so the caller most not
+ // live longer than RenderEngine.
+ virtual Framebuffer* getFramebufferForDrawing() = 0;
+ friend class BindNativeBufferAsFramebuffer;
};
class BindNativeBufferAsFramebuffer {
public:
BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
- : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) {
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected())
- ? mEngine.bindFrameBuffer(mFramebuffer.get())
+ ? mEngine.bindFrameBuffer(mFramebuffer)
: NO_MEMORY;
}
~BindNativeBufferAsFramebuffer() {
mFramebuffer->setNativeWindowBuffer(nullptr, false);
- mEngine.unbindFrameBuffer(mFramebuffer.get());
+ mEngine.unbindFrameBuffer(mFramebuffer);
}
status_t getStatus() const { return mStatus; }
private:
RenderEngine& mEngine;
- std::unique_ptr<Framebuffer> mFramebuffer;
+ Framebuffer* mFramebuffer;
status_t mStatus;
};
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index b4c7c96..800eac3 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -36,6 +36,7 @@
MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>());
MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
+ MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
MOCK_CONST_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(useNativeFenceSync, bool());
@@ -52,6 +53,7 @@
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
+ MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool));
MOCK_CONST_METHOD0(checkErrors, void());
MOCK_METHOD4(setViewportAndProjection,
void(size_t, size_t, Rect, ui::Transform::orientation_flags));
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index bef25a8..f82beeb 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -178,6 +178,9 @@
template <typename SourceVariant>
void fillBufferWithRoundedCorners();
+ template <typename SourceVariant>
+ void overlayCorners();
+
void fillRedBufferTextureTransform();
void fillBufferTextureTransform();
@@ -194,7 +197,7 @@
void clearLeftRegion();
- void fillBufferThenClearRegion();
+ void clearRegion();
// Dumb hack to get aroud the fact that tear-down for renderengine isn't
// well defined right now, so we can't create multiple instances
@@ -543,6 +546,44 @@
255);
}
+template <typename SourceVariant>
+void RenderEngineTest::overlayCorners() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layersFirst;
+
+ renderengine::LayerSettings layerOne;
+ layerOne.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0);
+ SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+ layerOne.alpha = 0.2;
+
+ layersFirst.push_back(layerOne);
+ invokeDraw(settings, layersFirst, mBuffer);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+
+ std::vector<renderengine::LayerSettings> layersSecond;
+ renderengine::LayerSettings layerTwo;
+ layerTwo.geometry.boundaries =
+ FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+ layerTwo.alpha = 1.0f;
+
+ layersSecond.push_back(layerTwo);
+ invokeDraw(settings, layersSecond, mBuffer);
+
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
void RenderEngineTest::fillRedBufferTextureTransform() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -685,14 +726,13 @@
invokeDraw(settings, layers, mBuffer);
}
-void RenderEngineTest::fillBufferThenClearRegion() {
- fillGreenBuffer<ColorSourceVariant>();
+void RenderEngineTest::clearRegion() {
// Reuse mBuffer
clearLeftRegion();
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
DEFAULT_DISPLAY_HEIGHT),
- 0, 255, 0, 255);
+ 0, 0, 0, 0);
}
TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
@@ -747,6 +787,10 @@
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+ overlayCorners<ColorSourceVariant>();
+}
+
TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
@@ -795,6 +839,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+ overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
@@ -843,6 +891,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+ overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
fillBufferTextureTransform();
}
@@ -855,8 +907,8 @@
fillBufferWithoutPremultiplyAlpha();
}
-TEST_F(RenderEngineTest, drawLayers_fillBufferThenClearRegion) {
- fillBufferThenClearRegion();
+TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+ clearRegion();
}
} // namespace android
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 46ba7c6..4438d45 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -29,6 +29,7 @@
#include <sensor/ISensorEventConnection.h>
#include <android/sensor.h>
+#include <hardware/sensors-base.h>
using std::min;
@@ -188,6 +189,52 @@
return;
}
+ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const {
+ // Check if this Sensor Event Queue is registered to receive each type of event. If it is not,
+ // then do not copy the event into the final buffer. Minimize the number of copy operations by
+ // finding consecutive sequences of events that the Sensor Event Queue should receive and only
+ // copying the events once an unregistered event type is reached.
+ bool intervalStartLocSet = false;
+ size_t intervalStartLoc = 0;
+ size_t eventsInInterval = 0;
+ ssize_t eventsCopied = 0;
+
+ for (size_t i = 0; i < count; i++) {
+ bool includeEvent =
+ (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo);
+
+ if (includeEvent) {
+ // Do not copy events yet since there may be more consecutive events that should be
+ // copied together. Track the start location and number of events in the current
+ // sequence.
+ if (!intervalStartLocSet) {
+ intervalStartLoc = i;
+ intervalStartLocSet = true;
+ eventsInInterval = 0;
+ }
+ eventsInInterval++;
+ }
+
+ // Shift the events from the already processed interval once an event that should not be
+ // included is reached or if this is the final event to be processed.
+ if (!includeEvent || (i + 1 == count)) {
+ // Only shift the events if the interval did not start with the first event. If the
+ // interval started with the first event, the events are already in their correct
+ // location.
+ if (intervalStartLoc > 0) {
+ memmove(&events[eventsCopied], &events[intervalStartLoc],
+ eventsInInterval * sizeof(ASensorEvent));
+ }
+ eventsCopied += eventsInInterval;
+
+ // Reset the interval information
+ eventsInInterval = 0;
+ intervalStartLocSet = false;
+ }
+ }
+ return eventsCopied;
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8176578..8c3fde0 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -34,6 +34,7 @@
// Concrete types for the NDK
struct ASensorEventQueue {
ALooper* looper;
+ bool requestAdditionalInfo;
};
// ----------------------------------------------------------------------------
@@ -92,6 +93,13 @@
void sendAck(const ASensorEvent* events, int count);
status_t injectSensorEvent(const ASensorEvent& event);
+
+ // Filters the given sensor events in place and returns the new number of events.
+ //
+ // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is
+ // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed.
+ ssize_t filterEvents(ASensorEvent* events, size_t count) const;
+
private:
sp<Looper> getLooper() const;
sp<ISensorEventConnection> mSensorEventConnection;
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9fd84bc..c9a7668 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -21,6 +21,7 @@
srcs: [
"Sensor_test.cpp",
+ "SensorEventQueue_test.cpp",
],
shared_libs: [
diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp
new file mode 100644
index 0000000..1eb5883
--- /dev/null
+++ b/libs/sensor/tests/SensorEventQueue_test.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+#include <android/sensor.h>
+#include <hardware/sensors-base.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
+
+namespace android {
+
+class SensorEventQueueTest : public ::testing::Test {
+protected:
+ typedef std::vector<int32_t> Events;
+
+ SensorEventQueueTest() {};
+
+ virtual void SetUp() override {
+ SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest"));
+ mQueue = manager.createEventQueue();
+ }
+
+ void configureAdditionalInfo(bool enable) {
+ mQueue->requestAdditionalInfo = enable;
+ }
+
+ Events filterEvents(const Events &types) const {
+ // Convert the events into SensorEvent array
+ ASensorEvent* events = new ASensorEvent[types.size()];
+ for (size_t i = 0; i < types.size(); i++) {
+ events[i].type = types[i];
+ }
+
+ // Filter the events
+ ssize_t filteredCount = mQueue->filterEvents(events, types.size());
+
+ // Copy the result into an output vector
+ Events result;
+ for (size_t i = 0; i < filteredCount; i++) {
+ result.push_back(events[i].type);
+ }
+ delete[] events;
+
+ return result;
+ }
+
+ Events getExpectedEvents(const Events &events) const {
+ Events output;
+ for (size_t i = 0; i != events.size(); i++) {
+ // Copy events if the event queue is configured to receive them
+ if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) {
+ output.push_back(events[i]);
+ }
+ }
+ return output;
+ }
+
+ void runFilterTest(const Events& events) {
+ Events filtered = filterEvents(events);
+ Events expected = getExpectedEvents(events);
+ EXPECT_EQ(expected.size(), filtered.size());
+ EXPECT_EQ(expected, filtered);
+ }
+
+private:
+ sp<SensorEventQueue> mQueue;
+};
+
+TEST_F(SensorEventQueueTest, FilterZeroEvents) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) {
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) {
+ configureAdditionalInfo(false /* enable */);
+ const Events events = {SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO};
+ runFilterTest(events);
+
+ // Update setting to request Additional Info
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest(events);
+
+ // Update setting to stop requesting Additional Info
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest(events);
+}
+
+} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 74450d7..00e227f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -87,7 +87,7 @@
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
- "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.2",
"android.hardware.configstore-utils",
"libbase",
"libcutils",
@@ -102,6 +102,7 @@
],
export_shared_lib_headers: [
+ "android.hardware.configstore@1.2",
"android.hardware.graphics.common@1.2",
],
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 6310f29..604665b 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -24,12 +24,12 @@
#include <utils/Trace.h>
using ::android::base::unique_fd;
-using ::android::BufferHubDefs::AnyClientAcquired;
-using ::android::BufferHubDefs::AnyClientGained;
-using ::android::BufferHubDefs::IsClientAcquired;
-using ::android::BufferHubDefs::IsClientGained;
-using ::android::BufferHubDefs::IsClientPosted;
-using ::android::BufferHubDefs::IsClientReleased;
+using ::android::BufferHubDefs::isAnyClientAcquired;
+using ::android::BufferHubDefs::isAnyClientGained;
+using ::android::BufferHubDefs::isClientAcquired;
+using ::android::BufferHubDefs::isClientGained;
+using ::android::BufferHubDefs::isClientPosted;
+using ::android::BufferHubDefs::isClientReleased;
using ::android::frameworks::bufferhub::V1_0::BufferHubStatus;
using ::android::frameworks::bufferhub::V1_0::BufferTraits;
using ::android::frameworks::bufferhub::V1_0::IBufferClient;
@@ -39,22 +39,22 @@
namespace android {
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::Create(uint32_t width, uint32_t height,
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::create(uint32_t width, uint32_t height,
uint32_t layerCount, uint32_t format,
uint64_t usage, size_t userMetadataSize) {
auto buffer = std::unique_ptr<BufferHubBuffer>(
new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
- return buffer->IsValid() ? std::move(buffer) : nullptr;
+ return buffer->isValid() ? std::move(buffer) : nullptr;
}
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::Import(const native_handle_t* token) {
- if (token == nullptr) {
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) {
+ if (token == nullptr || token.get() == nullptr) {
ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
return nullptr;
}
auto buffer = std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(token));
- return buffer->IsValid() ? std::move(buffer) : nullptr;
+ return buffer->isValid() ? std::move(buffer) : nullptr;
}
BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount,
@@ -79,10 +79,10 @@
sp<IBufferClient> client;
BufferTraits bufferTraits;
IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& status, const auto& outClient,
- const auto& traits) {
+ const auto& outTraits) {
ret = status;
client = std::move(outClient);
- bufferTraits = std::move(traits);
+ bufferTraits = std::move(outTraits);
};
if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), alloc_cb)
@@ -105,7 +105,7 @@
mBufferClient = std::move(client);
}
-BufferHubBuffer::BufferHubBuffer(const native_handle_t* token) {
+BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) {
sp<IBufferHub> bufferhub = IBufferHub::getService();
if (bufferhub.get() == nullptr) {
ALOGE("%s: BufferHub service not found!", __FUNCTION__);
@@ -116,15 +116,15 @@
sp<IBufferClient> client;
BufferTraits bufferTraits;
IBufferHub::importBuffer_cb import_cb = [&](const auto& status, const auto& outClient,
- const auto& traits) {
+ const auto& outTraits) {
ret = status;
client = std::move(outClient);
- bufferTraits = std::move(traits);
+ bufferTraits = std::move(outTraits);
};
// hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
// transfer.
- if (!bufferhub->importBuffer(hidl_handle(token), import_cb).isOk()) {
+ if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), import_cb).isOk()) {
ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
return;
} else if (ret != BufferHubStatus::NO_ERROR) {
@@ -167,53 +167,58 @@
return -EINVAL;
}
- int bufferId = bufferTraits.bufferInfo->data[1];
+ // Import fds. Dup fds because hidl_handle owns the fds.
+ unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
+ mMetadata = BufferHubMetadata::import(std::move(ashmemFd));
+ if (!mMetadata.isValid()) {
+ ALOGE("%s: Received an invalid metadata.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0));
+ if (!mEventFd.isValid()) {
+ ALOGE("%s: Received ad invalid event fd.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ int bufferId = bufferTraits.bufferInfo->data[2];
if (bufferId < 0) {
- ALOGE("%s: Received an invalid (negative) id!", __FUNCTION__);
+ ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__);
return -EINVAL;
}
uint32_t clientBitMask;
- memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[2], sizeof(clientBitMask));
+ memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
if (clientBitMask == 0U) {
- ALOGE("%s: Received a invalid client state mask!", __FUNCTION__);
- return -EINVAL;
- }
-
- // Import the metadata. Dup since hidl_handle owns the fd
- unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
- mMetadata = BufferHubMetadata::Import(std::move(ashmemFd));
-
- if (!mMetadata.IsValid()) {
- ALOGE("%s: invalid metadata.", __FUNCTION__);
+ ALOGE("%s: Received an invalid client state mask.", __FUNCTION__);
return -EINVAL;
}
uint32_t userMetadataSize;
- memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[3], sizeof(userMetadataSize));
- if (mMetadata.user_metadata_size() != userMetadataSize) {
+ memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize));
+ if (mMetadata.userMetadataSize() != userMetadataSize) {
ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__,
- userMetadataSize, mMetadata.user_metadata_size());
+ userMetadataSize, mMetadata.userMetadataSize());
return -EINVAL;
}
- size_t metadataSize = static_cast<size_t>(mMetadata.metadata_size());
+ size_t metadataSize = static_cast<size_t>(mMetadata.metadataSize());
if (metadataSize < BufferHubDefs::kMetadataHeaderSize) {
ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize);
return -EINVAL;
}
// Populate shortcuts to the atomics in metadata.
- auto metadata_header = mMetadata.metadata_header();
- buffer_state_ = &metadata_header->buffer_state;
- fence_state_ = &metadata_header->fence_state;
- active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask;
+ auto metadataHeader = mMetadata.metadataHeader();
+ mBufferState = &metadataHeader->buffer_state;
+ mFenceState = &metadataHeader->fence_state;
+ mActiveClientsBitMask = &metadataHeader->active_clients_bit_mask;
// The C++ standard recommends (but does not require) that lock-free atomic operations are
// also address-free, that is, suitable for communication between processes using shared
// memory.
- LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
- !std::atomic_is_lock_free(fence_state_) ||
- !std::atomic_is_lock_free(active_clients_bit_mask_),
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
+ !std::atomic_is_lock_free(mFenceState) ||
+ !std::atomic_is_lock_free(mActiveClientsBitMask),
"Atomic variables in ashmen are not lock free.");
// Import the buffer: We only need to hold on the native_handle_t here so that
@@ -226,100 +231,104 @@
// TODO(b/112012161) Set up shared fences.
ALOGD("%s: id=%d, buffer_state=%" PRIx32 ".", __FUNCTION__, mId,
- buffer_state_->load(std::memory_order_acquire));
+ mBufferState->load(std::memory_order_acquire));
return 0;
}
-int BufferHubBuffer::Gain() {
- uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (IsClientGained(current_buffer_state, mClientStateMask)) {
+int BufferHubBuffer::gain() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientGained(currentBufferState, mClientStateMask)) {
ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__,
mClientStateMask);
return 0;
}
do {
- if (AnyClientGained(current_buffer_state & (~mClientStateMask)) ||
- AnyClientAcquired(current_buffer_state)) {
+ if (isAnyClientGained(currentBufferState & (~mClientStateMask)) ||
+ isAnyClientAcquired(currentBufferState)) {
ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
return -EBUSY;
}
// Change the buffer state to gained state, whose value happens to be the same as
// mClientStateMask.
- } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, mClientStateMask,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
// TODO(b/119837586): Update fence state and return GPU fence.
return 0;
}
-int BufferHubBuffer::Post() {
- uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
- uint32_t updated_buffer_state = (~mClientStateMask) & BufferHubDefs::kHighBitsMask;
+int BufferHubBuffer::post() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ uint32_t updatedBufferState = (~mClientStateMask) & BufferHubDefs::kHighBitsMask;
do {
- if (!IsClientGained(current_buffer_state, mClientStateMask)) {
+ if (!isClientGained(currentBufferState, mClientStateMask)) {
ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d "
"mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
return -EBUSY;
}
// Set the producer client buffer state to released, other clients' buffer state to posted.
// Post to all existing and non-existing clients.
- } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
// TODO(b/119837586): Update fence state and return GPU fence if needed.
return 0;
}
-int BufferHubBuffer::Acquire() {
- uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (IsClientAcquired(current_buffer_state, mClientStateMask)) {
+int BufferHubBuffer::acquire() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientAcquired(currentBufferState, mClientStateMask)) {
ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__,
mClientStateMask);
return 0;
}
- uint32_t updated_buffer_state = 0U;
+ uint32_t updatedBufferState = 0U;
do {
- if (!IsClientPosted(current_buffer_state, mClientStateMask)) {
+ if (!isClientPosted(currentBufferState, mClientStateMask)) {
ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d "
"mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
- __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
return -EBUSY;
}
// Change the buffer state for this consumer from posted to acquired.
- updated_buffer_state = current_buffer_state ^ mClientStateMask;
- } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
+ updatedBufferState = currentBufferState ^ mClientStateMask;
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
// TODO(b/119837586): Update fence state and return GPU fence.
return 0;
}
-int BufferHubBuffer::Release() {
- uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (IsClientReleased(current_buffer_state, mClientStateMask)) {
+int BufferHubBuffer::release() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientReleased(currentBufferState, mClientStateMask)) {
ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__,
mClientStateMask);
return 0;
}
- uint32_t updated_buffer_state = 0U;
+ uint32_t updatedBufferState = 0U;
do {
- updated_buffer_state = current_buffer_state & (~mClientStateMask);
- } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
- std::memory_order_acq_rel,
- std::memory_order_acquire));
+ updatedBufferState = currentBufferState & (~mClientStateMask);
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
// TODO(b/119837586): Update fence state and return GPU fence if needed.
return 0;
}
-bool BufferHubBuffer::IsValid() const {
- // TODO(b/68770788): check eventFd once implemented
- return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
- mMetadata.IsValid() && mBufferClient != nullptr;
+bool BufferHubBuffer::isReleased() const {
+ return (mBufferState->load(std::memory_order_acquire) &
+ mActiveClientsBitMask->load(std::memory_order_acquire)) == 0;
}
-native_handle_t* BufferHubBuffer::Duplicate() {
+bool BufferHubBuffer::isValid() const {
+ return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
+ mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr;
+}
+
+sp<NativeHandle> BufferHubBuffer::duplicate() {
if (mBufferClient == nullptr) {
ALOGE("%s: missing BufferClient!", __FUNCTION__);
return nullptr;
@@ -343,7 +352,7 @@
return nullptr;
}
- return native_handle_clone(token.getNativeHandle());
+ return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true);
}
} // namespace android
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp
index 978b352..bffc2ca 100644
--- a/libs/ui/BufferHubEventFd.cpp
+++ b/libs/ui/BufferHubEventFd.cpp
@@ -23,6 +23,8 @@
BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {}
+BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {}
+
status_t BufferHubEventFd::signal() const {
if (!isValid()) {
ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__);
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
index 816707d..05bc7dd 100644
--- a/libs/ui/BufferHubMetadata.cpp
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -34,7 +34,7 @@
using BufferHubDefs::MetadataHeader;
/* static */
-BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) {
+BufferHubMetadata BufferHubMetadata::create(size_t userMetadataSize) {
// The size the of metadata buffer is used as the "width" parameter during allocation. Thus it
// cannot overflow uint32_t.
if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) {
@@ -59,11 +59,11 @@
return {};
}
- return BufferHubMetadata::Import(std::move(ashmemFd));
+ return BufferHubMetadata::import(std::move(ashmemFd));
}
/* static */
-BufferHubMetadata BufferHubMetadata::Import(unique_fd ashmemFd) {
+BufferHubMetadata BufferHubMetadata::import(unique_fd ashmemFd) {
if (!ashmem_valid(ashmemFd.get())) {
ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
return {};
@@ -94,7 +94,7 @@
BufferHubMetadata::~BufferHubMetadata() {
if (mMetadataHeader != nullptr) {
- int ret = munmap(mMetadataHeader, metadata_size());
+ int ret = munmap(mMetadataHeader, metadataSize());
ALOGE_IF(ret != 0,
"BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno);
mMetadataHeader = nullptr;
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index ea7321e..5dc4530 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -94,7 +94,7 @@
mMapperV2_1 = IMapper::castFrom(mMapper);
}
-bool Gralloc2Mapper::isSupported() const {
+bool Gralloc2Mapper::isLoaded() const {
return mMapper != nullptr;
}
@@ -227,7 +227,14 @@
}
status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, void** outData) const {
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const {
+ if (outBytesPerPixel) {
+ *outBytesPerPixel = -1;
+ }
+ if (outBytesPerStride) {
+ *outBytesPerStride = -1;
+ }
auto buffer = const_cast<native_handle_t*>(bufferHandle);
IMapper::Rect accessRegion = sGralloc2Rect(bounds);
@@ -344,6 +351,12 @@
return releaseFence;
}
+status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
+ android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/, bool* /*outSupported*/) const {
+ return INVALID_OPERATION;
+}
+
Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -352,7 +365,7 @@
}
}
-bool Gralloc2Allocator::isSupported() const {
+bool Gralloc2Allocator::isLoaded() const {
return mAllocator != nullptr;
}
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 128200e..7f8e57c 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -92,7 +92,7 @@
}
}
-bool Gralloc3Mapper::isSupported() const {
+bool Gralloc3Mapper::isLoaded() const {
return mMapper != nullptr;
}
@@ -192,7 +192,8 @@
}
status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, void** outData) const {
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
IMapper::Rect accessRegion = sGralloc3Rect(bounds);
@@ -208,12 +209,19 @@
Error error;
auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpData) {
+ [&](const auto& tmpError, const auto& tmpData,
+ const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outData = tmpData;
+ if (outBytesPerPixel) {
+ *outBytesPerPixel = tmpBytesPerPixel;
+ }
+ if (outBytesPerStride) {
+ *outBytesPerStride = tmpBytesPerStride;
+ }
});
// we own acquireFence even on errors
@@ -306,6 +314,36 @@
return releaseFence;
}
+status_t Gralloc3Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ bool* outSupported) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+ Error error;
+ auto ret = mMapper->isSupported(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpSupported) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ if (outSupported) {
+ *outSupported = tmpSupported;
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount,
+ error);
+ }
+
+ return static_cast<status_t>(error);
+}
+
Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -314,7 +352,7 @@
}
}
-bool Gralloc3Allocator::isSupported() const {
+bool Gralloc3Allocator::isLoaded() const {
return mAllocator != nullptr;
}
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index f408fcb..f487dfa 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -100,7 +100,7 @@
return;
}
- mInitCheck = initWithHandle(buffer->DuplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+ mInitCheck = initWithHandle(buffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
buffer->desc().width, buffer->desc().height,
static_cast<PixelFormat>(buffer->desc().format),
buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
@@ -236,15 +236,15 @@
return NO_ERROR;
}
-status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
const Rect lockBounds(width, height);
- status_t res = lock(inUsage, lockBounds, vaddr);
+ status_t res = lock(inUsage, lockBounds, vaddr, outBytesPerPixel, outBytesPerStride);
return res;
}
-status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
if (rect.left < 0 || rect.right > width ||
rect.top < 0 || rect.bottom > height) {
ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -252,7 +252,10 @@
width, height);
return BAD_VALUE;
}
- status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr);
+
+ status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel,
+ outBytesPerStride);
+
return res;
}
@@ -283,22 +286,22 @@
return res;
}
-status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd)
-{
+status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
const Rect lockBounds(width, height);
- status_t res = lockAsync(inUsage, lockBounds, vaddr, fenceFd);
+ status_t res =
+ lockAsync(inUsage, lockBounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
return res;
}
-status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
- void** vaddr, int fenceFd)
-{
- return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd);
+status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
+ return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
}
-status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage,
- uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd)
-{
+status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
+ const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
if (rect.left < 0 || rect.right > width ||
rect.top < 0 || rect.bottom > height) {
ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -306,8 +309,10 @@
width, height);
return BAD_VALUE;
}
- status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
- inConsumerUsage, rect, vaddr, fenceFd);
+
+ status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect,
+ vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
+
return res;
}
@@ -339,6 +344,13 @@
return res;
}
+status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint64_t inUsage,
+ bool* outSupported) const {
+ return mBufferMapper.isSupported(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+ outSupported);
+}
+
size_t GraphicBuffer::getFlattenedSize() const {
return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index efb5798..5a67dc4 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -48,12 +48,12 @@
GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
mAllocator = std::make_unique<const Gralloc3Allocator>(
reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
- if (!mAllocator->isSupported()) {
+ if (!mAllocator->isLoaded()) {
mAllocator = std::make_unique<const Gralloc2Allocator>(
reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
}
- if (!mAllocator->isSupported()) {
+ if (!mAllocator->isLoaded()) {
LOG_ALWAYS_FATAL("gralloc-allocator is missing");
}
}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index b049329..06981c3 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -51,11 +51,11 @@
GraphicBufferMapper::GraphicBufferMapper() {
mMapper = std::make_unique<const Gralloc3Mapper>();
- if (!mMapper->isSupported()) {
+ if (!mMapper->isLoaded()) {
mMapper = std::make_unique<const Gralloc2Mapper>();
}
- if (!mMapper->isSupported()) {
+ if (!mMapper->isLoaded()) {
LOG_ALWAYS_FATAL("gralloc-mapper is missing");
}
}
@@ -102,10 +102,10 @@
return NO_ERROR;
}
-status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
- const Rect& bounds, void** vaddr)
-{
- return lockAsync(handle, usage, bounds, vaddr, -1);
+status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+ void** vaddr, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
+ return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride);
}
status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage,
@@ -125,21 +125,23 @@
return error;
}
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
-{
- return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+ void** vaddr, int fenceFd, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
+ return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel,
+ outBytesPerStride);
}
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
- uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
- void** vaddr, int fenceFd)
-{
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,
+ uint64_t consumerUsage, const Rect& bounds, void** vaddr,
+ int fenceFd, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
ATRACE_CALL();
const uint64_t usage = static_cast<uint64_t>(
android_convertGralloc1To0Usage(producerUsage, consumerUsage));
- return mMapper->lock(handle, usage, bounds, fenceFd, vaddr);
+ return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,
+ outBytesPerStride);
}
status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
@@ -159,5 +161,10 @@
return NO_ERROR;
}
+status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, bool* outSupported) {
+ return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
+}
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 54405e6..97ead21 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -3,3 +3,5 @@
mathias@google.com
romainguy@google.com
stoza@google.com
+jwcai@google.com
+tianyuj@google.com
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index c6a4a23..5c09032 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -21,21 +21,21 @@
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
#include <ui/BufferHubDefs.h>
+#include <ui/BufferHubEventFd.h>
#include <ui/BufferHubMetadata.h>
+#include <utils/NativeHandle.h>
namespace android {
class BufferHubBuffer {
public:
// Allocates a standalone BufferHubBuffer.
- static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height,
+ static std::unique_ptr<BufferHubBuffer> create(uint32_t width, uint32_t height,
uint32_t layerCount, uint32_t format,
uint64_t usage, size_t userMetadataSize);
- // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. Caller
- // should close and destroy the token after calling this function regardless of output.
- // TODO(b/122543147): use a movable wrapper for token
- static std::unique_ptr<BufferHubBuffer> Import(const native_handle_t* token);
+ // Imports the given token to a BufferHubBuffer. Not taking ownership of the token.
+ static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token);
BufferHubBuffer(const BufferHubBuffer&) = delete;
void operator=(const BufferHubBuffer&) = delete;
@@ -51,62 +51,63 @@
// Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle
// after use.
- native_handle_t* DuplicateHandle() {
+ native_handle_t* duplicateHandle() {
return native_handle_clone(mBufferHandle.getNativeHandle());
}
+ const BufferHubEventFd& eventFd() const { return mEventFd; }
+
// Returns the current value of MetadataHeader::buffer_state.
- uint32_t buffer_state() {
- return mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire);
- }
+ uint32_t bufferState() const { return mBufferState->load(std::memory_order_acquire); }
// A state mask which is unique to a buffer hub client among all its siblings sharing the same
// concrete graphic buffer.
- uint32_t client_state_mask() const { return mClientStateMask; }
+ uint32_t clientStateMask() const { return mClientStateMask; }
- size_t user_metadata_size() const { return mMetadata.user_metadata_size(); }
+ size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
// Returns true if the BufferClient is still alive.
- bool IsConnected() const { return mBufferClient->ping().isOk(); }
+ bool isConnected() const { return mBufferClient->ping().isOk(); }
// Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask,
// valid metadata and valid buffer client
- bool IsValid() const;
+ bool isValid() const;
// Gains the buffer for exclusive write permission. Read permission is implied once a buffer is
// gained.
// The buffer can be gained as long as there is no other client in acquired or gained state.
- int Gain();
+ int gain();
// Posts the gained buffer for other buffer clients to use the buffer.
// The buffer can be posted iff the buffer state for this client is gained.
// After posting the buffer, this client is put to released state and does not have access to
// the buffer for this cycle of the usage of the buffer.
- int Post();
+ int post();
// Acquires the buffer for shared read permission.
// The buffer can be acquired iff the buffer state for this client is posted.
- int Acquire();
+ int acquire();
// Releases the buffer.
// The buffer can be released from any buffer state.
// After releasing the buffer, this client no longer have any permissions to the buffer for the
// current cycle of the usage of the buffer.
- int Release();
+ int release();
+
+ // Returns whether the buffer is released by all active clients or not.
+ bool isReleased() const;
// Creates a token that stands for this BufferHubBuffer client and could be used for Import to
// create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
- // gralloc buffer and ashmem region for metadata. Note that the caller owns the token and
- // should free it after use.
+ // gralloc buffer and ashmem region for metadata. Not taking ownership of the token.
// Returns a valid token on success, nullptr on failure.
- // TODO(b/122543147): use a movable wrapper for token
- native_handle_t* Duplicate();
+ sp<NativeHandle> duplicate();
private:
BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
uint64_t usage, size_t userMetadataSize);
- BufferHubBuffer(const native_handle_t* token);
+ BufferHubBuffer(const sp<NativeHandle>& token);
int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
@@ -123,13 +124,16 @@
// Wraps the gralloc buffer handle of this buffer.
hardware::hidl_handle mBufferHandle;
+ // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer.
+ BufferHubEventFd mEventFd;
+
// An ashmem-based metadata object. The same shared memory are mapped to the
// bufferhubd daemon and all buffer clients.
BufferHubMetadata mMetadata;
// Shortcuts to the atomics inside the header of mMetadata.
- std::atomic<uint32_t>* buffer_state_ = nullptr;
- std::atomic<uint32_t>* fence_state_ = nullptr;
- std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+ std::atomic<uint32_t>* mBufferState = nullptr;
+ std::atomic<uint32_t>* mFenceState = nullptr;
+ std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
// HwBinder backend
sp<frameworks::bufferhub::V1_0::IBufferClient> mBufferClient;
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h
index 069f0dc..722a060 100644
--- a/libs/ui/include/ui/BufferHubDefs.h
+++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -63,19 +63,19 @@
static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U;
// Returns true if any of the client is in gained state.
-static inline bool AnyClientGained(uint32_t state) {
+static inline bool isAnyClientGained(uint32_t state) {
uint32_t high_bits = state >> kMaxNumberOfClients;
uint32_t low_bits = state & kLowbitsMask;
return high_bits == low_bits && low_bits != 0U;
}
// Returns true if the input client is in gained state.
-static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) {
+static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) {
return state == client_bit_mask;
}
// Returns true if any of the client is in posted state.
-static inline bool AnyClientPosted(uint32_t state) {
+static inline bool isAnyClientPosted(uint32_t state) {
uint32_t high_bits = state >> kMaxNumberOfClients;
uint32_t low_bits = state & kLowbitsMask;
uint32_t posted_or_acquired = high_bits ^ low_bits;
@@ -83,7 +83,7 @@
}
// Returns true if the input client is in posted state.
-static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) {
+static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) {
uint32_t client_bits = state & client_bit_mask;
if (client_bits == 0U) return false;
uint32_t low_bits = client_bits & kLowbitsMask;
@@ -91,7 +91,7 @@
}
// Return true if any of the client is in acquired state.
-static inline bool AnyClientAcquired(uint32_t state) {
+static inline bool isAnyClientAcquired(uint32_t state) {
uint32_t high_bits = state >> kMaxNumberOfClients;
uint32_t low_bits = state & kLowbitsMask;
uint32_t posted_or_acquired = high_bits ^ low_bits;
@@ -99,26 +99,21 @@
}
// Return true if the input client is in acquired state.
-static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) {
uint32_t client_bits = state & client_bit_mask;
if (client_bits == 0U) return false;
uint32_t high_bits = client_bits & kHighBitsMask;
return high_bits == 0U;
}
-// Returns true if all clients are in released state.
-static inline bool IsBufferReleased(uint32_t state) {
- return state == 0U;
-}
-
// Returns true if the input client is in released state.
-static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
+static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) {
return (state & client_bit_mask) == 0U;
}
// Returns the next available buffer client's client_state_masks.
// @params union_bits. Union of all existing clients' client_state_masks.
-static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) {
+static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) {
uint32_t low_union = union_bits & kLowbitsMask;
if (low_union == kLowbitsMask) return 0U;
uint32_t incremented = low_union + 1U;
@@ -170,15 +165,16 @@
*
* It's definition should follow the following format:
* {
- * NumFds = 1,
+ * NumFds = 2,
* NumInts = 3,
* data[0] = Ashmem fd for BufferHubMetadata,
- * data[1] = buffer id,
- * data[2] = client state bit mask,
- * data[3] = user metadata size,
+ * data[1] = event fd,
+ * data[2] = buffer id,
+ * data[3] = client state bit mask,
+ * data[4] = user metadata size,
* }
*/
-static constexpr int kBufferInfoNumFds = 1;
+static constexpr int kBufferInfoNumFds = 2;
static constexpr int kBufferInfoNumInts = 3;
} // namespace BufferHubDefs
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h
index 0e856bd..8772304 100644
--- a/libs/ui/include/ui/BufferHubEventFd.h
+++ b/libs/ui/include/ui/BufferHubEventFd.h
@@ -30,6 +30,12 @@
BufferHubEventFd();
/**
+ * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes
+ * ownership.
+ */
+ BufferHubEventFd(int fd);
+
+ /**
* Returns whether this BufferHubEventFd holds a valid event_fd.
*/
bool isValid() const { return get() >= 0; }
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
index 2121894..3482507 100644
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -33,12 +33,12 @@
// @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata
// shared memory region to be allocated is the size of canonical
// BufferHubDefs::MetadataHeader plus userMetadataSize.
- static BufferHubMetadata Create(size_t userMetadataSize);
+ static BufferHubMetadata create(size_t userMetadataSize);
// Imports an existing BufferHubMetadata from an ashmem FD.
//
// @param ashmemFd Ashmem file descriptor representing an ashmem region.
- static BufferHubMetadata Import(unique_fd ashmemFd);
+ static BufferHubMetadata import(unique_fd ashmemFd);
BufferHubMetadata() = default;
@@ -63,13 +63,13 @@
// Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
// has been mapped into virtual address space.
- bool IsValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
+ bool isValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
- size_t user_metadata_size() const { return mUserMetadataSize; }
- size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
+ size_t userMetadataSize() const { return mUserMetadataSize; }
+ size_t metadataSize() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
- const unique_fd& ashmem_fd() const { return mAshmemFd; }
- BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
+ const unique_fd& ashmemFd() const { return mAshmemFd; }
+ BufferHubDefs::MetadataHeader* metadataHeader() { return mMetadataHeader; }
private:
BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
diff --git a/libs/ui/include/ui/ConfigStoreTypes.h b/libs/ui/include/ui/ConfigStoreTypes.h
new file mode 100644
index 0000000..37a2bd5
--- /dev/null
+++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
+namespace android {
+namespace ui {
+
+using android::hardware::configstore::V1_2::DisplayPrimaries;
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 92bf043..6cc23f0 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -31,7 +31,7 @@
public:
virtual ~GrallocMapper();
- virtual bool isSupported() const = 0;
+ virtual bool isLoaded() const = 0;
virtual status_t createDescriptor(void* bufferDescriptorInfo,
void* outBufferDescriptor) const = 0;
@@ -56,7 +56,8 @@
// The ownership of acquireFence is always transferred to the callee, even
// on errors.
virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, void** outData) const = 0;
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const = 0;
// The ownership of acquireFence is always transferred to the callee, even
// on errors.
@@ -66,6 +67,15 @@
// unlock returns a fence sync object (or -1) and the fence sync object is
// owned by the caller
virtual int unlock(buffer_handle_t bufferHandle) const = 0;
+
+ // isSupported queries whether or not a buffer with the given width, height,
+ // format, layer count, and usage can be allocated on the device. If
+ // *outSupported is set to true, a buffer with the given specifications may be successfully
+ // allocated if resources are available. If false, a buffer with the given specifications will
+ // never successfully allocate on this device. Note that this function is not guaranteed to be
+ // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
+ virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
};
// A wrapper to IAllocator
@@ -73,7 +83,7 @@
public:
virtual ~GrallocAllocator();
- virtual bool isSupported() const = 0;
+ virtual bool isLoaded() const = 0;
virtual std::string dumpDebugInfo() const = 0;
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index e03cb43..948f597 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -36,7 +36,7 @@
Gralloc2Mapper();
- bool isSupported() const override;
+ bool isLoaded() const override;
status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
@@ -53,13 +53,17 @@
uint32_t* outNumInts) const override;
status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, void** outData) const override;
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const override;
status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
int acquireFence, android_ycbcr* ycbcr) const override;
int unlock(buffer_handle_t bufferHandle) const override;
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+
private:
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
@@ -75,7 +79,7 @@
// time.
Gralloc2Allocator(const Gralloc2Mapper& mapper);
- bool isSupported() const override;
+ bool isLoaded() const override;
std::string dumpDebugInfo() const override;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 510ce4a..0965f52 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -35,7 +35,7 @@
Gralloc3Mapper();
- bool isSupported() const override;
+ bool isLoaded() const override;
status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
@@ -52,13 +52,17 @@
uint32_t* outNumInts) const override;
status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, void** outData) const override;
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const override;
status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
int acquireFence, android_ycbcr* ycbcr) const override;
int unlock(buffer_handle_t bufferHandle) const override;
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+
private:
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
@@ -73,7 +77,7 @@
// time.
Gralloc3Allocator(const Gralloc3Mapper& mapper);
- bool isSupported() const override;
+ bool isLoaded() const override;
std::string dumpDebugInfo() const override;
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index b73ca2b..4d4ee68 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -169,24 +169,35 @@
bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
- status_t lock(uint32_t inUsage, void** vaddr);
- status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
+ // For the following two lock functions, if bytesPerStride or bytesPerPixel
+ // are unknown or variable, -1 will be returned
+ status_t lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
+ status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
// For HAL_PIXEL_FORMAT_YCbCr_420_888
status_t lockYCbCr(uint32_t inUsage, android_ycbcr *ycbcr);
status_t lockYCbCr(uint32_t inUsage, const Rect& rect,
android_ycbcr *ycbcr);
status_t unlock();
- status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd);
- status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr,
- int fenceFd);
- status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
- const Rect& rect, void** vaddr, int fenceFd);
+ // For the following three lockAsync functions, if bytesPerStride or bytesPerPixel
+ // are unknown or variable, -1 will be returned
+ status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
+ status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
+ status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect,
+ void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr,
int fenceFd);
status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
android_ycbcr *ycbcr, int fenceFd);
status_t unlockAsync(int *fenceFd);
+ status_t isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint64_t inUsage, bool* outSupported) const;
+
ANativeWindowBuffer* getNativeBuffer() const;
// for debugging
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 156bd7a..77c99cc 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -56,20 +56,21 @@
void getTransportSize(buffer_handle_t handle,
uint32_t* outTransportNumFds, uint32_t* outTransportNumInts);
- status_t lock(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr);
+ status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
status_t lockYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr);
status_t unlock(buffer_handle_t handle);
- status_t lockAsync(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
+ status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+ int fenceFd, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
- status_t lockAsync(buffer_handle_t handle,
- uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
- void** vaddr, int fenceFd);
+ status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage,
+ const Rect& bounds, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
status_t lockAsyncYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
@@ -77,6 +78,9 @@
status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported);
+
const GrallocMapper& getGrallocMapper() const {
return reinterpret_cast<const GrallocMapper&>(*mMapper);
}
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index fb3c5f8..5dc56c8 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,20 +16,28 @@
#pragma once
+#include <cinttypes>
+#include <cstdint>
+
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
+#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
+
+namespace android {
+
+using PhysicalDisplayId = uint64_t;
+
// android::ui::* in this header file will alias different types as
// the HIDL interface is updated.
-namespace android {
namespace ui {
-using android::hardware::graphics::common::V1_1::PixelFormat;
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
} // namespace ui
} // namespace android
diff --git a/libs/ui/include_vndk/ui/ConfigStoreTypes.h b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
new file mode 100644
index 0000000..37a2bd5
--- /dev/null
+++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
+namespace android {
+namespace ui {
+
+using android::hardware::configstore::V1_2::DisplayPrimaries;
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index cd744cd..634bce1 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "BufferHubBufferTest"
+#include <errno.h>
+#include <sys/epoll.h>
+
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
#include <gmock/gmock.h>
@@ -23,19 +26,19 @@
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubEventFd.h>
namespace android {
namespace {
-using ::android::BufferHubDefs::AnyClientAcquired;
-using ::android::BufferHubDefs::AnyClientGained;
-using ::android::BufferHubDefs::AnyClientPosted;
-using ::android::BufferHubDefs::IsBufferReleased;
-using ::android::BufferHubDefs::IsClientAcquired;
-using ::android::BufferHubDefs::IsClientGained;
-using ::android::BufferHubDefs::IsClientPosted;
-using ::android::BufferHubDefs::IsClientReleased;
+using ::android::BufferHubDefs::isAnyClientAcquired;
+using ::android::BufferHubDefs::isAnyClientGained;
+using ::android::BufferHubDefs::isAnyClientPosted;
+using ::android::BufferHubDefs::isClientAcquired;
+using ::android::BufferHubDefs::isClientGained;
+using ::android::BufferHubDefs::isClientPosted;
+using ::android::BufferHubDefs::isClientReleased;
using ::android::BufferHubDefs::kMetadataHeaderSize;
using ::testing::IsNull;
using ::testing::NotNull;
@@ -78,106 +81,124 @@
};
void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() {
- b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+ b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
ASSERT_THAT(b1, NotNull());
- b1ClientMask = b1->client_state_mask();
+ b1ClientMask = b1->clientStateMask();
ASSERT_NE(b1ClientMask, 0U);
- native_handle_t* token = b1->Duplicate();
+ sp<NativeHandle> token = b1->duplicate();
ASSERT_THAT(token, NotNull());
- // TODO(b/122543147): use a movalbe wrapper for token
- b2 = BufferHubBuffer::Import(token);
- native_handle_close(token);
- native_handle_delete(token);
+ b2 = BufferHubBuffer::import(token);
ASSERT_THAT(b2, NotNull());
- b2ClientMask = b2->client_state_mask();
+ b2ClientMask = b2->clientStateMask();
ASSERT_NE(b2ClientMask, 0U);
ASSERT_NE(b2ClientMask, b1ClientMask);
}
TEST_F(BufferHubBufferTest, CreateBufferFails) {
// Buffer Creation will fail: BLOB format requires height to be 1.
- auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
+ auto b1 = BufferHubBuffer::create(kWidth, /*height=*/2, kLayerCount,
/*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize);
EXPECT_THAT(b1, IsNull());
// Buffer Creation will fail: user metadata size too large.
- auto b2 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ auto b2 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
/*userMetadataSize=*/std::numeric_limits<size_t>::max());
EXPECT_THAT(b2, IsNull());
// Buffer Creation will fail: user metadata size too large.
const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize;
- auto b3 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ auto b3 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
userMetadataSize);
EXPECT_THAT(b3, IsNull());
}
TEST_F(BufferHubBufferTest, CreateBuffer) {
- auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
kUserMetadataSize);
ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isConnected());
+ EXPECT_TRUE(b1->isValid());
EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc));
- EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize);
+ EXPECT_EQ(b1->userMetadataSize(), kUserMetadataSize);
}
TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
- auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
kUserMetadataSize);
ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isValid());
- native_handle_t* token = b1->Duplicate();
- EXPECT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
// The detached buffer should still be valid.
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isConnected());
+ EXPECT_TRUE(b1->isValid());
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
- native_handle_close(token);
- native_handle_delete(token);
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
ASSERT_THAT(b2, NotNull());
- EXPECT_TRUE(b2->IsValid());
+ EXPECT_TRUE(b2->isValid());
EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc()));
- EXPECT_EQ(b1->user_metadata_size(), b2->user_metadata_size());
+ EXPECT_EQ(b1->userMetadataSize(), b2->userMetadataSize());
// These two buffer instances are based on the same physical buffer under the
// hood, so they should share the same id.
EXPECT_EQ(b1->id(), b2->id());
- // We use client_state_mask() to tell those two instances apart.
- EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
+ // We use clientStateMask() to tell those two instances apart.
+ EXPECT_NE(b1->clientStateMask(), b2->clientStateMask());
// Both buffer instances should be in released state currently.
- EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
- EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
+ EXPECT_TRUE(b1->isReleased());
+ EXPECT_TRUE(b2->isReleased());
+
+ // The event fd should behave like duped event fds.
+ const BufferHubEventFd& eventFd1 = b1->eventFd();
+ ASSERT_GE(eventFd1.get(), 0);
+ const BufferHubEventFd& eventFd2 = b2->eventFd();
+ ASSERT_GE(eventFd2.get(), 0);
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ // Add eventFd1 to epoll set, and signal eventFd2.
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd2.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd2.signal();
+ eventFd2.clear();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
}
TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
- auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
kUserMetadataSize);
ASSERT_THAT(b1, NotNull());
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isValid());
- native_handle_t* token = b1->Duplicate();
- EXPECT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
// Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
b1.reset();
- // TODO(b/122543147): use a movalbe wrapper for token
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
- native_handle_close(token);
- native_handle_delete(token);
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
// Import should fail with INVALID_TOKEN
EXPECT_THAT(b2, IsNull());
@@ -185,195 +206,193 @@
// nullptr must not crash the service
TEST_F(BufferHubBufferTest, ImportNullToken) {
- auto b1 = BufferHubBuffer::Import(nullptr);
+ auto b1 = BufferHubBuffer::import(nullptr);
EXPECT_THAT(b1, IsNull());
}
-// TODO(b/118180214): remove the comment after ag/5856474 landed
-// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
TEST_F(BufferHubBufferTest, ImportInvalidToken) {
native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
token->data[0] = 0;
- auto b1 = BufferHubBuffer::Import(token);
+ auto b1 = BufferHubBuffer::import(NativeHandle::create(token, /*ownHandle=*/true));
native_handle_delete(token);
EXPECT_THAT(b1, IsNull());
}
TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
- ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+ ASSERT_TRUE(b1->isReleased());
// Successful gaining the buffer should change the buffer state bit of b1 to
// gained state, other client state bits to released state.
- EXPECT_EQ(b1->Gain(), 0);
- EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+ EXPECT_EQ(b1->gain(), 0);
+ EXPECT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
}
TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) {
- ASSERT_EQ(b1->Gain(), 0);
- auto current_buffer_state = b1->buffer_state();
- ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ auto currentBufferState = b1->bufferState();
+ ASSERT_TRUE(isClientGained(currentBufferState, b1ClientMask));
// Gaining from gained state by the same client should not return error.
- EXPECT_EQ(b1->Gain(), 0);
+ EXPECT_EQ(b1->gain(), 0);
// Gaining from gained state by another client should return error.
- EXPECT_EQ(b2->Gain(), -EBUSY);
+ EXPECT_EQ(b2->gain(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_EQ(b2->Acquire(), 0);
- ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
// Gaining from acquired state should fail.
- EXPECT_EQ(b1->Gain(), -EBUSY);
- EXPECT_EQ(b2->Gain(), -EBUSY);
+ EXPECT_EQ(b1->gain(), -EBUSY);
+ EXPECT_EQ(b2->gain(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
// Gaining a buffer who has other posted client should succeed.
- EXPECT_EQ(b1->Gain(), 0);
+ EXPECT_EQ(b1->gain(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
// A posted client should be able to gain the buffer when there is no other clients in
// acquired state.
- EXPECT_EQ(b2->Gain(), 0);
+ EXPECT_EQ(b2->gain(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
- EXPECT_EQ(b2->Post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
- EXPECT_EQ(b1->Post(), 0);
- auto current_buffer_state = b1->buffer_state();
- EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask));
- EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask));
+ EXPECT_EQ(b1->post(), 0);
+ auto currentBufferState = b1->bufferState();
+ EXPECT_TRUE(isClientReleased(currentBufferState, b1ClientMask));
+ EXPECT_TRUE(isClientPosted(currentBufferState, b2ClientMask));
}
TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
// Post from posted state should fail.
- EXPECT_EQ(b1->Post(), -EBUSY);
- EXPECT_EQ(b2->Post(), -EBUSY);
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_EQ(b2->Acquire(), 0);
- ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
// Posting from acquired state should fail.
- EXPECT_EQ(b1->Post(), -EBUSY);
- EXPECT_EQ(b2->Post(), -EBUSY);
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
- ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+ ASSERT_TRUE(b1->isReleased());
// Posting from released state should fail.
- EXPECT_EQ(b1->Post(), -EBUSY);
- EXPECT_EQ(b2->Post(), -EBUSY);
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
// Acquire from posted state should pass.
- EXPECT_EQ(b2->Acquire(), 0);
+ EXPECT_EQ(b2->acquire(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
// Acquire from released state should fail, although there are other clients
// in posted state.
- EXPECT_EQ(b1->Acquire(), -EBUSY);
+ EXPECT_EQ(b1->acquire(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_EQ(b2->Acquire(), 0);
- auto current_buffer_state = b1->buffer_state();
- ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ auto currentBufferState = b1->bufferState();
+ ASSERT_TRUE(isClientAcquired(currentBufferState, b2ClientMask));
// Acquiring from acquired state by the same client should not error out.
- EXPECT_EQ(b2->Acquire(), 0);
+ EXPECT_EQ(b2->acquire(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
- ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+ ASSERT_TRUE(b1->isReleased());
// Acquiring form released state should fail.
- EXPECT_EQ(b1->Acquire(), -EBUSY);
- EXPECT_EQ(b2->Acquire(), -EBUSY);
+ EXPECT_EQ(b1->acquire(), -EBUSY);
+ EXPECT_EQ(b2->acquire(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
// Acquiring from gained state should fail.
- EXPECT_EQ(b1->Acquire(), -EBUSY);
- EXPECT_EQ(b2->Acquire(), -EBUSY);
+ EXPECT_EQ(b1->acquire(), -EBUSY);
+ EXPECT_EQ(b2->acquire(), -EBUSY);
}
TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
- ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+ ASSERT_TRUE(b1->isReleased());
- EXPECT_EQ(b1->Release(), 0);
+ EXPECT_EQ(b1->release(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
- ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+ ASSERT_TRUE(b1->isReleased());
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
- EXPECT_EQ(b1->Release(), 0);
+ EXPECT_EQ(b1->release(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
- EXPECT_EQ(b2->Release(), 0);
+ EXPECT_EQ(b2->release(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_EQ(b2->Acquire(), 0);
- ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
- EXPECT_EQ(b2->Release(), 0);
+ EXPECT_EQ(b2->release(), 0);
}
TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) {
@@ -381,60 +400,54 @@
// Test if this set of basic operation succeed:
// Producer post three times to the consumer, and released by consumer.
for (int i = 0; i < 3; ++i) {
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
- ASSERT_EQ(b2->Acquire(), 0);
- ASSERT_EQ(b2->Release(), 0);
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_EQ(b2->release(), 0);
}
}
TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) {
// Create a poducer buffer and gain.
std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
kUserMetadataSize);
ASSERT_THAT(b1, NotNull());
- ASSERT_EQ(b1->Gain(), 0);
+ ASSERT_EQ(b1->gain(), 0);
// Create a consumer of the buffer and test if the consumer can acquire the
// buffer if producer posts.
- // TODO(b/122543147): use a movalbe wrapper for token
- native_handle_t* token = b1->Duplicate();
- ASSERT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
- native_handle_close(token);
- native_handle_delete(token);
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
ASSERT_THAT(b2, NotNull());
- ASSERT_NE(b1->client_state_mask(), b2->client_state_mask());
+ ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
- ASSERT_EQ(b1->Post(), 0);
- EXPECT_EQ(b2->Acquire(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ EXPECT_EQ(b2->acquire(), 0);
}
TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) {
// Create a poducer buffer and post.
std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
kUserMetadataSize);
- ASSERT_EQ(b1->Gain(), 0);
- ASSERT_EQ(b1->Post(), 0);
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
// Create a consumer of the buffer and test if the consumer can acquire the
// buffer if producer posts.
- // TODO(b/122543147): use a movalbe wrapper for token
- native_handle_t* token = b1->Duplicate();
- ASSERT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
- std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
- native_handle_close(token);
- native_handle_delete(token);
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
ASSERT_THAT(b2, NotNull());
- ASSERT_NE(b1->client_state_mask(), b2->client_state_mask());
+ ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
- EXPECT_EQ(b2->Acquire(), 0);
+ EXPECT_EQ(b2->acquire(), 0);
}
} // namespace
diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp
index 92fb33f..ef1781f 100644
--- a/libs/ui/tests/BufferHubEventFd_test.cpp
+++ b/libs/ui/tests/BufferHubEventFd_test.cpp
@@ -35,6 +35,7 @@
const int kTimeout = 100;
const std::chrono::milliseconds kTimeoutMs(kTimeout);
+const int kTestRuns = 5;
using ::testing::Contains;
using BufferHubEventFdTest = ::testing::Test;
@@ -46,9 +47,9 @@
ASSERT_TRUE(eventFd.isValid());
base::unique_fd epollFd(epoll_create(64));
- epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
-
ASSERT_GE(epollFd.get(), 0);
+
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
std::array<epoll_event, 1> events;
@@ -59,6 +60,96 @@
// The epoll fd is edge triggered, so it only responds to the eventFd once.
EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that it can receive consecutive signal.
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that it can receive consecutive signal from a duplicated eventfd.
+ BufferHubEventFd dupEventFd(dup(eventFd.get()));
+ ASSERT_TRUE(dupEventFd.isValid());
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ eventFd.signal();
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ // Make sure that the epoll set has not been signal yet.
+ std::array<epoll_event, 1> events;
+ ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that adding an signaled fd into this epoll set will trigger the epoll set.
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ eventFd.signal();
+
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ for (int i = 0; i < kTestRuns; ++i) {
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ }
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ BufferHubEventFd dupEventFd(dup(eventFd.get()));
+ ASSERT_TRUE(dupEventFd.isValid());
+
+ std::array<epoll_event, 1> events;
+ for (int i = 0; i < kTestRuns; ++i) {
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ }
}
TEST_F(BufferHubEventFdTest, EventFd_testClear) {
@@ -91,22 +182,21 @@
// Technically, the dupliated eventFd and the original eventFd are pointing
// to the same kernel object. This test signals the duplicated eventFd but epolls the origianl
// eventFd.
- base::unique_fd dupedEventFd(dup(eventFd.get()));
+ BufferHubEventFd dupedEventFd(dup(eventFd.get()));
ASSERT_GE(dupedEventFd.get(), 0);
std::array<epoll_event, 1> events;
EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
- eventfd_write(dupedEventFd.get(), 1);
+ dupedEventFd.signal();
EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
// The epoll fd is edge triggered, so it only responds to the eventFd once.
EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
- eventfd_write(dupedEventFd.get(), 1);
+ dupedEventFd.signal();
- eventfd_t value;
- eventfd_read(dupedEventFd.get(), &value);
+ dupedEventFd.clear();
EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
}
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
index 11f8e57..f02c4fc 100644
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -17,8 +17,6 @@
#include <gtest/gtest.h>
#include <ui/BufferHubMetadata.h>
-using android::BufferHubDefs::IsBufferReleased;
-
namespace android {
namespace dvr {
@@ -27,70 +25,73 @@
class BufferHubMetadataTest : public ::testing::Test {};
TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) {
- BufferHubMetadata m1 =
- BufferHubMetadata::Create(std::numeric_limits<uint32_t>::max());
- EXPECT_FALSE(m1.IsValid());
+ BufferHubMetadata m1 = BufferHubMetadata::create(std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(m1.isValid());
}
TEST_F(BufferHubMetadataTest, Create_Success) {
- BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
- EXPECT_TRUE(m1.IsValid());
- EXPECT_NE(m1.metadata_header(), nullptr);
+ BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
}
TEST_F(BufferHubMetadataTest, Import_Success) {
- BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
- EXPECT_TRUE(m1.IsValid());
- EXPECT_NE(m1.metadata_header(), nullptr);
+ BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
- unique_fd h2 = unique_fd(dup(m1.ashmem_fd().get()));
- EXPECT_NE(h2.get(), -1);
+ unique_fd h2 = unique_fd(dup(m1.ashmemFd().get()));
+ EXPECT_NE(h2.get(), -1);
- BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2));
- EXPECT_EQ(h2.get(), -1);
- EXPECT_TRUE(m1.IsValid());
- BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
- EXPECT_NE(mh1, nullptr);
+ BufferHubMetadata m2 = BufferHubMetadata::import(std::move(h2));
+ EXPECT_EQ(h2.get(), -1);
+ EXPECT_TRUE(m1.isValid());
+ BufferHubDefs::MetadataHeader* mh1 = m1.metadataHeader();
+ EXPECT_NE(mh1, nullptr);
- EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load()));
+ // Check if the newly allocated buffer is initialized in released state (i.e.
+ // state equals to 0U).
+ EXPECT_TRUE(mh1->buffer_state.load() == 0U);
- EXPECT_TRUE(m2.IsValid());
- BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
- EXPECT_NE(mh2, nullptr);
+ EXPECT_TRUE(m2.isValid());
+ BufferHubDefs::MetadataHeader* mh2 = m2.metadataHeader();
+ EXPECT_NE(mh2, nullptr);
- EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load()));
+ // Check if the newly allocated buffer is initialized in released state (i.e.
+ // state equals to 0U).
+ EXPECT_TRUE(mh2->buffer_state.load() == 0U);
}
TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
- BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int));
- EXPECT_TRUE(m1.IsValid());
- EXPECT_NE(m1.metadata_header(), nullptr);
- EXPECT_NE(m1.ashmem_fd().get(), -1);
- EXPECT_EQ(m1.user_metadata_size(), sizeof(int));
+ BufferHubMetadata m1 = BufferHubMetadata::create(sizeof(int));
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
+ EXPECT_NE(m1.ashmemFd().get(), -1);
+ EXPECT_EQ(m1.userMetadataSize(), sizeof(int));
- BufferHubMetadata m2 = std::move(m1);
+ BufferHubMetadata m2 = std::move(m1);
- // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
- EXPECT_EQ(m1.metadata_header(), nullptr);
- EXPECT_NE(m2.metadata_header(), nullptr);
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+ EXPECT_EQ(m1.metadataHeader(), nullptr);
+ EXPECT_NE(m2.metadataHeader(), nullptr);
- EXPECT_EQ(m1.ashmem_fd().get(), -1);
- EXPECT_NE(m2.ashmem_fd().get(), -1);
+ EXPECT_EQ(m1.ashmemFd().get(), -1);
+ EXPECT_NE(m2.ashmemFd().get(), -1);
- EXPECT_EQ(m1.user_metadata_size(), 0U);
- EXPECT_EQ(m2.user_metadata_size(), sizeof(int));
+ EXPECT_EQ(m1.userMetadataSize(), 0U);
+ EXPECT_EQ(m2.userMetadataSize(), sizeof(int));
- BufferHubMetadata m3{std::move(m2)};
+ BufferHubMetadata m3{std::move(m2)};
- // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
- EXPECT_EQ(m2.metadata_header(), nullptr);
- EXPECT_NE(m3.metadata_header(), nullptr);
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+ EXPECT_EQ(m2.metadataHeader(), nullptr);
+ EXPECT_NE(m3.metadataHeader(), nullptr);
- EXPECT_EQ(m2.ashmem_fd().get(), -1);
- EXPECT_NE(m3.ashmem_fd().get(), -1);
+ EXPECT_EQ(m2.ashmemFd().get(), -1);
+ EXPECT_NE(m3.ashmemFd().get(), -1);
- EXPECT_EQ(m2.user_metadata_size(), 0U);
- EXPECT_EQ(m3.user_metadata_size(), sizeof(int));
+ EXPECT_EQ(m2.userMetadataSize(), 0U);
+ EXPECT_EQ(m3.userMetadataSize(), sizeof(int));
}
} // namespace dvr
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index 5b46454..c767ce0 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -37,10 +37,10 @@
TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
kTestUsage, /*userMetadataSize=*/0);
ASSERT_NE(b1, nullptr);
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isValid());
sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
EXPECT_TRUE(gb->isBufferHubBuffer());
@@ -61,10 +61,10 @@
TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) {
std::unique_ptr<BufferHubBuffer> b1 =
- BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
kTestUsage, /*userMetadataSize=*/0);
EXPECT_NE(b1, nullptr);
- EXPECT_TRUE(b1->IsValid());
+ EXPECT_TRUE(b1->isValid());
int b1_id = b1->id();
EXPECT_GE(b1_id, 0);
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
new file mode 100644
index 0000000..e95a080
--- /dev/null
+++ b/libs/vibrator/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libvibrator",
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ local_include_dirs: ["include/"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libvibrator_aidl",
+ "*.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wno-unused-parameter",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
new file mode 100644
index 0000000..f6fc19e
--- /dev/null
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vibrator/ExternalVibration.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) {
+ out->writeInt32(attrs.usage);
+ out->writeInt32(attrs.content_type);
+ out->writeInt32(attrs.source);
+ out->writeInt32(attrs.flags);
+}
+
+void readAudioAttributes(audio_attributes_t* attrs, const android::Parcel* in) {
+ attrs->usage = static_cast<audio_usage_t>(in->readInt32());
+ attrs->content_type = static_cast<audio_content_type_t>(in->readInt32());
+ attrs->source = static_cast<audio_source_t>(in->readInt32());
+ attrs->flags = static_cast<audio_flags_mask_t>(in->readInt32());
+}
+
+namespace android {
+namespace os {
+
+ExternalVibration::ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+ sp<IExternalVibrationController> controller) :
+ mUid(uid), mPkg(pkg), mAttrs(attrs), mController(controller) { }
+
+status_t ExternalVibration::writeToParcel(Parcel* parcel) const {
+ parcel->writeInt32(mUid);
+ parcel->writeString16(String16(mPkg.c_str()));
+ writeAudioAttributes(mAttrs, parcel);
+ parcel->writeStrongBinder(IInterface::asBinder(mController));
+ parcel->writeStrongBinder(mToken);
+ return OK;
+}
+status_t ExternalVibration::readFromParcel(const Parcel* parcel) {
+ mUid = parcel->readInt32();
+ String8 pkgStr8 = String8(parcel->readString16());
+ mPkg = pkgStr8.c_str();
+ readAudioAttributes(&mAttrs, parcel);
+ mController = IExternalVibrationController::asInterface(parcel->readStrongBinder());
+ mToken = parcel->readStrongBinder();
+ return OK;
+}
+
+inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const {
+ return mToken == rhs.mToken;
+}
+
+} // namespace os
+} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
new file mode 100644
index 0000000..c6eb3d1
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EXTERNAL_VIBRATION_H
+#define ANDROID_EXTERNAL_VIBRATION_H
+
+#include <android/os/IExternalVibrationController.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
+#include <system/audio.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+class ExternalVibration : public Parcelable, public virtual RefBase {
+public :
+ ExternalVibration() = default;
+ ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+ sp<IExternalVibrationController> controller);
+ virtual ~ExternalVibration() = default;
+ ExternalVibration(const ExternalVibration&) = default;
+
+ bool operator==(const ExternalVibration&) const;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ int32_t getUid() const { return mUid; }
+ std::string getPackage() const { return mPkg; }
+ audio_attributes_t getAudioAttributes() const { return mAttrs; }
+ sp<IExternalVibrationController> getController() { return mController; }
+
+
+private:
+ int32_t mUid;
+ std::string mPkg;
+ audio_attributes_t mAttrs;
+ sp<IExternalVibrationController> mController;
+ sp<IBinder> mToken = new BBinder();
+};
+
+} // namespace android
+} // namespace os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index ed5a992..27ab024 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -10,6 +10,7 @@
#include <mutex>
#include <thread>
+namespace {
#define RETRY_EINTR(fnc_call) \
([&]() -> decltype(fnc_call) { \
decltype(fnc_call) result; \
@@ -19,18 +20,18 @@
return result; \
})()
-using android::BufferHubDefs::AnyClientAcquired;
-using android::BufferHubDefs::AnyClientGained;
-using android::BufferHubDefs::AnyClientPosted;
-using android::BufferHubDefs::IsBufferReleased;
-using android::BufferHubDefs::IsClientAcquired;
-using android::BufferHubDefs::IsClientPosted;
-using android::BufferHubDefs::IsClientReleased;
+using android::BufferHubDefs::isAnyClientAcquired;
+using android::BufferHubDefs::isAnyClientGained;
+using android::BufferHubDefs::isAnyClientPosted;
+using android::BufferHubDefs::isClientAcquired;
+using android::BufferHubDefs::isClientPosted;
+using android::BufferHubDefs::isClientReleased;
using android::BufferHubDefs::kFirstClientBitMask;
using android::dvr::ConsumerBuffer;
using android::dvr::ProducerBuffer;
using android::pdx::LocalHandle;
using android::pdx::Status;
+using LibBufferHubTest = ::testing::Test;
const int kWidth = 640;
const int kHeight = 480;
@@ -42,7 +43,15 @@
android::BufferHubDefs::kMaxNumberOfClients - 1;
const int kPollTimeoutMs = 100;
-using LibBufferHubTest = ::testing::Test;
+// Helper function to poll the eventfd in BufferHubBase.
+template <class BufferHubBase>
+int PollBufferEvent(const std::unique_ptr<BufferHubBase>& buffer,
+ int timeout_ms = kPollTimeoutMs) {
+ pollfd p = {buffer->event_fd(), POLLIN, 0};
+ return poll(&p, 1, timeout_ms);
+}
+
+} // namespace
TEST_F(LibBufferHubTest, TestBasicUsage) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
@@ -62,36 +71,36 @@
EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
// Initial state: producer not available, consumers not available.
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->Post(LocalHandle()));
// New state: producer not available, consumers available.
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2)));
LocalHandle fence;
EXPECT_EQ(0, c1->Acquire(&fence));
- EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2)));
EXPECT_EQ(0, c2->Acquire(&fence));
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
EXPECT_EQ(0, c1->Release(LocalHandle()));
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, c2->Discard());
- EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, p->Gain(&fence));
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
}
TEST_F(LibBufferHubTest, TestEpoll) {
@@ -226,7 +235,7 @@
// Release in acquired state should succeed.
EXPECT_EQ(0, c->Release(LocalHandle()));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
// Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
@@ -259,7 +268,7 @@
// Post in gained state should succeed.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientPosted(p->buffer_state()));
// Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -267,11 +276,11 @@
EXPECT_FALSE(invalid_fence.IsValid());
// Acquire in posted state should succeed.
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(AnyClientAcquired(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientAcquired(p->buffer_state()));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -282,9 +291,9 @@
// Release in acquired state should succeed.
EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_TRUE(p->is_released());
// Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -295,7 +304,7 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(AnyClientGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
// Acquire and gain in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -320,7 +329,7 @@
ASSERT_TRUE(c.get() != nullptr);
ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
- ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
// Gain in posted state should only succeed with gain_posted_buffer = true.
LocalHandle invalid_fence;
@@ -337,7 +346,7 @@
ASSERT_TRUE(c.get() != nullptr);
ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
- ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
// GainAsync in posted state should only succeed with gain_posted_buffer
// equals true.
@@ -355,9 +364,9 @@
ASSERT_EQ(0, p->Post(LocalHandle()));
// Producer state bit is in released state after post, other clients shall be
// in posted state although there is no consumer of this buffer yet.
- ASSERT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
- ASSERT_FALSE(IsBufferReleased(p->buffer_state()));
- ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
+ ASSERT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask()));
+ ASSERT_TRUE(p->is_released());
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
// Gain in released state should succeed.
LocalHandle invalid_fence;
@@ -374,7 +383,7 @@
for (size_t i = 0; i < kMaxConsumerCount; ++i) {
cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
- EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state()));
+ EXPECT_TRUE(cs[i]->is_released());
EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
}
@@ -384,25 +393,25 @@
// Post the producer should trigger all consumers to be available.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+ EXPECT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask()));
for (size_t i = 0; i < kMaxConsumerCount; ++i) {
EXPECT_TRUE(
- IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
- EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
+ isClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(cs[i])));
EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
EXPECT_TRUE(
- IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
+ isClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
}
// All consumers have to release before the buffer is considered to be
// released.
for (size_t i = 0; i < kMaxConsumerCount; i++) {
- EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+ EXPECT_FALSE(p->is_released());
EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
}
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_TRUE(p->is_released());
// Buffer state cross all clients must be consistent.
for (size_t i = 0; i < kMaxConsumerCount; i++) {
@@ -415,22 +424,22 @@
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
EXPECT_EQ(0, p->GainAsync());
- EXPECT_TRUE(AnyClientGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(AnyClientGained(c->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(c->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer should signal already created consumer.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_TRUE(isAnyClientPosted(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(AnyClientAcquired(c->buffer_state()));
+ EXPECT_TRUE(isAnyClientAcquired(c->buffer_state()));
}
TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
@@ -438,22 +447,22 @@
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
EXPECT_EQ(0, p->GainAsync());
- EXPECT_TRUE(AnyClientGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer before any consumer gets created.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_TRUE(p->is_released());
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
// Newly created consumer will be signalled for the posted buffer although it
// is created after producer posting.
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsClientPosted(c->buffer_state(), c->client_state_mask()));
+ EXPECT_TRUE(isClientPosted(c->buffer_state(), c->client_state_mask()));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
}
@@ -472,7 +481,7 @@
// Post, acquire, and release the buffer..
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence));
EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence));
@@ -480,8 +489,8 @@
// executed before Release impulse gets executed by bufferhubd. Thus, here we
// need to wait until the releasd is confirmed before creating another
// consumer.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_TRUE(p->is_released());
// Create another consumer immediately after the release, should not make the
// buffer un-released.
@@ -489,9 +498,9 @@
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_TRUE(p->is_released());
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(AnyClientGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
}
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -508,14 +517,14 @@
EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
- EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LE(0, RETRY_EINTR(PollBufferEvent(c)));
LocalHandle fence;
Metadata m2 = {};
EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2)));
EXPECT_EQ(m.field1, m2.field1);
EXPECT_EQ(m.field2, m2.field2);
EXPECT_EQ(0, c->Release(LocalHandle()));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p, /*timeout_ms=*/0)));
}
TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
@@ -540,7 +549,7 @@
// buffer allocation.
OverSizedMetadata evil_meta = {};
EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata)));
- EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c)));
// It is ok to post metadata smaller than originally requested during
// buffer allocation.
@@ -652,7 +661,7 @@
// Should acquire a valid fence.
LocalHandle f2;
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&meta, &f2));
EXPECT_TRUE(f2.IsValid());
// The original fence and acquired fence should have different fd number.
@@ -669,7 +678,7 @@
// Should gain an invalid fence.
LocalHandle f3;
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, p->GainAsync(&meta, &f3));
EXPECT_FALSE(f3.IsValid());
@@ -678,7 +687,7 @@
// Should acquire a valid fence and it's already signalled.
LocalHandle f4;
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&meta, &f4));
EXPECT_TRUE(f4.IsValid());
EXPECT_LT(0, PollFd(f4.Get(), kPollTimeoutMs));
@@ -691,7 +700,7 @@
// Should gain a valid fence, which is already signaled.
LocalHandle f6;
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, p->GainAsync(&meta, &f6));
EXPECT_TRUE(f6.IsValid());
EXPECT_LT(0, PollFd(f6.Get(), kPollTimeoutMs));
@@ -711,14 +720,14 @@
EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
LocalHandle fence;
- EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
// Destroy the consumer who has acquired but not released the buffer.
c1 = nullptr;
// The buffer is now available for the producer to gain.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
// Newly added consumer is not able to acquire the buffer.
std::unique_ptr<ConsumerBuffer> c2 =
@@ -726,7 +735,7 @@
ASSERT_TRUE(c2.get() != nullptr);
const uint32_t client_state_mask2 = c2->client_state_mask();
EXPECT_NE(client_state_mask1, client_state_mask2);
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
// Producer should be able to gain.
@@ -745,7 +754,7 @@
EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
- EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
// c2 is created when the buffer is in posted state. buffer state for c1 is
// posted. Thus, c2 should be automatically set to posted and able to acquire.
@@ -754,7 +763,7 @@
ASSERT_TRUE(c2.get() != nullptr);
const uint32_t client_state_mask2 = c2->client_state_mask();
EXPECT_NE(client_state_mask1, client_state_mask2);
- EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c2)));
LocalHandle invalid_fence;
EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
@@ -769,7 +778,7 @@
const uint32_t client_state_mask3 = c3->client_state_mask();
EXPECT_NE(client_state_mask1, client_state_mask3);
EXPECT_NE(client_state_mask2, client_state_mask3);
- EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c3)));
EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
// Releasing c2 and c3 in normal ways.
@@ -780,7 +789,7 @@
c1 = nullptr;
// The buffer is now available for the producer to gain.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
// C4 is created in released state. Thus, it cannot gain the just posted
// buffer.
@@ -789,7 +798,7 @@
ASSERT_TRUE(c4.get() != nullptr);
const uint32_t client_state_mask4 = c4->client_state_mask();
EXPECT_NE(client_state_mask3, client_state_mask4);
- EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c3)));
EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
// Producer should be able to gain.
@@ -814,7 +823,7 @@
// Detach in posted state should fail.
EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0);
auto s1 = p->Detach();
EXPECT_FALSE(s1);
@@ -825,7 +834,7 @@
// Detach in released state should fail.
EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0);
s1 = p->Detach();
EXPECT_FALSE(s1);
@@ -838,12 +847,12 @@
EXPECT_TRUE(handle.valid());
// Both producer and consumer should have hangup.
- EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0);
auto s2 = p->GetEventMask(POLLHUP);
EXPECT_TRUE(s2);
EXPECT_EQ(s2.get(), POLLHUP);
- EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0);
s2 = p->GetEventMask(POLLHUP);
EXPECT_TRUE(s2);
EXPECT_EQ(s2.get(), POLLHUP);
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
index 8497f3e..b28d101 100644
--- a/libs/vr/libbufferhub/buffer_hub_base.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -196,12 +196,6 @@
return 0;
}
-int BufferHubBase::Poll(int timeout_ms) {
- ATRACE_NAME("BufferHubBase::Poll");
- pollfd p = {event_fd(), POLLIN, 0};
- return poll(&p, 1, timeout_ms);
-}
-
int BufferHubBase::Lock(int usage, int x, int y, int width, int height,
void** address) {
return buffer_.Lock(usage, x, y, width, height, address);
@@ -218,12 +212,5 @@
return ret;
}
-void BufferHubBase::GetBlobFds(int* fds, size_t* fds_count,
- size_t max_fds_count) const {
- size_t numFds = static_cast<size_t>(native_handle()->numFds);
- *fds_count = std::min(max_fds_count, numFds);
- std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
-}
-
} // namespace dvr
} // namespace android
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index b6ca64e..115e866 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -38,7 +38,7 @@
// The buffer can be acquired iff the buffer state for this client is posted.
uint32_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
client_state_mask())) {
ALOGE(
"%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
@@ -58,7 +58,7 @@
" when trying to acquire the buffer and modify the buffer state to "
"%" PRIx32 ". About to try again if the buffer is still posted.",
__FUNCTION__, current_buffer_state, updated_buffer_state);
- if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
client_state_mask())) {
ALOGE(
"%s: Failed to acquire the buffer. The buffer is no longer posted, "
@@ -144,7 +144,7 @@
// released state.
uint32_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ if (BufferHubDefs::isClientReleased(current_buffer_state,
client_state_mask())) {
return 0;
}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
index 889763a..fa39d08 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
@@ -21,19 +21,6 @@
// a file descriptor for the new channel or a negative error code.
Status<LocalChannelHandle> CreateConsumer();
- // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
- int Poll(int timeout_ms);
-
- // Locks the area specified by (x, y, width, height) for a specific usage. If
- // the usage is software then |addr| will be updated to point to the address
- // of the buffer in virtual memory. The caller should only access/modify the
- // pixels in the specified area. anything else is undefined behavior.
- int Lock(int usage, int x, int y, int width, int height, void** addr);
-
- // Must be called after Lock() when the caller has finished changing the
- // buffer.
- int Unlock();
-
// Gets a blob buffer that was created with ProducerBuffer::CreateBlob.
// Locking and Unlocking is handled internally. There's no need to Unlock
// after calling this method.
@@ -52,10 +39,6 @@
return LocalHandle(dup(native_handle()->data[0]));
}
- // Get up to |max_fds_count| file descriptors for accessing the blob shared
- // memory. |fds_count| will contain the actual number of file descriptors.
- void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
-
using Client::event_fd;
Status<int> GetEventMask(int events) {
@@ -135,6 +118,16 @@
int UpdateSharedFence(const LocalHandle& new_fence,
const LocalHandle& shared_fence);
+ // Locks the area specified by (x, y, width, height) for a specific usage. If
+ // the usage is software then |addr| will be updated to point to the address
+ // of the buffer in virtual memory. The caller should only access/modify the
+ // pixels in the specified area. anything else is undefined behavior.
+ int Lock(int usage, int x, int y, int width, int height, void** addr);
+
+ // Must be called after Lock() when the caller has finished changing the
+ // buffer.
+ int Unlock();
+
// IonBuffer that is shared between bufferhubd, producer, and consumers.
size_t metadata_buf_size_{0};
size_t user_metadata_size_{0};
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index f5761d5..e610e18 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -27,42 +27,38 @@
static constexpr uint32_t kFirstClientBitMask =
android::BufferHubDefs::kFirstClientBitMask;
-static inline bool AnyClientGained(uint32_t state) {
- return android::BufferHubDefs::AnyClientGained(state);
+static inline bool isAnyClientGained(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientGained(state);
}
-static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) {
- return android::BufferHubDefs::IsClientGained(state, client_bit_mask);
+static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientGained(state, client_bit_mask);
}
-static inline bool AnyClientPosted(uint32_t state) {
- return android::BufferHubDefs::AnyClientPosted(state);
+static inline bool isAnyClientPosted(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientPosted(state);
}
-static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) {
- return android::BufferHubDefs::IsClientPosted(state, client_bit_mask);
+static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientPosted(state, client_bit_mask);
}
-static inline bool AnyClientAcquired(uint32_t state) {
- return android::BufferHubDefs::AnyClientAcquired(state);
+static inline bool isAnyClientAcquired(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientAcquired(state);
}
-static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) {
- return android::BufferHubDefs::IsClientAcquired(state, client_bit_mask);
+static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientAcquired(state, client_bit_mask);
}
-static inline bool IsBufferReleased(uint32_t state) {
- return android::BufferHubDefs::IsBufferReleased(state);
-}
-
-static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
- return android::BufferHubDefs::IsClientReleased(state, client_bit_mask);
+static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientReleased(state, client_bit_mask);
}
// Returns the next available buffer client's client_state_masks.
// @params union_bits. Union of all existing clients' client_state_masks.
-static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) {
- return android::BufferHubDefs::FindNextAvailableClientStateMask(union_bits);
+static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) {
+ return android::BufferHubDefs::findNextAvailableClientStateMask(union_bits);
}
using MetadataHeader = android::BufferHubDefs::MetadataHeader;
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index edfdddf..3d88ba5 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -82,7 +82,7 @@
// The buffer can be posted iff the buffer state for this client is gained.
uint32_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ if (!BufferHubDefs::isClientGained(current_buffer_state,
client_state_mask())) {
ALOGE("%s: not gained, id=%d state=%" PRIx32 ".", __FUNCTION__, id(),
current_buffer_state);
@@ -103,7 +103,7 @@
"%" PRIx32
". About to try again if the buffer is still gained by this client.",
__FUNCTION__, current_buffer_state, updated_buffer_state);
- if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ if (!BufferHubDefs::isClientGained(current_buffer_state,
client_state_mask())) {
ALOGE(
"%s: Failed to post the buffer. The buffer is no longer gained, "
@@ -166,14 +166,14 @@
ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx32 ".", __FUNCTION__, id(),
current_buffer_state);
- if (BufferHubDefs::IsClientGained(current_buffer_state,
+ if (BufferHubDefs::isClientGained(current_buffer_state,
client_state_mask())) {
ALOGV("%s: already gained id=%d.", __FUNCTION__, id());
return 0;
}
- if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
- BufferHubDefs::AnyClientGained(current_buffer_state) ||
- (BufferHubDefs::AnyClientPosted(
+ if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::isAnyClientPosted(
current_buffer_state &
active_clients_bit_mask_->load(std::memory_order_acquire)) &&
!gain_posted_buffer)) {
@@ -195,9 +195,9 @@
"clients.",
__FUNCTION__, current_buffer_state, updated_buffer_state);
- if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
- BufferHubDefs::AnyClientGained(current_buffer_state) ||
- (BufferHubDefs::AnyClientPosted(
+ if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::isAnyClientPosted(
current_buffer_state &
active_clients_bit_mask_->load(std::memory_order_acquire)) &&
!gain_posted_buffer)) {
@@ -291,7 +291,7 @@
// TODO(b/112338294) Keep here for reference. Remove it after new logic is
// written.
/* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsClientGained(
+ if (!BufferHubDefs::isClientGained(
buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a ProducerBuffer when it's in gained state.
ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index d7833f3..2d3fa4a 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -532,7 +532,7 @@
Status<size_t> ProducerQueue::InsertBuffer(
const std::shared_ptr<ProducerBuffer>& buffer) {
if (buffer == nullptr ||
- !BufferHubDefs::IsClientGained(buffer->buffer_state(),
+ !BufferHubDefs::isClientGained(buffer->buffer_state(),
buffer->client_state_mask())) {
ALOGE(
"ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
@@ -638,7 +638,7 @@
static_cast<int>(*slot));
return ErrorStatus(EIO);
}
- if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) {
+ if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
*slot = *iter;
unavailable_buffers_slot_.erase(iter);
unavailable_buffers_slot_.push_back(*slot);
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 0eb7fec..7075e88 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -205,9 +205,9 @@
ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
void* raw_buf = nullptr;
- ASSERT_GE(buffer.get()->Lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
- /*x=*/0, /*y=*/0, buffer.get()->width(),
- buffer.get()->height(), &raw_buf),
+ ASSERT_GE(buffer.get()->buffer()->Lock(
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, /*x=*/0, /*y=*/0,
+ buffer.get()->width(), buffer.get()->height(), &raw_buf),
0);
ASSERT_NE(raw_buf, nullptr);
uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
@@ -216,7 +216,7 @@
pixels[i] = 0x0000ff00;
}
- ASSERT_GE(buffer.get()->Unlock(), 0);
+ ASSERT_GE(buffer.get()->buffer()->Unlock(), 0);
ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
diff --git a/opengl/libagl/Android.bp b/opengl/libagl/Android.bp
new file mode 100644
index 0000000..6ec24b3
--- /dev/null
+++ b/opengl/libagl/Android.bp
@@ -0,0 +1,99 @@
+//
+// Build the software OpenGL ES library
+//
+
+cc_defaults {
+ name: "libGLES_android_defaults",
+
+ cflags: [
+ "-DLOG_TAG=\"libagl\"",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libhardware",
+ "libutils",
+ "liblog",
+ "libpixelflinger",
+ "libETC1",
+ "libui",
+ "libnativewindow",
+ ],
+
+ // we need to access the private Bionic header <bionic_tls.h>
+ include_dirs: ["bionic/libc/private"],
+
+ arch: {
+ arm: {
+ cflags: ["-fstrict-aliasing"],
+ },
+
+ mips: {
+ cflags: [
+ "-fstrict-aliasing",
+ // The graphics code can generate division by zero
+ "-mno-check-zero-division",
+ ],
+ },
+ },
+}
+
+cc_library_shared {
+ name: "libGLES_android",
+ defaults: ["libGLES_android_defaults"],
+
+ whole_static_libs: ["libGLES_android_arm"],
+
+ srcs: [
+ "egl.cpp",
+ "state.cpp",
+ "texture.cpp",
+ "Tokenizer.cpp",
+ "TokenManager.cpp",
+ "TextureObjectManager.cpp",
+ "BufferObjectManager.cpp",
+ ],
+
+ arch: {
+ arm: {
+ srcs: [
+ "fixed_asm.S",
+ "iterators.S",
+ ],
+ },
+
+ mips: {
+ rev6: {
+ srcs: ["arch-mips/fixed_asm.S"],
+ },
+ },
+ },
+
+ relative_install_path: "egl",
+}
+
+cc_library_static {
+ name: "libGLES_android_arm",
+ defaults: ["libGLES_android_defaults"],
+
+ srcs: [
+ "array.cpp",
+ "fp.cpp",
+ "light.cpp",
+ "matrix.cpp",
+ "mipmap.cpp",
+ "primitives.cpp",
+ "vertex.cpp",
+ ],
+
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
deleted file mode 100644
index 15a12e4..0000000
--- a/opengl/libagl/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#
-# Build the software OpenGL ES library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- egl.cpp \
- state.cpp \
- texture.cpp \
- Tokenizer.cpp \
- TokenManager.cpp \
- TextureObjectManager.cpp \
- BufferObjectManager.cpp \
- array.cpp.arm \
- fp.cpp.arm \
- light.cpp.arm \
- matrix.cpp.arm \
- mipmap.cpp.arm \
- primitives.cpp.arm \
- vertex.cpp.arm
-
-LOCAL_CFLAGS += -DLOG_TAG=\"libagl\"
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-LOCAL_CFLAGS += -fvisibility=hidden
-
-LOCAL_SHARED_LIBRARIES := libcutils libhardware libutils liblog libpixelflinger libETC1 libui libnativewindow
-
-LOCAL_SRC_FILES_arm += fixed_asm.S iterators.S
-LOCAL_CFLAGS_arm += -fstrict-aliasing
-
-ifndef ARCH_MIPS_REV6
-LOCAL_SRC_FILES_mips += arch-mips/fixed_asm.S
-endif
-LOCAL_CFLAGS_mips += -fstrict-aliasing
-# The graphics code can generate division by zero
-LOCAL_CFLAGS_mips += -mno-check-zero-division
-
-LOCAL_CFLAGS += -Wall -Werror
-
-# we need to access the private Bionic header <bionic_tls.h>
-LOCAL_C_INCLUDES += bionic/libc/private
-
-LOCAL_MODULE_RELATIVE_PATH := egl
-LOCAL_MODULE:= libGLES_android
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
deleted file mode 100644
index 2f42ab6..0000000
--- a/opengl/libs/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 8a409ae..4cafe2b 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -255,6 +255,8 @@
LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
"couldn't load system OpenGL ES wrapper libraries");
+ android::GraphicsEnv::getInstance().sendGpuStats();
+
return (void*)hnd;
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 79166a7..872631f 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -954,6 +954,22 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
+ // b/111083885 - If we are presenting EGL 1.4 interface to apps
+ // error out on robust access attributes that are invalid
+ // in EGL 1.4 as the driver may be fine with them but dEQP expects
+ // tests to fail according to spec.
+ if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
+ const EGLint* attrib_ptr = attrib_list;
+ while (*attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint value = *attrib_ptr++;
+ if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
+ // We are GL ES context with EGL 1.4, this is an invalid
+ // attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
+ };
+ }
EGLContext context = cnx->egl.eglCreateContext(
dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
diff --git a/opengl/tests/Android.mk b/opengl/tests/Android.mk
deleted file mode 100644
index 134854a..0000000
--- a/opengl/tests/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-dirs := \
- linetex \
- swapinterval \
- textures \
- tritex \
-
-ifneq (,$(TARGET_BUILD_JAVA_SUPPORT_LEVEL))
-dirs += \
- gl2_cameraeye \
- gl2_java \
- gl2_jni \
- gldual \
- gl_jni \
- gl_perfapp \
- lighting1709 \
- testLatency \
- testPauseResume \
- testViewport \
-
-endif # JAVA_SUPPORT
-
-ifeq (platform,$(TARGET_BUILD_JAVA_SUPPORT_LEVEL))
-dirs += \
- testFramerate
-
-endif # JAVA_SUPPORT platform
-
-include $(call all-named-subdir-makefiles, $(dirs))
diff --git a/opengl/tests/gl2_cameraeye/Android.bp b/opengl/tests/gl2_cameraeye/Android.bp
new file mode 100644
index 0000000..00e00df
--- /dev/null
+++ b/opengl/tests/gl2_cameraeye/Android.bp
@@ -0,0 +1,6 @@
+android_app {
+ name: "GL2CameraEye",
+ // Only compile source java files in this apk.
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_cameraeye/Android.mk b/opengl/tests/gl2_cameraeye/Android.mk
deleted file mode 100644
index 4a43a9e..0000000
--- a/opengl/tests/gl2_cameraeye/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := GL2CameraEye
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/opengl/tests/gl2_java/Android.bp b/opengl/tests/gl2_java/Android.bp
new file mode 100644
index 0000000..a8e5d7d
--- /dev/null
+++ b/opengl/tests/gl2_java/Android.bp
@@ -0,0 +1,8 @@
+//########################################################################
+// OpenGL ES 2.0 Java sample
+//########################################################################
+android_app {
+ name: "GL2Java",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_java/Android.mk b/opengl/tests/gl2_java/Android.mk
deleted file mode 100644
index 71aa5a0..0000000
--- a/opengl/tests/gl2_java/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#########################################################################
-# OpenGL ES 2.0 Java sample
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GL2Java
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp
new file mode 100644
index 0000000..65f89b1
--- /dev/null
+++ b/opengl/tests/gl2_jni/Android.bp
@@ -0,0 +1,27 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+
+android_app {
+ name: "GL2JNI",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+ jni_libs: ["libgl2jni"],
+}
+
+// Build JNI Shared Library
+cc_library_shared {
+ name: "libgl2jni",
+ cflags: [
+ "-Werror",
+ "-Wno-error=unused-parameter",
+ ],
+ srcs: ["jni/gl_code.cpp"],
+ shared_libs: [
+ "liblog",
+ "libEGL",
+ "libGLESv2",
+ ],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/gl2_jni/Android.mk b/opengl/tests/gl2_jni/Android.mk
deleted file mode 100644
index b0081c2..0000000
--- a/opengl/tests/gl2_jni/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GL2JNI
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgl2jni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
- gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libEGL \
- libGLESv2
-
-LOCAL_MODULE := libgl2jni
-
-LOCAL_SDK_VERSION := current
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp
new file mode 100644
index 0000000..5bec336
--- /dev/null
+++ b/opengl/tests/gl_jni/Android.bp
@@ -0,0 +1,34 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+// Build activity
+
+android_app {
+ name: "GLJNI",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+ jni_libs: ["libgljni"],
+}
+
+// Build JNI Shared Library
+
+cc_library_shared {
+ name: "libgljni",
+ cflags: [
+ "-Werror",
+ "-Wno-error=unused-parameter",
+ ],
+ srcs: ["jni/gl_code.cpp"],
+ shared_libs: [
+ "liblog",
+ "libEGL",
+ "libGLESv1_CM",
+ ],
+ sdk_version: "current",
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
diff --git a/opengl/tests/gl_jni/Android.mk b/opengl/tests/gl_jni/Android.mk
deleted file mode 100644
index d64dfcf..0000000
--- a/opengl/tests/gl_jni/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLJNI
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgljni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
- gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libEGL \
- libGLESv1_CM
-
-LOCAL_MODULE := libgljni
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_ARM_MODE := arm
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp
new file mode 100644
index 0000000..cf899ac
--- /dev/null
+++ b/opengl/tests/gl_perfapp/Android.bp
@@ -0,0 +1,27 @@
+//########################################################################
+// OpenGL ES Perf App
+// This makefile builds both an activity and a shared library.
+//########################################################################
+android_app {
+ name: "GLPerf",
+ srcs: ["**/*.java"],
+ jni_libs: ["libglperf"],
+ // Run on Eclair
+ sdk_version: "7",
+}
+
+// Build JNI Shared Library
+cc_library_shared {
+ name: "libglperf",
+ cflags: [
+ "-Werror",
+ "-Wno-error=unused-parameter",
+ ],
+ srcs: ["jni/gl_code.cpp"],
+ shared_libs: [
+ "liblog",
+ "libEGL",
+ "libGLESv2",
+ ],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/gl_perfapp/Android.mk b/opengl/tests/gl_perfapp/Android.mk
deleted file mode 100644
index 3f411ea..0000000
--- a/opengl/tests/gl_perfapp/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-#########################################################################
-# OpenGL ES Perf App
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLPerf
-
-LOCAL_JNI_SHARED_LIBRARIES := libglperf
-
-# Run on Eclair
-LOCAL_SDK_VERSION := 7
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
- gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libEGL \
- libGLESv2
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MODULE := libglperf
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp
new file mode 100644
index 0000000..2432566
--- /dev/null
+++ b/opengl/tests/gldual/Android.bp
@@ -0,0 +1,30 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+// Build activity
+
+android_app {
+ name: "GLDual",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+ jni_libs: ["libgldualjni"],
+}
+
+//########################################################################
+// Build JNI Shared Library
+//########################################################################
+cc_library_shared {
+ name: "libgldualjni",
+ cflags: [
+ "-Werror",
+ "-Wno-error=unused-parameter",
+ ],
+ srcs: ["jni/gl_code.cpp"],
+ shared_libs: [
+ "liblog",
+ "libEGL",
+ "libGLESv2",
+ ],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk
deleted file mode 100644
index 5bdc0a8..0000000
--- a/opengl/tests/gldual/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := GLDual
-LOCAL_SDK_VERSION := current
-
-LOCAL_JNI_SHARED_LIBRARIES := libgldualjni
-
-include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-# Optional tag would mean it doesn't get installed by default
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -Werror -Wno-error=unused-parameter
-
-LOCAL_SRC_FILES:= \
- gl_code.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libEGL \
- libGLESv2
-
-LOCAL_MODULE := libgldualjni
-
-LOCAL_SDK_VERSION := current
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index b06422a..a0bd4e2 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -34,8 +34,12 @@
}
// Get main display parameters.
- sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain);
+ const auto mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+ if (mainDpy == nullptr) {
+ fprintf(stderr, "ERROR: no display\n");
+ return;
+ }
+
DisplayInfo mainDpyInfo;
err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
if (err != NO_ERROR) {
diff --git a/opengl/tests/lighting1709/Android.bp b/opengl/tests/lighting1709/Android.bp
new file mode 100644
index 0000000..e734dd1
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.bp
@@ -0,0 +1,6 @@
+android_test {
+ name: "LightingTest",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+ certificate: "platform",
+}
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
deleted file mode 100644
index 0047231..0000000
--- a/opengl/tests/lighting1709/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := LightingTest
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/linetex/Android.bp b/opengl/tests/linetex/Android.bp
new file mode 100644
index 0000000..dbc2cdb
--- /dev/null
+++ b/opengl/tests/linetex/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ name: "test-opengl-linetex",
+ srcs: ["linetex.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libui",
+ "libgui",
+ "libutils",
+ ],
+ static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/linetex/Android.mk b/opengl/tests/linetex/Android.mk
deleted file mode 100644
index 3df0a0f..0000000
--- a/opengl/tests/linetex/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- linetex.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libEGL \
- libGLESv1_CM \
- libui \
- libgui \
- libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-linetex
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/swapinterval/Android.bp b/opengl/tests/swapinterval/Android.bp
new file mode 100644
index 0000000..eed4dff
--- /dev/null
+++ b/opengl/tests/swapinterval/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ name: "test-opengl-swapinterval",
+ srcs: ["swapinterval.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libui",
+ "libgui",
+ ],
+ static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/swapinterval/Android.mk b/opengl/tests/swapinterval/Android.mk
deleted file mode 100644
index 2a2c12f..0000000
--- a/opengl/tests/swapinterval/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- swapinterval.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libEGL \
- libGLESv1_CM \
- libui \
- libgui
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-swapinterval
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/testFramerate/Android.bp b/opengl/tests/testFramerate/Android.bp
new file mode 100644
index 0000000..5aa83b0
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.bp
@@ -0,0 +1,9 @@
+//########################################################################
+// Test framerate and look for hiccups
+//########################################################################
+
+android_app {
+ name: "TestFramerate",
+ srcs: ["**/*.java"],
+ sdk_version: "system_current",
+}
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
deleted file mode 100644
index ca6654a..0000000
--- a/opengl/tests/testFramerate/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#########################################################################
-# Test framerate and look for hiccups
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestFramerate
-LOCAL_SDK_VERSION := system_current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testLatency/Android.bp b/opengl/tests/testLatency/Android.bp
new file mode 100644
index 0000000..c516dc3
--- /dev/null
+++ b/opengl/tests/testLatency/Android.bp
@@ -0,0 +1,8 @@
+//########################################################################
+// Test end-to-end latency.
+//########################################################################
+android_app {
+ name: "TestLatency",
+ sdk_version: "8",
+ srcs: ["**/*.java"],
+}
diff --git a/opengl/tests/testLatency/Android.mk b/opengl/tests/testLatency/Android.mk
deleted file mode 100644
index 96417c7..0000000
--- a/opengl/tests/testLatency/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#########################################################################
-# Test end-to-end latency.
-#########################################################################
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SDK_VERSION := 8
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestLatency
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testPauseResume/Android.bp b/opengl/tests/testPauseResume/Android.bp
new file mode 100644
index 0000000..810e895
--- /dev/null
+++ b/opengl/tests/testPauseResume/Android.bp
@@ -0,0 +1,6 @@
+// OpenGL ES JNI sample
+android_app {
+ name: "TestEGL",
+ srcs: ["**/*.java"],
+ sdk_version: "current",
+}
diff --git a/opengl/tests/testPauseResume/Android.mk b/opengl/tests/testPauseResume/Android.mk
deleted file mode 100644
index dda5424..0000000
--- a/opengl/tests/testPauseResume/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestEGL
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testViewport/Android.bp b/opengl/tests/testViewport/Android.bp
new file mode 100644
index 0000000..629b573
--- /dev/null
+++ b/opengl/tests/testViewport/Android.bp
@@ -0,0 +1,9 @@
+//########################################################################
+// OpenGL ES JNI sample
+// This makefile builds both an activity and a shared library.
+//########################################################################
+android_app {
+ name: "TestViewport",
+ srcs: ["**/*.java"],
+ sdk_version: "8",
+}
diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
deleted file mode 100644
index 9980e7d..0000000
--- a/opengl/tests/testViewport/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#########################################################################
-# OpenGL ES JNI sample
-# This makefile builds both an activity and a shared library.
-#########################################################################
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build activity
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := TestViewport
-
-# Set a specific SDK version so we can run on Froyo.
-
-LOCAL_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
diff --git a/opengl/tests/textures/Android.bp b/opengl/tests/textures/Android.bp
new file mode 100644
index 0000000..84adda2
--- /dev/null
+++ b/opengl/tests/textures/Android.bp
@@ -0,0 +1,18 @@
+cc_binary {
+ name: "test-opengl-textures",
+ srcs: ["textures.cpp"],
+ shared_libs: [
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libui",
+ "libgui",
+ "libutils",
+ ],
+ static_libs: ["libglTest"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
deleted file mode 100644
index 629a2d2..0000000
--- a/opengl/tests/textures/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- textures.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libEGL \
- libGLESv1_CM \
- libui \
- libgui \
- libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-textures
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp
new file mode 100644
index 0000000..390397b
--- /dev/null
+++ b/opengl/tests/tritex/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ name: "test-opengl-tritex",
+ srcs: ["tritex.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libui",
+ "libgui",
+ "libutils",
+ ],
+ static_libs: ["libglTest"],
+}
diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk
deleted file mode 100644
index 7055afa..0000000
--- a/opengl/tests/tritex/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- tritex.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libEGL \
- libGLESv1_CM \
- libui \
- libgui \
- libutils
-
-LOCAL_STATIC_LIBRARIES += libglTest
-
-LOCAL_C_INCLUDES += $(call include-path-for, opengl-tests-includes)
-
-LOCAL_MODULE:= test-opengl-tritex
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
index e312011..ec7e535 100644
--- a/services/bufferhub/BufferClient.cpp
+++ b/services/bufferhub/BufferClient.cpp
@@ -41,6 +41,14 @@
}
BufferClient::~BufferClient() {
+ {
+ std::lock_guard<std::mutex> lock(mClosedMutex);
+ if (!mClosed) {
+ ALOGW("%s: client of buffer #%d destroyed without close. Closing it now.", __FUNCTION__,
+ mBufferNode->id());
+ }
+ }
+
close();
}
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index a40443d..ade08e7 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -53,7 +53,7 @@
std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
desc.usage, userMetadataSize,
BufferHubIdGenerator::getInstance().getId());
- if (node == nullptr || !node->IsValid()) {
+ if (node == nullptr || !node->isValid()) {
ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
_hidl_cb(/*status=*/BufferHubStatus::ALLOCATION_FAILED, /*bufferClient=*/nullptr,
/*bufferTraits=*/{});
@@ -65,15 +65,26 @@
std::lock_guard<std::mutex> lock(mClientSetMutex);
mClientSet.emplace(client);
+ // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+ // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+ NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
hidl_handle bufferInfo =
- buildBufferInfo(node->id(), node->AddNewActiveClientsBitToMask(),
- node->user_metadata_size(), node->metadata().ashmem_fd());
- BufferTraits bufferTraits = {/*bufferDesc=*/description,
- /*bufferHandle=*/hidl_handle(node->buffer_handle()),
- /*bufferInfo=*/bufferInfo};
+ buildBufferInfo(bufferInfoStorage, node->id(), node->addNewActiveClientsBitToMask(),
+ node->userMetadataSize(), node->metadata().ashmemFd(),
+ node->eventFd().get());
+ // During the gralloc allocation carried out by BufferNode, gralloc allocator will populate the
+ // fields of its HardwareBufferDescription (i.e. strides) according to the actual
+ // gralloc implementation. We need to read those fields back and send them to the client via
+ // BufferTraits.
+ HardwareBufferDescription allocatedBufferDesc;
+ memcpy(&allocatedBufferDesc, &node->bufferDesc(), sizeof(AHardwareBuffer_Desc));
+ BufferTraits bufferTraits = {/*bufferDesc=*/allocatedBufferDesc,
+ /*bufferHandle=*/hidl_handle(node->bufferHandle()),
+ /*bufferInfo=*/std::move(bufferInfo)};
_hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
- /*bufferTraits=*/bufferTraits);
+ /*bufferTraits=*/std::move(bufferTraits));
return Void();
}
@@ -136,7 +147,7 @@
}
sp<BufferClient> client = new BufferClient(*originClient);
- uint32_t clientStateMask = client->getBufferNode()->AddNewActiveClientsBitToMask();
+ uint32_t clientStateMask = client->getBufferNode()->addNewActiveClientsBitToMask();
if (clientStateMask == 0U) {
// Reach max client count
ALOGE("%s: import failed, BufferNode#%u reached maximum clients.", __FUNCTION__,
@@ -152,17 +163,21 @@
std::shared_ptr<BufferNode> node = client->getBufferNode();
HardwareBufferDescription bufferDesc;
- memcpy(&bufferDesc, &node->buffer_desc(), sizeof(HardwareBufferDescription));
+ memcpy(&bufferDesc, &node->bufferDesc(), sizeof(HardwareBufferDescription));
- hidl_handle bufferInfo =
- buildBufferInfo(node->id(), clientStateMask, node->user_metadata_size(),
- node->metadata().ashmem_fd());
+ // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+ // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+ NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
+ hidl_handle bufferInfo = buildBufferInfo(bufferInfoStorage, node->id(), clientStateMask,
+ node->userMetadataSize(), node->metadata().ashmemFd(),
+ node->eventFd().get());
BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
- /*bufferHandle=*/hidl_handle(node->buffer_handle()),
- /*bufferInfo=*/bufferInfo};
+ /*bufferHandle=*/hidl_handle(node->bufferHandle()),
+ /*bufferInfo=*/std::move(bufferInfo)};
_hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
- /*bufferTraits=*/bufferTraits);
+ /*bufferTraits=*/std::move(bufferTraits));
return Void();
}
@@ -206,7 +221,7 @@
stream << std::right;
stream << std::setw(6) << "Id";
stream << " ";
- stream << std::setw(9) << "Clients";
+ stream << std::setw(9) << "#Clients";
stream << " ";
stream << std::setw(14) << "Geometry";
stream << " ";
@@ -222,17 +237,17 @@
for (auto iter = clientCount.begin(); iter != clientCount.end(); ++iter) {
const std::shared_ptr<BufferNode> node = std::move(iter->second.first);
const uint32_t clientCount = iter->second.second;
- AHardwareBuffer_Desc desc = node->buffer_desc();
+ AHardwareBuffer_Desc desc = node->bufferDesc();
MetadataHeader* metadataHeader =
- const_cast<BufferHubMetadata*>(&node->metadata())->metadata_header();
+ const_cast<BufferHubMetadata*>(&node->metadata())->metadataHeader();
const uint32_t state = metadataHeader->buffer_state.load(std::memory_order_acquire);
const uint64_t index = metadataHeader->queue_index;
stream << std::right;
stream << std::setw(6) << /*Id=*/node->id();
stream << " ";
- stream << std::setw(9) << /*Clients=*/clientCount;
+ stream << std::setw(9) << /*#Clients=*/clientCount;
stream << " ";
if (desc.format == HAL_PIXEL_FORMAT_BLOB) {
std::string size = std::to_string(desc.width) + " B";
@@ -282,14 +297,14 @@
stream << std::right;
stream << std::setw(8) << "Buffer Id";
stream << " ";
- stream << std::setw(6) << "Tokens";
+ stream << std::setw(7) << "#Tokens";
stream << std::endl;
for (auto iter = tokenCount.begin(); iter != tokenCount.end(); ++iter) {
stream << std::right;
stream << std::setw(8) << /*Buffer Id=*/iter->first;
stream << " ";
- stream << std::setw(6) << /*Tokens=*/iter->second;
+ stream << std::setw(7) << /*#Tokens=*/iter->second;
stream << std::endl;
}
@@ -344,20 +359,23 @@
// Implementation of this function should be consistent with the definition of bufferInfo handle in
// ui/BufferHubDefs.h.
-hidl_handle BufferHubService::buildBufferInfo(int bufferId, uint32_t clientBitMask,
- uint32_t userMetadataSize, const int metadataFd) {
- native_handle_t* infoHandle = native_handle_create(BufferHubDefs::kBufferInfoNumFds,
- BufferHubDefs::kBufferInfoNumInts);
+hidl_handle BufferHubService::buildBufferInfo(char* bufferInfoStorage, int bufferId,
+ uint32_t clientBitMask, uint32_t userMetadataSize,
+ int metadataFd, int eventFd) {
+ native_handle_t* infoHandle =
+ native_handle_init(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
- infoHandle->data[0] = dup(metadataFd);
- infoHandle->data[1] = bufferId;
+ infoHandle->data[0] = metadataFd;
+ infoHandle->data[1] = eventFd;
+ infoHandle->data[2] = bufferId;
// Use memcpy to convert to int without missing digit.
// TOOD(b/121345852): use bit_cast to unpack bufferInfo when C++20 becomes available.
- memcpy(&infoHandle->data[2], &clientBitMask, sizeof(clientBitMask));
- memcpy(&infoHandle->data[3], &userMetadataSize, sizeof(userMetadataSize));
+ memcpy(&infoHandle->data[3], &clientBitMask, sizeof(clientBitMask));
+ memcpy(&infoHandle->data[4], &userMetadataSize, sizeof(userMetadataSize));
hidl_handle bufferInfo;
- bufferInfo.setTo(infoHandle, /*shouldOwn=*/true);
+ bufferInfo.setTo(infoHandle, /*shouldOwn=*/false);
return bufferInfo;
}
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index 5106390..1efb27e 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -11,62 +11,61 @@
namespace V1_0 {
namespace implementation {
-void BufferNode::InitializeMetadata() {
+void BufferNode::initializeMetadata() {
// Using placement new here to reuse shared memory instead of new allocation
// Initialize the atomic variables to zero.
- BufferHubDefs::MetadataHeader* metadata_header = metadata_.metadata_header();
- buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint32_t>(0);
- fence_state_ = new (&metadata_header->fence_state) std::atomic<uint32_t>(0);
- active_clients_bit_mask_ =
- new (&metadata_header->active_clients_bit_mask) std::atomic<uint32_t>(0);
+ BufferHubDefs::MetadataHeader* metadataHeader = mMetadata.metadataHeader();
+ mBufferState = new (&metadataHeader->buffer_state) std::atomic<uint32_t>(0);
+ mFenceState = new (&metadataHeader->fence_state) std::atomic<uint32_t>(0);
+ mActiveClientsBitMask = new (&metadataHeader->active_clients_bit_mask) std::atomic<uint32_t>(0);
// The C++ standard recommends (but does not require) that lock-free atomic operations are
// also address-free, that is, suitable for communication between processes using shared
// memory.
- LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
- !std::atomic_is_lock_free(fence_state_) ||
- !std::atomic_is_lock_free(active_clients_bit_mask_),
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
+ !std::atomic_is_lock_free(mFenceState) ||
+ !std::atomic_is_lock_free(mActiveClientsBitMask),
"Atomic variables in ashmen are not lock free.");
}
// Allocates a new BufferNode.
-BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t user_metadata_size, int id)
+BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id)
: mId(id) {
- uint32_t out_stride = 0;
+ uint32_t outStride = 0;
// graphicBufferId is not used in GraphicBufferAllocator::allocate
// TODO(b/112338294) After move to the service folder, stop using the
// hardcoded service name "bufferhub".
- int ret = GraphicBufferAllocator::get().allocate(width, height, format, layer_count, usage,
+ int ret = GraphicBufferAllocator::get().allocate(width, height, format, layerCount, usage,
const_cast<const native_handle_t**>(
- &buffer_handle_),
- &out_stride,
+ &mBufferHandle),
+ &outStride,
/*graphicBufferId=*/0,
/*requestor=*/"bufferhub");
- if (ret != OK || buffer_handle_ == nullptr) {
+ if (ret != OK || mBufferHandle == nullptr) {
ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
return;
}
- buffer_desc_.width = width;
- buffer_desc_.height = height;
- buffer_desc_.layers = layer_count;
- buffer_desc_.format = format;
- buffer_desc_.usage = usage;
- buffer_desc_.stride = out_stride;
+ mBufferDesc.width = width;
+ mBufferDesc.height = height;
+ mBufferDesc.layers = layerCount;
+ mBufferDesc.format = format;
+ mBufferDesc.usage = usage;
+ mBufferDesc.stride = outStride;
- metadata_ = BufferHubMetadata::Create(user_metadata_size);
- if (!metadata_.IsValid()) {
+ mMetadata = BufferHubMetadata::create(userMetadataSize);
+ if (!mMetadata.isValid()) {
ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
return;
}
- InitializeMetadata();
+ initializeMetadata();
}
BufferNode::~BufferNode() {
// Free the handle
- if (buffer_handle_ != nullptr) {
- status_t ret = GraphicBufferAllocator::get().free(buffer_handle_);
+ if (mBufferHandle != nullptr) {
+ status_t ret = GraphicBufferAllocator::get().free(mBufferHandle);
if (ret != OK) {
ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
}
@@ -78,33 +77,33 @@
}
}
-uint32_t BufferNode::GetActiveClientsBitMask() const {
- return active_clients_bit_mask_->load(std::memory_order_acquire);
+uint32_t BufferNode::getActiveClientsBitMask() const {
+ return mActiveClientsBitMask->load(std::memory_order_acquire);
}
-uint32_t BufferNode::AddNewActiveClientsBitToMask() {
- uint32_t current_active_clients_bit_mask = GetActiveClientsBitMask();
- uint32_t client_state_mask = 0U;
- uint32_t updated_active_clients_bit_mask = 0U;
+uint32_t BufferNode::addNewActiveClientsBitToMask() {
+ uint32_t currentActiveClientsBitMask = getActiveClientsBitMask();
+ uint32_t clientStateMask = 0U;
+ uint32_t updatedActiveClientsBitMask = 0U;
do {
- client_state_mask =
- BufferHubDefs::FindNextAvailableClientStateMask(current_active_clients_bit_mask);
- if (client_state_mask == 0U) {
+ clientStateMask =
+ BufferHubDefs::findNextAvailableClientStateMask(currentActiveClientsBitMask);
+ if (clientStateMask == 0U) {
ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
BufferHubDefs::kMaxNumberOfClients);
errno = E2BIG;
return 0U;
}
- updated_active_clients_bit_mask = current_active_clients_bit_mask | client_state_mask;
- } while (!(active_clients_bit_mask_->compare_exchange_weak(current_active_clients_bit_mask,
- updated_active_clients_bit_mask,
- std::memory_order_acq_rel,
- std::memory_order_acquire)));
- return client_state_mask;
+ updatedActiveClientsBitMask = currentActiveClientsBitMask | clientStateMask;
+ } while (!(mActiveClientsBitMask->compare_exchange_weak(currentActiveClientsBitMask,
+ updatedActiveClientsBitMask,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire)));
+ return clientStateMask;
}
-void BufferNode::RemoveClientsBitFromMask(const uint32_t& value) {
- active_clients_bit_mask_->fetch_and(~value);
+void BufferNode::removeClientsBitFromMask(const uint32_t& value) {
+ mActiveClientsBitMask->fetch_and(~value);
}
} // namespace implementation
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 66ed4bd..644b403 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -72,4 +72,4 @@
} // namespace frameworks
} // namespace android
-#endif
\ No newline at end of file
+#endif
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 23e664e..edad20b 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -58,8 +58,8 @@
private:
// Helper function to build BufferTraits.bufferInfo handle
- hidl_handle buildBufferInfo(int bufferId, uint32_t clientBitMask, uint32_t userMetadataSize,
- const int metadataFd);
+ hidl_handle buildBufferInfo(char* bufferInfoStorage, int bufferId, uint32_t clientBitMask,
+ uint32_t userMetadataSize, int metadataFd, int eventFd);
// Helper function to remove all the token belongs to a specific client.
void removeTokenByClient(const BufferClient* client);
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
index b7a195b..62a8d63 100644
--- a/services/bufferhub/include/bufferhub/BufferNode.h
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -4,6 +4,7 @@
#include <android/hardware_buffer.h>
#include <bufferhub/BufferHubIdGenerator.h>
#include <cutils/native_handle.h>
+#include <ui/BufferHubEventFd.h>
#include <ui/BufferHubMetadata.h>
namespace android {
@@ -15,73 +16,79 @@
class BufferNode {
public:
// Allocates a new BufferNode.
- BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t user_metadata_size, int id = -1);
+ BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id = -1);
~BufferNode();
// Returns whether the object holds a valid metadata.
- bool IsValid() const { return metadata_.IsValid(); }
+ bool isValid() const { return mMetadata.isValid(); }
int id() const { return mId; }
- size_t user_metadata_size() const { return metadata_.user_metadata_size(); }
+ size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
// Accessors of the buffer description and handle
- const native_handle_t* buffer_handle() const { return buffer_handle_; }
- const AHardwareBuffer_Desc& buffer_desc() const { return buffer_desc_; }
+ const native_handle_t* bufferHandle() const { return mBufferHandle; }
+ const AHardwareBuffer_Desc& bufferDesc() const { return mBufferDesc; }
- // Accessors of metadata.
- const BufferHubMetadata& metadata() const { return metadata_; }
+ // Accessor of event fd.
+ const BufferHubEventFd& eventFd() const { return mEventFd; }
- // Gets the current value of active_clients_bit_mask in metadata_ with
+ // Accessors of mMetadata.
+ const BufferHubMetadata& metadata() const { return mMetadata; }
+
+ // Gets the current value of mActiveClientsBitMask in mMetadata with
// std::memory_order_acquire, so that all previous releases of
- // active_clients_bit_mask from all threads will be returned here.
- uint32_t GetActiveClientsBitMask() const;
+ // mActiveClientsBitMask from all threads will be returned here.
+ uint32_t getActiveClientsBitMask() const;
- // Find and add a new client_state_mask to active_clients_bit_mask in
- // metadata_.
- // Return the new client_state_mask that is added to active_clients_bit_mask.
+ // Find and add a new client state mask to mActiveClientsBitMask in
+ // mMetadata.
+ // Return the new client state mask that is added to mActiveClientsBitMask.
// Return 0U if there are already 16 clients of the buffer.
- uint32_t AddNewActiveClientsBitToMask();
+ uint32_t addNewActiveClientsBitToMask();
- // Removes the value from active_clients_bit_mask in metadata_ with
+ // Removes the value from active_clients_bit_mask in mMetadata with
// std::memory_order_release, so that the change will be visible to any
- // acquire of active_clients_bit_mask_ in any threads after the succeed of
+ // acquire of mActiveClientsBitMask in any threads after the succeed of
// this operation.
- void RemoveClientsBitFromMask(const uint32_t& value);
+ void removeClientsBitFromMask(const uint32_t& value);
private:
// Helper method for constructors to initialize atomic metadata header
// variables in shared memory.
- void InitializeMetadata();
+ void initializeMetadata();
// Gralloc buffer handles.
- native_handle_t* buffer_handle_;
- AHardwareBuffer_Desc buffer_desc_;
+ native_handle_t* mBufferHandle;
+ AHardwareBuffer_Desc mBufferDesc;
+
+ // Eventfd used for signalling buffer events among the clients of the buffer.
+ BufferHubEventFd mEventFd;
// Metadata in shared memory.
- BufferHubMetadata metadata_;
+ BufferHubMetadata mMetadata;
// A system-unique id generated by bufferhub from 0 to std::numeric_limits<int>::max().
// BufferNodes not created by bufferhub will have id < 0, meaning "not specified".
// TODO(b/118891412): remove default id = -1 and update comments after pdx is no longer in use
const int mId = -1;
- // The following variables are atomic variables in metadata_ that are visible
+ // The following variables are atomic variables in mMetadata that are visible
// to Bn object and Bp objects. Please find more info in
// BufferHubDefs::MetadataHeader.
- // buffer_state_ tracks the state of the buffer. Buffer can be in one of these
+ // mBufferState tracks the state of the buffer. Buffer can be in one of these
// four states: gained, posted, acquired, released.
- std::atomic<uint32_t>* buffer_state_ = nullptr;
+ std::atomic<uint32_t>* mBufferState = nullptr;
- // TODO(b/112012161): add comments to fence_state_.
- std::atomic<uint32_t>* fence_state_ = nullptr;
+ // TODO(b/112012161): add comments to mFenceState.
+ std::atomic<uint32_t>* mFenceState = nullptr;
- // active_clients_bit_mask_ tracks all the bp clients of the buffer. It is the
+ // mActiveClientsBitMask tracks all the bp clients of the buffer. It is the
// union of all client_state_mask of all bp clients.
- std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+ std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
};
} // namespace implementation
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
index ccb1197..2dfd4fc 100644
--- a/services/bufferhub/tests/BufferNode_test.cpp
+++ b/services/bufferhub/tests/BufferNode_test.cpp
@@ -26,80 +26,78 @@
class BufferNodeTest : public ::testing::Test {
protected:
void SetUp() override {
- buffer_node =
+ mBufferNode =
new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
- ASSERT_TRUE(buffer_node->IsValid());
+ ASSERT_TRUE(mBufferNode->isValid());
}
void TearDown() override {
- if (buffer_node != nullptr) {
- delete buffer_node;
+ if (mBufferNode != nullptr) {
+ delete mBufferNode;
}
}
- BufferNode* buffer_node = nullptr;
+ BufferNode* mBufferNode = nullptr;
};
TEST_F(BufferNodeTest, TestCreateBufferNode) {
- EXPECT_EQ(buffer_node->user_metadata_size(), kUserMetadataSize);
+ EXPECT_EQ(mBufferNode->userMetadataSize(), kUserMetadataSize);
// Test the handle just allocated is good (i.e. able to be imported)
GraphicBufferMapper& mapper = GraphicBufferMapper::get();
const native_handle_t* outHandle;
status_t ret =
- mapper.importBuffer(buffer_node->buffer_handle(), buffer_node->buffer_desc().width,
- buffer_node->buffer_desc().height,
- buffer_node->buffer_desc().layers,
- buffer_node->buffer_desc().format, buffer_node->buffer_desc().usage,
- buffer_node->buffer_desc().stride, &outHandle);
+ mapper.importBuffer(mBufferNode->bufferHandle(), mBufferNode->bufferDesc().width,
+ mBufferNode->bufferDesc().height, mBufferNode->bufferDesc().layers,
+ mBufferNode->bufferDesc().format, mBufferNode->bufferDesc().usage,
+ mBufferNode->bufferDesc().stride, &outHandle);
EXPECT_EQ(ret, OK);
EXPECT_THAT(outHandle, NotNull());
}
-TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) {
- uint32_t new_client_state_mask_1 = buffer_node->AddNewActiveClientsBitToMask();
- EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_client_state_mask_1);
+TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_twoNewClients) {
+ uint32_t newClientStateMask1 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1);
// Request and add a new client_state_mask again.
// Active clients bit mask should be the union of the two new
// client_state_masks.
- uint32_t new_client_state_mask_2 = buffer_node->AddNewActiveClientsBitToMask();
- EXPECT_EQ(buffer_node->GetActiveClientsBitMask(),
- new_client_state_mask_1 | new_client_state_mask_2);
+ uint32_t newClientStateMask2 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1 | newClientStateMask2);
}
-TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) {
- uint32_t new_client_state_mask = 0U;
- uint32_t current_mask = 0U;
- uint32_t expected_mask = 0U;
+TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_32NewClients) {
+ uint32_t newClientStateMask = 0U;
+ uint32_t currentMask = 0U;
+ uint32_t expectedMask = 0U;
for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
- new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
- EXPECT_NE(new_client_state_mask, 0U);
- EXPECT_FALSE(new_client_state_mask & current_mask);
- expected_mask = current_mask | new_client_state_mask;
- current_mask = buffer_node->GetActiveClientsBitMask();
- EXPECT_EQ(current_mask, expected_mask);
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(newClientStateMask, 0U);
+ EXPECT_FALSE(newClientStateMask & currentMask);
+ expectedMask = currentMask | newClientStateMask;
+ currentMask = mBufferNode->getActiveClientsBitMask();
+ EXPECT_EQ(currentMask, expectedMask);
}
// Method should fail upon requesting for more than maximum allowable clients.
- new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
- EXPECT_EQ(new_client_state_mask, 0U);
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(newClientStateMask, 0U);
EXPECT_EQ(errno, E2BIG);
}
TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
- buffer_node->AddNewActiveClientsBitToMask();
- uint32_t current_mask = buffer_node->GetActiveClientsBitMask();
- uint32_t new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
- EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask);
+ mBufferNode->addNewActiveClientsBitToMask();
+ uint32_t currentMask = mBufferNode->getActiveClientsBitMask();
+ uint32_t newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(mBufferNode->getActiveClientsBitMask(), currentMask);
- buffer_node->RemoveClientsBitFromMask(new_client_state_mask);
- EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
// Remove the test_mask again to the active client bit mask should not modify
// the value of active clients bit mask.
- buffer_node->RemoveClientsBitFromMask(new_client_state_mask);
- EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
}
} // namespace
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 47bed65..e21d8e7 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -30,6 +30,8 @@
],
shared_libs: [
"libbinder",
+ "libcutils",
+ "libgraphicsenv",
"liblog",
"libutils",
"libvulkan",
@@ -59,6 +61,7 @@
],
shared_libs: [
"libbinder",
+ "libcutils",
"liblog",
"libutils",
],
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 150896c..68c185c 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -14,48 +14,19 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "GpuService.h"
#include <binder/IResultReceiver.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
+
#include <vkjson.h>
namespace android {
-class BpGpuService : public BpInterface<IGpuService> {
-public:
- explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
-};
-
-IMPLEMENT_META_INTERFACE(GpuService, "android.ui.IGpuService");
-
-status_t BnGpuService::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- status_t status;
- switch (code) {
- case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
- std::vector<String16> args;
- data.readString16Vector(&args);
- sp<IBinder> unusedCallback;
- sp<IResultReceiver> resultReceiver;
- if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK)
- return status;
- if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK)
- return status;
- status = shellCommand(in, out, err, args);
- if (resultReceiver != nullptr)
- resultReceiver->send(status);
- return OK;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
namespace {
status_t cmd_help(int out);
@@ -66,9 +37,25 @@
GpuService::GpuService() = default;
-status_t GpuService::shellCommand(int /*in*/, int out, int err,
- std::vector<String16>& args) {
- ALOGV("GpuService::shellCommand");
+void GpuService::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStateLock);
+ ALOGV("Received:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%llu]\n"
+ "\tappPackageName[%s]\n",
+ driverPackageName.c_str(), driverVersionName.c_str(),
+ (unsigned long long)driverVersionCode, appPackageName.c_str());
+}
+
+status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
+ ATRACE_CALL();
+
+ ALOGV("shellCommand");
for (size_t i = 0, n = args.size(); i < n; i++)
ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string());
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index e2b396e..5304fa1 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -17,31 +17,15 @@
#ifndef ANDROID_GPUSERVICE_H
#define ANDROID_GPUSERVICE_H
-#include <vector>
-
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <graphicsenv/IGpuService.h>
+
+#include <mutex>
+#include <vector>
namespace android {
-/*
- * This class defines the Binder IPC interface for GPU-related queries and
- * control.
- */
-class IGpuService : public IInterface {
-public:
- DECLARE_META_INTERFACE(GpuService);
-};
-
-class BnGpuService: public BnInterface<IGpuService> {
-protected:
- virtual status_t shellCommand(int in, int out, int err,
- std::vector<String16>& args) = 0;
-
- status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0) override;
-};
-
class GpuService : public BnGpuService {
public:
static const char* const SERVICE_NAME ANDROID_API;
@@ -49,8 +33,15 @@
GpuService() ANDROID_API;
protected:
- status_t shellCommand(int in, int out, int err,
- std::vector<String16>& args) override;
+ status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override;
+
+private:
+ // IGpuService interface
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ const uint64_t driverVersionCode, const std::string& appPackageName);
+
+ // GpuStats access must be protected by mStateLock
+ std::mutex mStateLock;
};
} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 843eb37..f73d498 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -16,21 +16,25 @@
name: "libinputflinger",
srcs: [
+ "InputClassifier.cpp",
"InputDispatcher.cpp",
"InputManager.cpp",
],
shared_libs: [
+ "android.hardware.input.classifier@1.0",
"libinputflinger_base",
"libinputreporter",
"libinputreader",
"libbase",
"libbinder",
"libcutils",
+ "libhidlbase",
"libinput",
"liblog",
"libutils",
"libui",
+ "server_configurable_flags",
],
cflags: [
@@ -38,7 +42,9 @@
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
- // TODO: Move inputflinger to its own process and mark it hidden
+ // TODO(b/123097103): annotate InputDispatcher and uncomment the following line
+ //"-Wthread-safety",
+ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
],
@@ -76,7 +82,8 @@
"libutils",
"libui",
"libhardware_legacy",
- "libutils"
+ "libstatslog",
+ "libutils",
],
header_libs: [
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
new file mode 100644
index 0000000..c9eb683
--- /dev/null
+++ b/services/inputflinger/BlockingQueue.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_BLOCKING_QUEUE_H
+#define _UI_INPUT_BLOCKING_QUEUE_H
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+/**
+ * A FIFO queue that stores up to <i>capacity</i> objects.
+ * Objects can always be added. Objects are added immediately.
+ * If the queue is full, new objects cannot be added.
+ *
+ * The action of retrieving an object will block until an element is available.
+ */
+template <class T>
+class BlockingQueue {
+public:
+ BlockingQueue(size_t capacity) : mCapacity(capacity) {
+ mQueue.reserve(mCapacity);
+ };
+
+ /**
+ * Retrieve and remove the oldest object.
+ * Blocks execution while queue is empty.
+ */
+ T pop() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); });
+ T t = std::move(mQueue.front());
+ mQueue.erase(mQueue.begin());
+ return std::move(t);
+ };
+
+ /**
+ * Add a new object to the queue.
+ * Does not block.
+ * Return true if an element was successfully added.
+ * Return false if the queue is full.
+ */
+ bool push(T&& t) {
+ std::unique_lock<std::mutex> lock(mLock);
+ if (mQueue.size() == mCapacity) {
+ return false;
+ }
+ mQueue.push_back(std::move(t));
+ mHasElements.notify_one();
+ return true;
+ };
+
+ void erase(const std::function<bool(const T&)>& lambda) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
+ [&lambda](const T& t) { return lambda(t); }), mQueue.end());
+ }
+
+ /**
+ * Remove all elements.
+ * Does not block.
+ */
+ void clear() {
+ std::scoped_lock lock(mLock);
+ mQueue.clear();
+ };
+
+ /**
+ * How many elements are currently stored in the queue.
+ * Primary used for debugging.
+ * Does not block.
+ */
+ size_t size() {
+ std::scoped_lock lock(mLock);
+ return mQueue.size();
+ }
+
+private:
+ size_t mCapacity;
+ /**
+ * Used to signal that mQueue is non-empty.
+ */
+ std::condition_variable mHasElements;
+ /**
+ * Lock for accessing and waiting on elements.
+ */
+ std::mutex mLock;
+ std::vector<T> mQueue; //GUARDED_BY(mLock)
+};
+
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index cf9d3c7..c13bac6 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -763,7 +763,7 @@
}
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
- if (deviceId == BUILT_IN_KEYBOARD_ID) {
+ if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
}
ssize_t index = mDevices.indexOfKey(deviceId);
@@ -835,7 +835,8 @@
device->id, device->path.c_str());
mClosingDevices = device->next;
event->when = now;
- event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
+ event->deviceId = (device->id == mBuiltInKeyboardId) ?
+ ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
@@ -1081,7 +1082,7 @@
ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
}
}
- if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
+ if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
}
}
@@ -1580,7 +1581,8 @@
identifier.uniqueId = "<virtual>";
assignDescriptorLocked(identifier);
- Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
+ Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
+ identifier);
device->classes = INPUT_DEVICE_CLASS_KEYBOARD
| INPUT_DEVICE_CLASS_ALPHAKEY
| INPUT_DEVICE_CLASS_DPAD
diff --git a/services/inputflinger/include/EventHub.h b/services/inputflinger/EventHub.h
similarity index 98%
rename from services/inputflinger/include/EventHub.h
rename to services/inputflinger/EventHub.h
index 295aca8..d176648 100644
--- a/services/inputflinger/include/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -46,13 +46,6 @@
namespace android {
-enum {
- // Device id of a special "virtual" keyboard that is always present.
- VIRTUAL_KEYBOARD_ID = -1,
- // Device id of the "built-in" keyboard if there is one.
- BUILT_IN_KEYBOARD_ID = 0,
-};
-
/*
* A raw event as retrieved from the EventHub.
*/
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
new file mode 100644
index 0000000..3905ed0
--- /dev/null
+++ b/services/inputflinger/InputClassifier.cpp
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputClassifier"
+
+#include "InputClassifier.h"
+
+#include <algorithm>
+#include <android-base/stringprintf.h>
+#include <cmath>
+#include <inttypes.h>
+#include <log/log.h>
+#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>
+
+#define INDENT1 " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
+#define INDENT5 " "
+
+using android::base::StringPrintf;
+using android::hardware::hidl_bitfield;
+using android::hardware::hidl_vec;
+using namespace android::hardware::input;
+
+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;
+
+template<class K, class V>
+static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
+ auto it = map.find(key);
+ if (it == map.end()) {
+ return defaultValue;
+ }
+ return it->second;
+}
+
+static common::V1_0::Source getSource(uint32_t source) {
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_UNKNOWN) ==
+ common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_KEYBOARD) ==
+ common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_DPAD) ==
+ common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_GAMEPAD) ==
+ common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
+ common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE) ==
+ common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_STYLUS) ==
+ common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
+ common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TRACKBALL) ==
+ common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
+ common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHPAD) ==
+ common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
+ common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_JOYSTICK) ==
+ common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
+ common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ANY) ==
+ common::V1_0::Source::ANY, "SOURCE_ANY mismatch");
+ return static_cast<common::V1_0::Source>(source);
+}
+
+static common::V1_0::Action getAction(int32_t actionMasked) {
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_DOWN) ==
+ common::V1_0::Action::DOWN, "ACTION_DOWN mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_UP) ==
+ common::V1_0::Action::UP, "ACTION_UP mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_MOVE) ==
+ common::V1_0::Action::MOVE, "ACTION_MOVE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
+ common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
+ common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
+ common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
+ common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch");
+ static_assert(static_cast<common::V1_0::Action>( AMOTION_EVENT_ACTION_HOVER_MOVE) ==
+ common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
+ common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
+ common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
+ common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
+ common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
+ common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch");
+ return static_cast<common::V1_0::Action>(actionMasked);
+}
+
+static common::V1_0::Button getActionButton(int32_t actionButton) {
+ static_assert(static_cast<common::V1_0::Button>(0) ==
+ common::V1_0::Button::NONE, "BUTTON_NONE mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
+ common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
+ common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
+ common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_BACK) ==
+ common::V1_0::Button::BACK, "BUTTON_BACK mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
+ common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
+ common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
+ common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch");
+ return static_cast<common::V1_0::Button>(actionButton);
+}
+
+static hidl_bitfield<common::V1_0::Flag> getFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
+ common::V1_0::Flag::WINDOW_IS_OBSCURED);
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
+ common::V1_0::Flag::IS_GENERATED_GESTURE);
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_TAINTED) ==
+ common::V1_0::Flag::TAINTED);
+ return static_cast<hidl_bitfield<common::V1_0::Flag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::PolicyFlag> getPolicyFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_WAKE) ==
+ common::V1_0::PolicyFlag::WAKE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
+ common::V1_0::PolicyFlag::VIRTUAL);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
+ common::V1_0::PolicyFlag::FUNCTION);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_GESTURE) ==
+ common::V1_0::PolicyFlag::GESTURE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INJECTED) ==
+ common::V1_0::PolicyFlag::INJECTED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
+ common::V1_0::PolicyFlag::TRUSTED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FILTERED) ==
+ common::V1_0::PolicyFlag::FILTERED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
+ common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
+ common::V1_0::PolicyFlag::INTERACTIVE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
+ common::V1_0::PolicyFlag::PASS_TO_USER);
+ return static_cast<hidl_bitfield<common::V1_0::PolicyFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::EdgeFlag> getEdgeFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
+ common::V1_0::EdgeFlag::NONE);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
+ common::V1_0::EdgeFlag::TOP);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
+ common::V1_0::EdgeFlag::BOTTOM);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
+ common::V1_0::EdgeFlag::LEFT);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
+ common::V1_0::EdgeFlag::RIGHT);
+ return static_cast<hidl_bitfield<common::V1_0::EdgeFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::Meta> getMetastate(int32_t state) {
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_NONE) ==
+ common::V1_0::Meta::NONE);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_ON) ==
+ common::V1_0::Meta::ALT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_LEFT_ON) ==
+ common::V1_0::Meta::ALT_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_RIGHT_ON) ==
+ common::V1_0::Meta::ALT_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_ON) ==
+ common::V1_0::Meta::SHIFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_LEFT_ON) ==
+ common::V1_0::Meta::SHIFT_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_RIGHT_ON) ==
+ common::V1_0::Meta::SHIFT_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SYM_ON) ==
+ common::V1_0::Meta::SYM_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_FUNCTION_ON) ==
+ common::V1_0::Meta::FUNCTION_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_ON) ==
+ common::V1_0::Meta::CTRL_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_LEFT_ON) ==
+ common::V1_0::Meta::CTRL_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_RIGHT_ON) ==
+ common::V1_0::Meta::CTRL_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_ON) ==
+ common::V1_0::Meta::META_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_LEFT_ON) ==
+ common::V1_0::Meta::META_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_RIGHT_ON) ==
+ common::V1_0::Meta::META_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CAPS_LOCK_ON) ==
+ common::V1_0::Meta::CAPS_LOCK_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_NUM_LOCK_ON) ==
+ common::V1_0::Meta::NUM_LOCK_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SCROLL_LOCK_ON) ==
+ common::V1_0::Meta::SCROLL_LOCK_ON);
+ return static_cast<hidl_bitfield<common::V1_0::Meta>>(state);
+}
+
+static hidl_bitfield<common::V1_0::Button> getButtonState(int32_t buttonState) {
+ // No need for static_assert here.
+ // The button values have already been asserted in getActionButton(..) above
+ return static_cast<hidl_bitfield<common::V1_0::Button>>(buttonState);
+}
+
+static common::V1_0::ToolType getToolType(int32_t toolType) {
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
+ common::V1_0::ToolType::UNKNOWN);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
+ common::V1_0::ToolType::FINGER);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
+ common::V1_0::ToolType::STYLUS);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
+ common::V1_0::ToolType::MOUSE);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
+ common::V1_0::ToolType::ERASER);
+ return static_cast<common::V1_0::ToolType>(toolType);
+}
+
+static common::V1_0::Axis getAxis(uint64_t axis) {
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_X) ==
+ common::V1_0::Axis::X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Y) ==
+ common::V1_0::Axis::Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_PRESSURE) ==
+ common::V1_0::Axis::PRESSURE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SIZE) ==
+ common::V1_0::Axis::SIZE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
+ common::V1_0::Axis::TOUCH_MAJOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
+ common::V1_0::Axis::TOUCH_MINOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) ==
+ common::V1_0::Axis::TOOL_MAJOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) ==
+ common::V1_0::Axis::TOOL_MINOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
+ common::V1_0::Axis::ORIENTATION);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_VSCROLL) ==
+ common::V1_0::Axis::VSCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HSCROLL) ==
+ common::V1_0::Axis::HSCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Z) ==
+ common::V1_0::Axis::Z);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RX) ==
+ common::V1_0::Axis::RX);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RY) ==
+ common::V1_0::Axis::RY);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RZ) ==
+ common::V1_0::Axis::RZ);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_X) ==
+ common::V1_0::Axis::HAT_X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_Y) ==
+ common::V1_0::Axis::HAT_Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) ==
+ common::V1_0::Axis::LTRIGGER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) ==
+ common::V1_0::Axis::RTRIGGER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_THROTTLE) ==
+ common::V1_0::Axis::THROTTLE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RUDDER) ==
+ common::V1_0::Axis::RUDDER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_WHEEL) ==
+ common::V1_0::Axis::WHEEL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GAS) ==
+ common::V1_0::Axis::GAS);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_BRAKE) ==
+ common::V1_0::Axis::BRAKE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_DISTANCE) ==
+ common::V1_0::Axis::DISTANCE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TILT) ==
+ common::V1_0::Axis::TILT);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SCROLL) ==
+ common::V1_0::Axis::SCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) ==
+ common::V1_0::Axis::RELATIVE_X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) ==
+ common::V1_0::Axis::RELATIVE_Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) ==
+ common::V1_0::Axis::GENERIC_1);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) ==
+ common::V1_0::Axis::GENERIC_2);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) ==
+ common::V1_0::Axis::GENERIC_3);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) ==
+ common::V1_0::Axis::GENERIC_4);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) ==
+ common::V1_0::Axis::GENERIC_5);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) ==
+ common::V1_0::Axis::GENERIC_6);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) ==
+ common::V1_0::Axis::GENERIC_7);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) ==
+ common::V1_0::Axis::GENERIC_8);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) ==
+ common::V1_0::Axis::GENERIC_9);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) ==
+ common::V1_0::Axis::GENERIC_10);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) ==
+ common::V1_0::Axis::GENERIC_11);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) ==
+ common::V1_0::Axis::GENERIC_12);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) ==
+ common::V1_0::Axis::GENERIC_13);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) ==
+ common::V1_0::Axis::GENERIC_14);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) ==
+ common::V1_0::Axis::GENERIC_15);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) ==
+ common::V1_0::Axis::GENERIC_16);
+ return static_cast<common::V1_0::Axis>(axis);
+}
+
+static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
+ common::V1_0::VideoFrame out;
+ out.width = frame.getWidth();
+ out.height = frame.getHeight();
+ out.data = frame.getData();
+ struct timeval timestamp = frame.getTimestamp();
+ out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
+ microseconds_to_nanoseconds(timestamp.tv_usec);
+ return out;
+}
+
+static std::vector<common::V1_0::VideoFrame> convertVideoFrames(
+ const std::vector<TouchVideoFrame>& frames) {
+ std::vector<common::V1_0::VideoFrame> out;
+ for (const TouchVideoFrame& frame : frames) {
+ out.push_back(getHalVideoFrame(frame));
+ }
+ return out;
+}
+
+static uint8_t getActionIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args,
+ std::vector<common::V1_0::PointerProperties>* outPointerProperties,
+ std::vector<common::V1_0::PointerCoords>* outPointerCoords) {
+ outPointerProperties->reserve(args.pointerCount);
+ outPointerCoords->reserve(args.pointerCount);
+ for (size_t i = 0; i < args.pointerCount; i++) {
+ common::V1_0::PointerProperties properties;
+ properties.id = args.pointerProperties[i].id;
+ properties.toolType = getToolType(args.pointerProperties[i].toolType);
+ outPointerProperties->push_back(properties);
+
+ common::V1_0::PointerCoords coords;
+ BitSet64 bits (args.pointerCoords[i].bits);
+ std::vector<float> values;
+ size_t index = 0;
+ while (!bits.isEmpty()) {
+ uint32_t axis = bits.clearFirstMarkedBit();
+ coords.bits |= 1 << static_cast<uint64_t>(getAxis(axis));
+ float value = args.pointerCoords[i].values[index++];
+ values.push_back(value);
+ }
+ coords.values = values;
+ outPointerCoords->push_back(coords);
+ }
+}
+
+static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) {
+ common::V1_0::MotionEvent event;
+ event.deviceId = args.deviceId;
+ event.source = getSource(args.source);
+ event.displayId = args.displayId;
+ event.downTime = args.downTime;
+ event.eventTime = args.eventTime;
+ event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
+ event.actionIndex = getActionIndex(args.action);
+ event.actionButton = getActionButton(args.actionButton);
+ event.flags = getFlags(args.flags);
+ event.policyFlags = getPolicyFlags(args.policyFlags);
+ event.edgeFlags = getEdgeFlags(args.edgeFlags);
+ event.metaState = getMetastate(args.metaState);
+ event.buttonState = getButtonState(args.buttonState);
+ event.xPrecision = args.xPrecision;
+ event.yPrecision = args.yPrecision;
+
+ std::vector<common::V1_0::PointerProperties> pointerProperties;
+ std::vector<common::V1_0::PointerCoords> pointerCoords;
+ getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords);
+ event.pointerProperties = pointerProperties;
+ event.pointerCoords = pointerCoords;
+
+ event.deviceTimestamp = args.deviceTimestamp;
+ event.frames = convertVideoFrames(args.videoFrames);
+
+ return event;
+}
+
+static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
+ static_assert(MotionClassification::NONE ==
+ static_cast<MotionClassification>(common::V1_0::Classification::NONE));
+ static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
+ static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
+ static_assert(MotionClassification::DEEP_PRESS ==
+ static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
+ return static_cast<MotionClassification>(classification);
+}
+
+static bool isTouchEvent(const NotifyMotionArgs& args) {
+ 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) :
+ type(ClassifierEventType::MOTION), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
+ type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
+ type(type), args(std::move(args)) { };
+
+ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
+ type(other.type), args(std::move(other.args)) { };
+
+ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
+ type = other.type;
+ args = std::move(other.args);
+ return *this;
+}
+
+ClassifierEvent ClassifierEvent::createHalResetEvent() {
+ return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+}
+
+ClassifierEvent ClassifierEvent::createExitEvent() {
+ return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+}
+
+std::optional<int32_t> ClassifierEvent::getDeviceId() const {
+ switch (type) {
+ case ClassifierEventType::MOTION: {
+ NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
+ return motionArgs->deviceId;
+ }
+ case ClassifierEventType::DEVICE_RESET: {
+ NotifyDeviceResetArgs* deviceResetArgs =
+ static_cast<NotifyDeviceResetArgs*>(args.get());
+ return deviceResetArgs->deviceId;
+ }
+ case ClassifierEventType::HAL_RESET: {
+ return std::nullopt;
+ }
+ case ClassifierEventType::EXIT: {
+ return std::nullopt;
+ }
+ }
+}
+
+// --- MotionClassifier ---
+
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) :
+ mEvents(MAX_EVENTS), mService(service) {
+ mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+ // 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());
+}
+
+MotionClassifier::~MotionClassifier() {
+ requestExit();
+ mHalThread.join();
+}
+
+void MotionClassifier::ensureHalThread(const char* function) {
+ if (DEBUG) {
+ if (std::this_thread::get_id() != mHalThread.get_id()) {
+ ALOGE("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).
+ * Should not be called from the thread that notifyMotion runs on.
+ *
+ * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
+ * to return a classification, this would directly impact the touch latency.
+ * To remove any possibility of negatively affecting the touch latency, the HAL
+ * is called from a dedicated thread.
+ */
+void MotionClassifier::callInputClassifierHal() {
+ ensureHalThread(__func__);
+ while (true) {
+ ClassifierEvent event = mEvents.pop();
+ switch (event.type) {
+ case ClassifierEventType::MOTION: {
+ NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
+ common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs);
+ common::V1_0::Classification halClassification = mService->classify(motionEvent);
+ updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+ getMotionClassification(halClassification));
+ break;
+ }
+ case ClassifierEventType::DEVICE_RESET: {
+ const int32_t deviceId = *(event.getDeviceId());
+ mService->resetDevice(deviceId);
+ setClassification(deviceId, MotionClassification::NONE);
+ break;
+ }
+ case ClassifierEventType::HAL_RESET: {
+ mService->reset();
+ clearClassifications();
+ break;
+ }
+ case ClassifierEventType::EXIT: {
+ clearClassifications();
+ return;
+ }
+ }
+ }
+}
+
+void MotionClassifier::requestExit() {
+ reset();
+ mEvents.push(ClassifierEvent::createExitEvent());
+}
+
+void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
+ MotionClassification classification) {
+ std::scoped_lock lock(mLock);
+ const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+ if (eventTime < lastDownTime) {
+ // HAL just finished processing an event that belonged to an earlier gesture,
+ // but new gesture is already in progress. Drop this classification.
+ ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
+ nanoseconds_to_milliseconds(lastDownTime - eventTime));
+ return;
+ }
+ mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
+ std::scoped_lock lock(mLock);
+ mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::clearClassifications() {
+ std::scoped_lock lock(mLock);
+ mClassifications.clear();
+}
+
+MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+}
+
+void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
+ std::scoped_lock lock(mLock);
+ mLastDownTimes[deviceId] = downTime;
+ mClassifications[deviceId] = MotionClassification::NONE;
+}
+
+MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
+ if (!mService) {
+ // If HAL is not present, do nothing
+ return MotionClassification::NONE;
+ }
+ if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
+ updateLastDownTime(args.deviceId, args.downTime);
+ }
+
+ ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
+ bool elementAdded = mEvents.push(std::move(event));
+ if (!elementAdded) {
+ // Queue should not ever overfill. Suspect HAL is slow.
+ ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime);
+ reset();
+ return MotionClassification::NONE;
+ }
+ return getClassification(args.deviceId);
+}
+
+void MotionClassifier::reset() {
+ mEvents.clear();
+ mEvents.push(ClassifierEvent::createHalResetEvent());
+}
+
+/**
+ * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
+ * Request InputClassifier thread to call resetDevice for this particular device.
+ */
+void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
+ int32_t deviceId = args.deviceId;
+ // Clear the pending events right away, to avoid unnecessary work done by the HAL.
+ mEvents.erase([deviceId](const ClassifierEvent& event) {
+ std::optional<int32_t> eventDeviceId = event.getDeviceId();
+ return eventDeviceId && (*eventDeviceId == deviceId);
+ });
+ mEvents.push(std::make_unique<NotifyDeviceResetArgs>(args));
+}
+
+void MotionClassifier::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
+ std::string serviceStatus = mService->ping().isOk() ? "running" : " not responding";
+ dump += StringPrintf(INDENT2 "mService status: %s\n", serviceStatus.c_str());
+ dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
+ mEvents.size(), MAX_EVENTS);
+ dump += INDENT2 "mClassifications, mLastDownTimes:\n";
+ dump += INDENT3 "Device Id\tClassification\tLast down time";
+ // Combine mClassifications and mLastDownTimes into a single table.
+ // Create a superset of device ids.
+ std::unordered_set<int32_t> deviceIds;
+ std::for_each(mClassifications.begin(), mClassifications.end(),
+ [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
+ std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
+ [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
+ for(int32_t deviceId : deviceIds) {
+ const MotionClassification classification =
+ getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+ const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+ dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
+ deviceId, motionClassificationToString(classification), downTime);
+ }
+}
+
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
+ mListener(listener) {
+ if (deepPressEnabled()) {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (service) {
+ mMotionClassifier = std::make_unique<MotionClassifier>(service);
+ } else {
+ ALOGI("Could not obtain InputClassifier HAL");
+ }
+ }
+};
+
+void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+ // pass through
+ mListener->notifyConfigurationChanged(args);
+}
+
+void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
+ // pass through
+ mListener->notifyKey(args);
+}
+
+void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+ NotifyMotionArgs copyArgs = NotifyMotionArgs(*args);
+ if (mMotionClassifier && isTouchEvent(*args)) {
+ // We only cover touch events, for now.
+ copyArgs.classification = mMotionClassifier->classify(copyArgs);
+ }
+ mListener->notifyMotion(©Args);
+}
+
+void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
+ // pass through
+ mListener->notifySwitch(args);
+}
+
+void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ if (mMotionClassifier) {
+ mMotionClassifier->reset(*args);
+ }
+ // continue to next stage
+ mListener->notifyDeviceReset(args);
+}
+
+void InputClassifier::dump(std::string& dump) {
+ dump += "Input Classifier State:\n";
+
+ dump += INDENT1 "Motion Classifier:\n";
+ if (mMotionClassifier) {
+ mMotionClassifier->dump(dump);
+ } else {
+ dump += INDENT2 "<nullptr>";
+ }
+ dump += "\n";
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
new file mode 100644
index 0000000..4b9dae2
--- /dev/null
+++ b/services/inputflinger/InputClassifier.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_CLASSIFIER_H
+#define _UI_INPUT_CLASSIFIER_H
+
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include <thread>
+
+#include "BlockingQueue.h"
+#include "InputListener.h"
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+namespace android {
+
+enum class ClassifierEventType : uint8_t {
+ MOTION = 0,
+ DEVICE_RESET = 1,
+ HAL_RESET = 2,
+ EXIT = 3,
+};
+
+struct ClassifierEvent {
+ ClassifierEventType type;
+ std::unique_ptr<NotifyArgs> args;
+
+ ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
+ ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
+ ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
+ ClassifierEvent(ClassifierEvent&& other);
+ ClassifierEvent& operator=(ClassifierEvent&& other);
+
+ // Convenience function to create a HAL_RESET event
+ static ClassifierEvent createHalResetEvent();
+ // Convenience function to create an EXIT event
+ static ClassifierEvent createExitEvent();
+
+ std::optional<int32_t> getDeviceId() const;
+};
+
+// --- Interfaces ---
+
+/**
+ * Interface for adding a MotionClassification to NotifyMotionArgs.
+ *
+ * To implement, override the classify function.
+ */
+class MotionClassifierInterface {
+public:
+ MotionClassifierInterface() { }
+ virtual ~MotionClassifierInterface() { }
+ /**
+ * Based on the motion event described by NotifyMotionArgs,
+ * provide a MotionClassification for the current gesture.
+ */
+ virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
+ virtual void reset() = 0;
+ virtual void reset(const NotifyDeviceResetArgs& args) = 0;
+
+ /**
+ * Dump the state of the motion classifier
+ */
+ virtual void dump(std::string& dump) = 0;
+};
+
+/**
+ * Base interface for an InputListener stage.
+ * Provides classification to events.
+ */
+class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+public:
+ /**
+ * Dump the state of the input classifier.
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void dump(std::string& dump) = 0;
+protected:
+ InputClassifierInterface() { }
+ virtual ~InputClassifierInterface() { }
+};
+
+// --- Implementations ---
+
+/**
+ * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
+ * in order to determine the classification for the current gesture.
+ *
+ * The InputClassifier HAL may keep track of the entire gesture in order to determine
+ * the classification, and may be hardware-specific. It may use the data in
+ * NotifyMotionArgs::videoFrames field to drive the classification decisions.
+ * The HAL is called from a separate thread.
+ */
+class MotionClassifier final : public MotionClassifierInterface {
+public:
+ MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
+ ~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.
+ *
+ * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
+ * in a particular gesture.
+ */
+ virtual MotionClassification classify(const NotifyMotionArgs& args) override;
+ virtual void reset() override;
+ virtual void reset(const NotifyDeviceResetArgs& args) override;
+
+ virtual void dump(std::string& dump) override;
+
+private:
+ // The events that need to be sent to the HAL.
+ BlockingQueue<ClassifierEvent> mEvents;
+ /**
+ * Thread that will communicate with InputClassifier HAL.
+ * This should be the only thread that communicates with InputClassifier HAL,
+ * because this thread is allowed to block on the HAL calls.
+ */
+ 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 __function__
+ */
+ void ensureHalThread(const char* function);
+ /**
+ * Call the InputClassifier HAL
+ */
+ void callInputClassifierHal();
+ /**
+ * Access to the InputClassifier HAL
+ */
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
+ std::mutex mLock;
+ /**
+ * Per-device input classifications. Should only be accessed using the
+ * getClassification / setClassification methods.
+ */
+ std::unordered_map<int32_t /*deviceId*/, MotionClassification>
+ mClassifications; //GUARDED_BY(mLock);
+ /**
+ * Set the current classification for a given device.
+ */
+ void setClassification(int32_t deviceId, MotionClassification classification);
+ /**
+ * Get the current classification for a given device.
+ */
+ MotionClassification getClassification(int32_t deviceId);
+ void updateClassification(int32_t deviceId, nsecs_t eventTime,
+ MotionClassification classification);
+ /**
+ * Clear all current classifications
+ */
+ void clearClassifications();
+ /**
+ * Per-device times when the last ACTION_DOWN was received.
+ * Used to reject late classifications that do not belong to the current gesture.
+ *
+ * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
+ */
+ std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/>
+ mLastDownTimes; //GUARDED_BY(mLock);
+ void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
+
+ /**
+ * Exit the InputClassifier HAL thread.
+ * Useful for tests to ensure proper cleanup.
+ */
+ void requestExit();
+};
+
+/**
+ * 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:
+ explicit InputClassifier(const sp<InputListenerInterface>& listener);
+
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+ virtual void dump(std::string& dump) override;
+
+private:
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr;
+ // The next stage to pass input events to
+ sp<InputListenerInterface> mListener;
+};
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9d92435..9702fb2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -215,10 +215,6 @@
return true;
}
-static bool isMainDisplay(int32_t displayId) {
- return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
static void dumpRegion(std::string& dump, const Region& region) {
if (region.isEmpty()) {
dump += "<empty>";
@@ -525,7 +521,7 @@
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
- int32_t x, int32_t y) {
+ int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
// Traverse windows from front to back to find touched window.
const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
size_t numWindows = windowHandles.size();
@@ -540,10 +536,25 @@
bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
+ int32_t portalToDisplayId = windowInfo->portalToDisplayId;
+ if (portalToDisplayId != ADISPLAY_ID_NONE
+ && portalToDisplayId != displayId) {
+ if (addPortalWindows) {
+ // For the monitoring channels of the display.
+ mTempTouchState.addPortalWindow(windowHandle);
+ }
+ return findTouchedWindowAtLocked(
+ portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+ }
// Found window.
return windowHandle;
}
}
+
+ if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ mTempTouchState.addOrUpdateWindow(
+ windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+ }
}
}
}
@@ -930,6 +941,22 @@
// Add monitor channels from event's or focused display.
addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+ if (isPointerEvent) {
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
+ if (stateIndex >= 0) {
+ const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+ if (!state.portalWindows.isEmpty()) {
+ // The event has gone through these portal windows, so we add monitoring targets of
+ // the corresponding displays as well.
+ for (size_t i = 0; i < state.portalWindows.size(); i++) {
+ const InputWindowInfo* windowInfo = state.portalWindows.itemAt(i)->getInfo();
+ addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+ -windowInfo->frameLeft, -windowInfo->frameTop);
+ }
+ }
+ }
+ }
+
// Dispatch the motion.
if (conflictingPointerActions) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1297,37 +1324,8 @@
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> newTouchedWindowHandle;
- bool isTouchModal = false;
-
- // Traverse windows from front to back to find touched window and outside targets.
- const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
- size_t numWindows = windowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo->displayId != displayId) {
- continue; // wrong display
- }
-
- int32_t flags = windowInfo->layoutParamsFlags;
- if (windowInfo->visible) {
- if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
- | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
- if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- newTouchedWindowHandle = windowHandle;
- break; // found touched window, exit window loop
- }
- }
-
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(
- windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
- }
- }
- }
+ sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+ displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
// Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr
@@ -1689,7 +1687,7 @@
}
void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
- int32_t displayId) {
+ int32_t displayId, float xOffset, float yOffset) {
std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
mMonitoringChannelsByDisplay.find(displayId);
@@ -1702,8 +1700,8 @@
InputTarget& target = inputTargets.editTop();
target.inputChannel = monitoringChannels[i];
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = 0;
- target.yOffset = 0;
+ target.xOffset = xOffset;
+ target.yOffset = yOffset;
target.pointerIds.clear();
target.globalScaleFactor = 1.0f;
}
@@ -2722,8 +2720,7 @@
}
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
- // TODO: support sending secondary display events to input filter
- return mInputFilterEnabled && isMainDisplay(args->displayId);
+ return mInputFilterEnabled;
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
@@ -3107,7 +3104,8 @@
Vector<sp<InputWindowHandle>> newHandles;
for (size_t i = 0; i < numWindows; i++) {
const sp<InputWindowHandle>& handle = inputWindowHandles.itemAt(i);
- if (!handle->updateInfo() || getInputChannelLocked(handle->getToken()) == nullptr) {
+ if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr
+ && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) {
ALOGE("Window handle %s has no registered input channel",
handle->getName().c_str());
continue;
@@ -3542,6 +3540,14 @@
} else {
dump += INDENT3 "Windows: <none>\n";
}
+ if (!state.portalWindows.isEmpty()) {
+ dump += INDENT3 "Portal windows:\n";
+ for (size_t i = 0; i < state.portalWindows.size(); i++) {
+ const sp<InputWindowHandle> portalWindowHandle = state.portalWindows.itemAt(i);
+ dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
+ i, portalWindowHandle->getName().c_str());
+ }
+ }
}
} else {
dump += INDENT "TouchStates: <no displays touched>\n";
@@ -3558,11 +3564,12 @@
const InputWindowInfo* windowInfo = windowHandle->getInfo();
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
- "paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
"frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
+ windowInfo->portalToDisplayId,
toString(windowInfo->paused),
toString(windowInfo->hasFocus),
toString(windowInfo->hasWallpaper),
@@ -4911,6 +4918,7 @@
source = 0;
displayId = ADISPLAY_ID_NONE;
windows.clear();
+ portalWindows.clear();
}
void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4920,6 +4928,7 @@
source = other.source;
displayId = other.displayId;
windows = other.windows;
+ portalWindows = other.portalWindows;
}
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4948,6 +4957,17 @@
touchedWindow.pointerIds = pointerIds;
}
+void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+ size_t numWindows = portalWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ sp<InputWindowHandle> portalWindowHandle = portalWindows.itemAt(i);
+ if (portalWindowHandle == windowHandle) {
+ return;
+ }
+ }
+ portalWindows.push_back(windowHandle);
+}
+
void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
for (size_t i = 0; i < windows.size(); i++) {
if (windows.itemAt(i).windowHandle == windowHandle) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 327dbbd..2d8df5c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -921,7 +921,8 @@
// to transfer focus to a new application.
EventEntry* mNextUnblockedEvent;
- sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
+ sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ bool addOutsideTargets = false, bool addPortalWindows = false);
// All registered connections mapped by channel file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByFd;
@@ -1016,12 +1017,18 @@
int32_t displayId; // id to the display that currently has a touch, others are rejected
Vector<TouchedWindow> windows;
+ // This collects the portal windows that the touch has gone through. Each portal window
+ // targets a display (embedded display for most cases). With this info, we can add the
+ // monitoring channels of the displays touched.
+ Vector<sp<InputWindowHandle>> portalWindows;
+
TouchState();
~TouchState();
void reset();
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
+ void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
void removeWindow(const sp<InputWindowHandle>& windowHandle);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
@@ -1096,7 +1103,8 @@
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
- void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId);
+ void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId,
+ float xOffset = 0, float yOffset = 0);
void pokeUserActivityLocked(const EventEntry* eventEntry);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index b349c73..a403f31 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -28,12 +28,16 @@
NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
uint32_t sequenceNum, nsecs_t eventTime) :
- NotifyArgs(sequenceNum), eventTime(eventTime) {
+ NotifyArgs(sequenceNum, eventTime) {
}
NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
const NotifyConfigurationChangedArgs& other) :
- NotifyArgs(other.sequenceNum), eventTime(other.eventTime) {
+ NotifyArgs(other.sequenceNum, other.eventTime) {
+}
+
+bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime;
}
void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -47,20 +51,35 @@
uint32_t source, int32_t displayId, uint32_t policyFlags,
int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, nsecs_t downTime) :
- NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId), source(source),
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
displayId(displayId), policyFlags(policyFlags),
action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
metaState(metaState), downTime(downTime) {
}
NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
- NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId),
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
action(other.action), flags(other.flags),
keyCode(other.keyCode), scanCode(other.scanCode),
metaState(other.metaState), downTime(other.downTime) {
}
+bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId
+ && source == rhs.source
+ && displayId == rhs.displayId
+ && policyFlags == rhs.policyFlags
+ && action == rhs.action
+ && flags == rhs.flags
+ && keyCode == rhs.keyCode
+ && scanCode == rhs.scanCode
+ && metaState == rhs.metaState
+ && downTime == rhs.downTime;
+}
+
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
@@ -76,7 +95,7 @@
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime,
const std::vector<TouchVideoFrame>& videoFrames) :
- NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId), source(source),
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
displayId(displayId), policyFlags(policyFlags),
action(action), actionButton(actionButton),
flags(flags), metaState(metaState), buttonState(buttonState),
@@ -91,7 +110,7 @@
}
NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
- NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId),
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
action(other.action), actionButton(other.actionButton), flags(other.flags),
metaState(other.metaState), buttonState(other.buttonState),
@@ -105,6 +124,43 @@
}
}
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+ bool equal =
+ sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId
+ && source == rhs.source
+ && displayId == rhs.displayId
+ && policyFlags == rhs.policyFlags
+ && action == rhs.action
+ && actionButton == rhs.actionButton
+ && flags == rhs.flags
+ && metaState == rhs.metaState
+ && buttonState == rhs.buttonState
+ && classification == rhs.classification
+ && edgeFlags == rhs.edgeFlags
+ && deviceTimestamp == rhs.deviceTimestamp
+ && pointerCount == rhs.pointerCount
+ // PointerProperties and PointerCoords are compared separately below
+ && xPrecision == rhs.xPrecision
+ && yPrecision == rhs.yPrecision
+ && downTime == rhs.downTime
+ && videoFrames == rhs.videoFrames;
+ if (!equal) {
+ return false;
+ }
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ equal =
+ pointerProperties[i] == rhs.pointerProperties[i]
+ && pointerCoords[i] == rhs.pointerCoords[i];
+ if (!equal) {
+ return false;
+ }
+ }
+ return true;
+}
+
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
@@ -114,15 +170,23 @@
NotifySwitchArgs::NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
uint32_t switchValues, uint32_t switchMask) :
- NotifyArgs(sequenceNum), eventTime(eventTime), policyFlags(policyFlags),
+ NotifyArgs(sequenceNum, eventTime), policyFlags(policyFlags),
switchValues(switchValues), switchMask(switchMask) {
}
NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
- NotifyArgs(other.sequenceNum), eventTime(other.eventTime), policyFlags(other.policyFlags),
+ NotifyArgs(other.sequenceNum, other.eventTime), policyFlags(other.policyFlags),
switchValues(other.switchValues), switchMask(other.switchMask) {
}
+bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && policyFlags == rhs.policyFlags
+ && switchValues == rhs.switchValues
+ && switchMask == rhs.switchMask;
+}
+
void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifySwitch(this);
}
@@ -132,11 +196,17 @@
NotifyDeviceResetArgs::NotifyDeviceResetArgs(
uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
- NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId) {
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId) {
}
NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
- NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId) {
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId) {
+}
+
+bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId;
}
void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 1d7ea00..a7fd9ba 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -34,7 +34,8 @@
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = createInputReader(readerPolicy, mDispatcher);
+ mClassifier = new InputClassifier(mDispatcher);
+ mReader = createInputReader(readerPolicy, mClassifier);
initialize();
}
@@ -83,6 +84,10 @@
return mReader;
}
+sp<InputClassifierInterface> InputManager::getClassifier() {
+ return mClassifier;
+}
+
sp<InputDispatcherInterface> InputManager::getDispatcher() {
return mDispatcher;
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index ab309b1..e632da3 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -23,7 +23,9 @@
#include "EventHub.h"
#include "InputReaderBase.h"
+#include "InputClassifier.h"
#include "InputDispatcher.h"
+#include "InputReader.h"
#include <input/Input.h>
#include <input/InputTransport.h>
@@ -88,6 +90,7 @@
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
+ virtual sp<InputClassifierInterface> getClassifier();
virtual sp<InputDispatcherInterface> getDispatcher();
virtual void setInputWindows(const Vector<InputWindowInfo>& handles);
@@ -100,6 +103,8 @@
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
+ sp<InputClassifierInterface> mClassifier;
+
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 2b31f6e..f84aa34 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -57,6 +57,7 @@
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
+#include <statslog.h>
#define INDENT " "
#define INDENT2 " "
@@ -71,18 +72,21 @@
// --- Constants ---
// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static const size_t MAX_SLOTS = 32;
+static constexpr size_t MAX_SLOTS = 32;
// Maximum amount of latency to add to touch events while waiting for data from an
// external stylus.
-static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
// Artificial latency on synthetic events created from stylus data without corresponding touch
// data.
-static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// How often to report input event statistics
+static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
// --- Static Functions ---
@@ -801,6 +805,30 @@
return false;
}
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+ return false;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+ // No associated display. By default, can dispatch to all displays.
+ if (!associatedDisplayId) {
+ return true;
+ }
+
+ if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+ ALOGW("Device has associated, but no associated display id.");
+ return true;
+ }
+
+ return *associatedDisplayId == displayId;
+}
+
void InputReader::dump(std::string& dump) {
AutoMutex _l(mLock);
@@ -1271,6 +1299,18 @@
mContext->getListener()->notifyDeviceReset(&args);
}
+std::optional<int32_t> InputDevice::getAssociatedDisplay() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
+ if (associatedDisplayId) {
+ return associatedDisplayId;
+ }
+ }
+
+ return std::nullopt;
+}
// --- CursorButtonAccumulator ---
@@ -2643,7 +2683,7 @@
}
// Update the PointerController if viewports changed.
- if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.mode == Parameters::MODE_POINTER) {
getPolicy()->obtainPointerController(getDeviceId());
}
bumpGeneration();
@@ -2915,6 +2955,19 @@
}
}
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ // If the device is orientationAware and not a mouse,
+ // it expects to dispatch events to any display
+ return std::make_optional(ADISPLAY_ID_NONE);
+ }
+ }
+ return std::nullopt;
+}
+
// --- RotaryEncoderInputMapper ---
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
@@ -4287,12 +4340,25 @@
mExternalStylusFusionTimeout = LLONG_MAX;
}
+void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) {
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t latency = now - evdevTime;
+ mStatistics.addValue(nanoseconds_to_microseconds(latency));
+ nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
+ if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
+ android::util::stats_write(android::util::TOUCH_EVENT_REPORTED,
+ mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev());
+ mStatistics.reset(now);
+ }
+}
+
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ reportEventForStatistics(rawEvent->when);
sync(rawEvent->when);
}
}
@@ -6494,9 +6560,7 @@
ALOG_ASSERT(false);
}
}
- const int32_t displayId = mPointerController != nullptr ?
- mPointerController->getDisplayId() : mViewport.displayId;
-
+ const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
@@ -6813,6 +6877,16 @@
return true;
}
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ return std::make_optional(mViewport.displayId);
+ }
+ }
+ return std::nullopt;
+}
// --- SingleTouchInputMapper ---
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 35f3c23..fed55ac 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -146,6 +146,7 @@
ssize_t repeat, int32_t token);
virtual void cancelVibrate(int32_t deviceId, int32_t token);
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
protected:
// These members are protected so they can be instrumented by test cases.
virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
@@ -320,6 +321,7 @@
return value;
}
+ std::optional<int32_t> getAssociatedDisplay();
private:
InputReaderContext* mContext;
int32_t mId;
@@ -567,6 +569,69 @@
}
};
+/**
+ * Basic statistics information.
+ * Keep track of min, max, average, and standard deviation of the received samples.
+ * Used to report latency information about input events.
+ */
+struct LatencyStatistics {
+ float min;
+ float max;
+ // Sum of all samples
+ float sum;
+ // Sum of squares of all samples
+ float sum2;
+ // The number of samples
+ size_t count;
+ // The last time statistics were reported.
+ nsecs_t lastReportTime;
+
+ LatencyStatistics() {
+ reset(systemTime(SYSTEM_TIME_MONOTONIC));
+ }
+
+ inline void addValue(float x) {
+ if (x < min) {
+ min = x;
+ }
+ if (x > max) {
+ max = x;
+ }
+ sum += x;
+ sum2 += x * x;
+ count++;
+ }
+
+ // Get the average value. Should not be called if no samples have been added.
+ inline float mean() {
+ if (count == 0) {
+ return 0;
+ }
+ return sum / count;
+ }
+
+ // Get the standard deviation. Should not be called if no samples have been added.
+ inline float stdev() {
+ if (count == 0) {
+ return 0;
+ }
+ float average = mean();
+ return sqrt(sum2 / count - average * average);
+ }
+
+ /**
+ * Reset internal state. The variable 'when' is the time when the data collection started.
+ * Call this to start a new data collection window.
+ */
+ inline void reset(nsecs_t when) {
+ max = 0;
+ min = std::numeric_limits<float>::max();
+ sum = 0;
+ sum2 = 0;
+ count = 0;
+ lastReportTime = when;
+ }
+};
/* Keeps track of the state of single-touch protocol. */
class SingleTouchMotionAccumulator {
@@ -715,7 +780,9 @@
virtual void updateExternalStylusState(const StylusState& state);
virtual void fadePointer();
-
+ virtual std::optional<int32_t> getAssociatedDisplay() {
+ return std::nullopt;
+ }
protected:
InputDevice* mDevice;
InputReaderContext* mContext;
@@ -869,6 +936,7 @@
virtual void fadePointer();
+ virtual std::optional<int32_t> getAssociatedDisplay();
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -962,7 +1030,7 @@
virtual void cancelTouch(nsecs_t when);
virtual void timeoutExpired(nsecs_t when);
virtual void updateExternalStylusState(const StylusState& state);
-
+ virtual std::optional<int32_t> getAssociatedDisplay();
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
CursorScrollAccumulator mCursorScrollAccumulator;
@@ -1511,6 +1579,9 @@
VelocityControl mWheelXVelocityControl;
VelocityControl mWheelYVelocityControl;
+ // Latency statistics for touch events
+ struct LatencyStatistics mStatistics;
+
std::optional<DisplayViewport> findViewport();
void resetExternalStylus();
@@ -1580,6 +1651,8 @@
static void assignPointerIds(const RawState* last, RawState* current);
+ void reportEventForStatistics(nsecs_t evdevTime);
+
const char* modeToString(DeviceMode deviceMode);
};
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
new file mode 100644
index 0000000..0313a40
--- /dev/null
+++ b/services/inputflinger/OWNERS
@@ -0,0 +1,2 @@
+michaelwr@google.com
+svv@google.com
diff --git a/services/inputflinger/include/TouchVideoDevice.h b/services/inputflinger/TouchVideoDevice.h
similarity index 96%
rename from services/inputflinger/include/TouchVideoDevice.h
rename to services/inputflinger/TouchVideoDevice.h
index 7ff2653..3d5c8c6 100644
--- a/services/inputflinger/include/TouchVideoDevice.h
+++ b/services/inputflinger/TouchVideoDevice.h
@@ -73,6 +73,8 @@
size_t readAndQueueFrames();
/**
* Return all of the queued frames, and erase them from the local buffer.
+ * The returned frames are in the order that they were received from the
+ * v4l2 device, with the oldest frame at the index 0.
*/
std::vector<TouchVideoFrame> consumeFrames();
/**
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 53247a0..cd8caf7 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -32,10 +32,12 @@
/* Superclass of all input event argument objects */
struct NotifyArgs {
uint32_t sequenceNum;
+ nsecs_t eventTime;
- inline NotifyArgs() : sequenceNum(0) { }
+ inline NotifyArgs() : sequenceNum(0), eventTime(0) { }
- inline explicit NotifyArgs(uint32_t sequenceNum) : sequenceNum(sequenceNum) { }
+ inline explicit NotifyArgs(uint32_t sequenceNum, nsecs_t eventTime) :
+ sequenceNum(sequenceNum), eventTime(eventTime) { }
virtual ~NotifyArgs() { }
@@ -45,10 +47,11 @@
/* Describes a configuration change event. */
struct NotifyConfigurationChangedArgs : public NotifyArgs {
- nsecs_t eventTime;
inline NotifyConfigurationChangedArgs() { }
+ bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
+
NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime);
NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
@@ -61,7 +64,6 @@
/* Describes a key event. */
struct NotifyKeyArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
int32_t displayId;
@@ -79,6 +81,8 @@
int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime);
+ bool operator==(const NotifyKeyArgs& rhs) const;
+
NotifyKeyArgs(const NotifyKeyArgs& other);
virtual ~NotifyKeyArgs() { }
@@ -89,7 +93,6 @@
/* Describes a motion event. */
struct NotifyMotionArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
int32_t displayId;
@@ -134,13 +137,14 @@
virtual ~NotifyMotionArgs() { }
+ bool operator==(const NotifyMotionArgs& rhs) const;
+
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a switch event. */
struct NotifySwitchArgs : public NotifyArgs {
- nsecs_t eventTime;
uint32_t policyFlags;
uint32_t switchValues;
uint32_t switchMask;
@@ -152,6 +156,8 @@
NotifySwitchArgs(const NotifySwitchArgs& other);
+ bool operator==(const NotifySwitchArgs rhs) const;
+
virtual ~NotifySwitchArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
@@ -161,7 +167,6 @@
/* Describes a device reset event, such as when a device is added,
* reconfigured, or removed. */
struct NotifyDeviceResetArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
inline NotifyDeviceResetArgs() { }
@@ -170,6 +175,8 @@
NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
+ bool operator==(const NotifyDeviceResetArgs& rhs) const;
+
virtual ~NotifyDeviceResetArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fe1c50b..fe1d4d0 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -100,6 +100,9 @@
virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
ssize_t repeat, int32_t token) = 0;
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+ /* Return true if the device can send input events to the specified display. */
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
};
/* Reads raw events from the event hub and processes them, endlessly. */
@@ -265,7 +268,7 @@
pointerGestureSwipeMaxWidthRatio(0.25f),
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false) { }
+ showTouches(false), pointerCapture(false) { }
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 5b275fb..1835449 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -3,8 +3,11 @@
cc_test {
name: "inputflinger_tests",
srcs: [
- "InputReader_test.cpp",
+ "BlockingQueue_test.cpp",
+ "TestInputListener.cpp",
+ "InputClassifier_test.cpp",
"InputDispatcher_test.cpp",
+ "InputReader_test.cpp",
],
cflags: [
"-Wall",
@@ -13,6 +16,7 @@
"-Wno-unused-parameter",
],
shared_libs: [
+ "android.hardware.input.classifier@1.0",
"libbase",
"libbinder",
"libcutils",
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
new file mode 100644
index 0000000..0dea8d7
--- /dev/null
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../BlockingQueue.h"
+
+
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+
+// --- BlockingQueueTest ---
+
+/**
+ * Sanity check of basic pop and push operation.
+ */
+TEST(BlockingQueueTest, Queue_AddAndRemove) {
+ constexpr size_t capacity = 10;
+ BlockingQueue<int> queue(capacity);
+
+ ASSERT_TRUE(queue.push(1));
+ ASSERT_EQ(queue.pop(), 1);
+}
+
+/**
+ * Make sure the queue has strict capacity limits.
+ */
+TEST(BlockingQueueTest, Queue_ReachesCapacity) {
+ constexpr size_t capacity = 3;
+ BlockingQueue<int> queue(capacity);
+
+ // First 3 elements should be added successfully
+ ASSERT_TRUE(queue.push(1));
+ ASSERT_TRUE(queue.push(2));
+ ASSERT_TRUE(queue.push(3));
+ ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
+}
+
+/**
+ * Make sure the queue maintains FIFO order.
+ * Add elements and remove them, and check the order.
+ */
+TEST(BlockingQueueTest, Queue_isFIFO) {
+ constexpr size_t capacity = 10;
+ BlockingQueue<int> queue(capacity);
+
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_TRUE(queue.push(static_cast<int>(i)));
+ }
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_EQ(queue.pop(), static_cast<int>(i));
+ }
+}
+
+TEST(BlockingQueueTest, Queue_Clears) {
+ constexpr size_t capacity = 2;
+ BlockingQueue<int> queue(capacity);
+
+ queue.push(1);
+ queue.push(2);
+ queue.clear();
+ queue.push(3);
+ // Should no longer receive elements 1 and 2
+ ASSERT_EQ(3, queue.pop());
+}
+
+TEST(BlockingQueueTest, Queue_Erases) {
+ constexpr size_t capacity = 4;
+ BlockingQueue<int> queue(capacity);
+
+ queue.push(1);
+ queue.push(2);
+ queue.push(3);
+ queue.push(4);
+ // Erase elements 2 and 4
+ queue.erase([](int element) { return element == 2 || element == 4; });
+ // Should no longer receive elements 2 and 4
+ ASSERT_EQ(1, queue.pop());
+ ASSERT_EQ(3, queue.pop());
+}
+
+// --- BlockingQueueTest - Multiple threads ---
+
+TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) {
+ constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap
+ BlockingQueue<int> queue(capacity);
+
+ // Fill queue from a different thread
+ std::thread fillQueue([&queue](){
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_TRUE(queue.push(static_cast<int>(i)));
+ }
+ });
+
+ // Make sure all elements are received in correct order
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_EQ(queue.pop(), static_cast<int>(i));
+ }
+
+ fillQueue.join();
+}
+
+/**
+ * When the queue has no elements, and pop is called, it should block
+ * the current thread until an element is added to the queue (from another thread).
+ * Here we create a separate thread and call pop on an empty queue. Next,
+ * we check that the thread is blocked.
+ */
+TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) {
+ constexpr size_t capacity = 1;
+ BlockingQueue<int> queue(capacity);
+
+ std::atomic_bool hasReceivedElement = false;
+
+ // fill queue from a different thread
+ std::thread waitUntilHasElements([&queue, &hasReceivedElement](){
+ queue.pop(); // This should block until an element has been added
+ hasReceivedElement = true;
+ });
+
+ ASSERT_FALSE(hasReceivedElement);
+ queue.push(1);
+ waitUntilHasElements.join();
+ ASSERT_TRUE(hasReceivedElement);
+}
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
new file mode 100644
index 0000000..20699c9
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../InputClassifier.h"
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using namespace android::hardware::input;
+
+namespace android {
+
+// --- InputClassifierTest ---
+
+static NotifyMotionArgs generateBasicMotionArgs() {
+ // Create a basic motion event for testing
+ constexpr size_t pointerCount = 1;
+ PointerProperties properties[pointerCount];
+ properties[0].id = 0;
+ properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ PointerCoords coords[pointerCount];
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+ static constexpr nsecs_t downTime = 2;
+ NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
+ AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
+ 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
+ 0/*pointerCount*/, properties, coords, 0/*xPrecision*/, 0/*yPrecision*/,
+ downTime, {}/*videoFrames*/);
+ return motionArgs;
+}
+
+class InputClassifierTest : public testing::Test {
+protected:
+ sp<InputClassifierInterface> mClassifier;
+ sp<TestInputListener> mTestListener;
+
+ virtual void SetUp() override {
+ mTestListener = new TestInputListener();
+ mClassifier = new InputClassifier(mTestListener);
+ }
+
+ virtual void TearDown() override {
+ mClassifier.clear();
+ mTestListener.clear();
+ }
+};
+
+/**
+ * Create a basic configuration change and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) {
+ // Create a basic configuration change and send to classifier
+ NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/);
+
+ mClassifier->notifyConfigurationChanged(&args);
+ NotifyConfigurationChangedArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
+ // Create a basic key event and send to classifier
+ NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
+ ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
+ AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+
+ mClassifier->notifyKey(&args);
+ NotifyKeyArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+
+/**
+ * Create a basic motion event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+ mClassifier->notifyMotion(&motionArgs);
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(motionArgs, args);
+}
+
+/**
+ * Create a basic switch event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) {
+ NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/,
+ 5/*switchMask*/);
+
+ mClassifier->notifySwitch(&args);
+ NotifySwitchArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+/**
+ * Create a basic device reset event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
+ NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+
+ mClassifier->notifyDeviceReset(&args);
+ NotifyDeviceResetArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+// --- MotionClassifierTest ---
+
+class MotionClassifierTest : public testing::Test {
+protected:
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
+
+ virtual void SetUp() override {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (service) {
+ mMotionClassifier = std::make_unique<MotionClassifier>(service);
+ }
+ }
+};
+
+/**
+ * Since MotionClassifier creates a new thread to communicate with HAL,
+ * it's not really expected to ever exit. However, for testing purposes,
+ * we need to ensure that it is able to exit cleanly.
+ * If the thread is not properly cleaned up, it will generate SIGABRT.
+ * The logic for exiting the thread and cleaning up the resources is inside
+ * the destructor. Here, we just make sure the destructor does not crash.
+ */
+TEST_F(MotionClassifierTest, Destructor_DoesNotCrash) {
+ mMotionClassifier = nullptr;
+}
+
+/**
+ * Make sure MotionClassifier can handle events that don't have any
+ * video frames.
+ */
+TEST_F(MotionClassifierTest, Classify_NoVideoFrames) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure nothing crashes when a videoFrame is sent.
+ */
+TEST_F(MotionClassifierTest, Classify_OneVideoFrame) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ std::vector<int16_t> videoData = {1, 2, 3, 4};
+ timeval timestamp = { 1, 1};
+ TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+ motionArgs.videoFrames = {frame};
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure nothing crashes when 2 videoFrames are sent.
+ */
+TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ std::vector<int16_t> videoData1 = {1, 2, 3, 4};
+ timeval timestamp1 = { 1, 1};
+ TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1);
+
+ std::vector<int16_t> videoData2 = {6, 6, 6, 6};
+ timeval timestamp2 = { 1, 2};
+ TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2);
+
+ motionArgs.videoFrames = {frame1, frame2};
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when it is reset.
+ */
+TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset());
+ }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when a device is reset.
+ */
+TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) {
+ NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args));
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3edec40..0f987c3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -48,9 +48,51 @@
public:
FakeInputDispatcherPolicy() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasNotCalled() {
+ ASSERT_FALSE(mInputEventFiltered)
+ << "Expected filterInputEvent() to not have been called.";
}
private:
+ bool mInputEventFiltered;
+ nsecs_t mTime;
+ int32_t mAction;
+ int32_t mDisplayId;
+
virtual void notifyConfigurationChanged(nsecs_t) {
}
@@ -70,7 +112,26 @@
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent*, uint32_t) {
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
+ mTime = keyEvent->getEventTime();
+ mAction = keyEvent->getAction();
+ mDisplayId = keyEvent->getDisplayId();
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
+ mTime = motionEvent->getEventTime();
+ mAction = motionEvent->getAction();
+ mDisplayId = motionEvent->getDisplayId();
+ break;
+ }
+ }
+
+ mInputEventFiltered = true;
return true;
}
@@ -99,6 +160,13 @@
virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
return false;
}
+
+ void reset() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
};
@@ -376,7 +444,7 @@
}
virtual bool updateInfo() {
- mInfo.token = mServerChannel->getToken();
+ mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
mInfo.name = mName;
mInfo.layoutParamsFlags = 0;
mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
@@ -405,14 +473,9 @@
mFocused = true;
}
- void assertNoEvents() {
- uint32_t consumeSeq;
- InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_NE(OK, status)
- << mName.c_str()
- << ": should not have received any events, so consume(..) should not return OK.";
+ void releaseChannel() {
+ mServerChannel.clear();
+ InputWindowHandle::releaseChannel();
}
protected:
virtual bool handled() {
@@ -469,6 +532,40 @@
INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
+static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid key event.
+ NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+
+ return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
+ POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+ AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
+ pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
+ /* videoFrames */ {});
+
+ return args;
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -567,24 +664,25 @@
sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
ADISPLAY_ID_DEFAULT);
- windowTop->setFocus();
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ windowTop->setFocus();
+ windowSecond->setFocus();
Vector<sp<InputWindowHandle>> inputWindowHandles;
inputWindowHandles.add(windowTop);
inputWindowHandles.add(windowSecond);
-
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-
// Release channel for window is no longer valid.
windowTop->releaseChannel();
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
- // Test inject a motion down, should timeout because of no target channel.
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+ // Test inject a key down, should dispatch to a valid window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Top window is invalid, so it should not receive any input event.
windowTop->assertNoEvents();
- windowSecond->assertNoEvents();
+ windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
}
/* Test InputDispatcher for MultiDisplay */
@@ -740,4 +838,76 @@
monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
}
+class InputFilterTest : public InputDispatcherTest {
+protected:
+ static constexpr int32_t SECOND_DISPLAY_ID = 1;
+
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ NotifyMotionArgs motionArgs;
+
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+
+ void testNotifyKey(bool expectToBeFiltered) {
+ NotifyKeyArgs keyArgs;
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+ mDispatcher->notifyKey(&keyArgs);
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
+ mDispatcher->notifyKey(&keyArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+};
+
+// Test InputFilter for MotionEvent
+TEST_F(InputFilterTest, MotionEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if touch events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Test touch on both primary and second display, and check if both events are filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Test touch on both primary and second display, and check if both events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+}
+
+// Test InputFilter for KeyEvent
+TEST_F(InputFilterTest, KeyEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if key event aren't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Send a key event, and check if it is filtered.
+ testNotifyKey(/*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Send a key event, and check if it isn't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index de87e7f..84c5ad6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -15,12 +15,13 @@
*/
#include "../InputReader.h"
+#include "TestInputListener.h"
-#include <inttypes.h>
-#include <utils/List.h>
#include <gtest/gtest.h>
+#include <inttypes.h>
#include <math.h>
+
namespace android {
// An arbitrary time value.
@@ -273,114 +274,6 @@
}
};
-
-// --- FakeInputListener ---
-
-class FakeInputListener : public InputListenerInterface {
-private:
- List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
- List<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
- List<NotifyKeyArgs> mNotifyKeyArgsQueue;
- List<NotifyMotionArgs> mNotifyMotionArgsQueue;
- List<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
-protected:
- virtual ~FakeInputListener() { }
-
-public:
- FakeInputListener() {
- }
-
- void assertNotifyConfigurationChangedWasCalled(
- NotifyConfigurationChangedArgs* outEventArgs = nullptr) {
- ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
- }
- mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
- }
-
- void assertNotifyConfigurationChangedWasNotCalled() {
- ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to not have been called.";
- }
-
- void assertNotifyDeviceResetWasCalled(
- NotifyDeviceResetArgs* outEventArgs = nullptr) {
- ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
- }
- mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
- }
-
- void assertNotifyDeviceResetWasNotCalled() {
- ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to not have been called.";
- }
-
- void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr) {
- ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyKeyArgsQueue.begin();
- }
- mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
- }
-
- void assertNotifyKeyWasNotCalled() {
- ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to not have been called.";
- }
-
- void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr) {
- ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyMotionArgsQueue.begin();
- }
- mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
- }
-
- void assertNotifyMotionWasNotCalled() {
- ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to not have been called.";
- }
-
- void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr) {
- ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
- << "Expected notifySwitch() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifySwitchArgsQueue.begin();
- }
- mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
- }
-
-private:
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- mNotifyConfigurationChangedArgsQueue.push_back(*args);
- }
-
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mNotifyDeviceResetArgsQueue.push_back(*args);
- }
-
- virtual void notifyKey(const NotifyKeyArgs* args) {
- mNotifyKeyArgsQueue.push_back(*args);
- }
-
- virtual void notifyMotion(const NotifyMotionArgs* args) {
- mNotifyMotionArgsQueue.push_back(*args);
- }
-
- virtual void notifySwitch(const NotifySwitchArgs* args) {
- mNotifySwitchArgsQueue.push_back(*args);
- }
-};
-
-
// --- FakeEventHub ---
class FakeEventHub : public EventHubInterface {
@@ -948,6 +841,7 @@
bool mResetWasCalled;
bool mProcessWasCalled;
+ std::optional<DisplayViewport> mViewport;
public:
FakeInputMapper(InputDevice* device, uint32_t sources) :
InputMapper(device),
@@ -1016,8 +910,14 @@
}
}
- virtual void configure(nsecs_t, const InputReaderConfiguration*, uint32_t) {
+ virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
mConfigureWasCalled = true;
+
+ // Find the associated viewport if exist.
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mViewport = config->getDisplayViewportByPort(*displayPort);
+ }
}
virtual void reset(nsecs_t) {
@@ -1064,6 +964,13 @@
virtual void fadePointer() {
}
+
+ virtual std::optional<int32_t> getAssociatedDisplay() {
+ if (mViewport) {
+ return std::make_optional(mViewport->displayId);
+ }
+ return std::nullopt;
+ }
};
@@ -1091,9 +998,10 @@
}
InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
- uint32_t classes) {
+ uint32_t classes, const std::string& location = "") {
InputDeviceIdentifier identifier;
identifier.name = name;
+ identifier.location = location;
int32_t generation = deviceId + 1;
return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
classes);
@@ -1303,7 +1211,7 @@
class InputReaderTest : public testing::Test {
protected:
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<FakeEventHub> mFakeEventHub;
sp<InstrumentedInputReader> mReader;
@@ -1311,7 +1219,7 @@
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
}
@@ -1393,7 +1301,7 @@
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = 1;
constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
- InputDevice* device = mReader->newDevice(deviceId, 0, "fake", deviceClass);
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
// Must add at least one mapper or the device will be ignored!
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
device->addMapper(mapper);
@@ -1577,7 +1485,7 @@
TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
constexpr int32_t deviceId = 1;
constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
- InputDevice* device = mReader->newDevice(deviceId, 0, "fake", deviceClass);
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
// Must add at least one mapper or the device will be ignored!
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
device->addMapper(mapper);
@@ -1607,6 +1515,36 @@
prevSequenceNum = resetArgs.sequenceNum;
}
+TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
+ constexpr int32_t deviceId = 1;
+ constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ const char* DEVICE_LOCATION = "USB1";
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
+ DEVICE_LOCATION);
+ FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
+ device->addMapper(mapper);
+ mReader->setNextDevice(device);
+ addDevice(deviceId, "fake", deviceClass, nullptr);
+
+ const uint8_t hdmi1 = 1;
+
+ // Associated touch screen with second display.
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+
+ // Add default and second display.
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ mReader->loopOnce();
+
+ // Check device.
+ ASSERT_EQ(deviceId, device->getId());
+ ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
+ ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+}
+
// --- InputDeviceTest ---
@@ -1620,7 +1558,7 @@
sp<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
@@ -1628,7 +1566,7 @@
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
@@ -1815,14 +1753,14 @@
sp<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
new file mode 100644
index 0000000..3ee33f1
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+TestInputListener::TestInputListener() { }
+
+TestInputListener::~TestInputListener() { }
+
+void TestInputListener::assertNotifyConfigurationChangedWasCalled(
+ NotifyConfigurationChangedArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
+ << "Expected notifyConfigurationChanged() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
+ }
+ mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
+ ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
+ << "Expected notifyConfigurationChanged() to not have been called.";
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+ NotifyDeviceResetArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
+ << "Expected notifyDeviceReset() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
+ }
+ mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
+ ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
+ << "Expected notifyDeviceReset() to not have been called.";
+}
+
+void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyKeyArgsQueue.begin();
+ }
+ mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyKeyWasNotCalled() {
+ ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+}
+
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyMotionArgsQueue.begin();
+ }
+ mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyMotionWasNotCalled() {
+ ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
+ << "Expected notifyMotion() to not have been called.";
+}
+
+void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
+ << "Expected notifySwitch() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifySwitchArgsQueue.begin();
+ }
+ mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+}
+
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+ mNotifyConfigurationChangedArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ mNotifyDeviceResetArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+ mNotifyKeyArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+ mNotifyMotionArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+ mNotifySwitchArgsQueue.push_back(*args);
+ }
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
new file mode 100644
index 0000000..085d343
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_TEST_INPUT_LISTENER_H
+#define _UI_TEST_INPUT_LISTENER_H
+
+#include <gtest/gtest.h>
+#include "InputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+class TestInputListener : public InputListenerInterface {
+private:
+ std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
+ std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
+ std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
+ std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
+ std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
+
+protected:
+ virtual ~TestInputListener();
+
+public:
+ TestInputListener();
+
+ void assertNotifyConfigurationChangedWasCalled(
+ NotifyConfigurationChangedArgs* outEventArgs = nullptr);
+
+ void assertNotifyConfigurationChangedWasNotCalled();
+
+ void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
+
+ void assertNotifyDeviceResetWasNotCalled();
+
+ void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+
+ void assertNotifyKeyWasNotCalled();
+
+ void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+
+ void assertNotifyMotionWasNotCalled();
+
+ void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+
+private:
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+ virtual void notifyKey(const NotifyKeyArgs* args);
+
+ virtual void notifyMotion(const NotifyMotionArgs* args);
+
+ virtual void notifySwitch(const NotifySwitchArgs* args);
+};
+
+} // namespace android
+#endif
diff --git a/services/nativeperms/Android.bp b/services/nativeperms/Android.bp
new file mode 100644
index 0000000..cbc7d66
--- /dev/null
+++ b/services/nativeperms/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "nativeperms",
+ srcs: [
+ "nativeperms.cpp",
+ "android/os/IPermissionController.aidl",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbrillo",
+ "libbrillo-binder",
+ "libchrome",
+ "libutils",
+ ],
+ init_rc: ["nativeperms.rc"],
+}
diff --git a/services/nativeperms/Android.mk b/services/nativeperms/Android.mk
deleted file mode 100644
index 34ccd0b..0000000
--- a/services/nativeperms/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := nativeperms
-LOCAL_SRC_FILES := \
- nativeperms.cpp \
- android/os/IPermissionController.aidl
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libbrillo \
- libbrillo-binder \
- libchrome \
- libutils
-LOCAL_INIT_RC := nativeperms.rc
-include $(BUILD_EXECUTABLE)
diff --git a/services/sensorservice/tests/Android.bp b/services/sensorservice/tests/Android.bp
new file mode 100644
index 0000000..d33c0ca
--- /dev/null
+++ b/services/sensorservice/tests/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+ name: "test-sensorservice",
+ srcs: ["sensorservicetest.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libutils",
+ "libsensor",
+ "libandroid",
+ ],
+}
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
deleted file mode 100644
index 8a9c36b..0000000
--- a/services/sensorservice/tests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- sensorservicetest.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libutils libsensor libandroid
-
-LOCAL_MODULE:= test-sensorservice
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 01c60d8..734ed6c 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -58,6 +58,7 @@
"libinput",
"libutils",
"libutilscallstack",
+ "libSurfaceFlingerProperties",
],
static_libs: [
"libcompositionengine",
@@ -127,7 +128,6 @@
"DisplayHardware/FramebufferSurface.cpp",
"DisplayHardware/HWC2.cpp",
"DisplayHardware/HWComposer.cpp",
- "DisplayHardware/HWComposerBufferCache.cpp",
"DisplayHardware/PowerAdvisor.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
"Effects/Daltonizer.cpp",
@@ -151,6 +151,7 @@
"Scheduler/MessageQueue.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
+ "Scheduler/PhaseOffsets.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
@@ -221,25 +222,10 @@
defaults: ["libsurfaceflinger_binary"],
init_rc: ["surfaceflinger.rc"],
srcs: [":surfaceflinger_binary_sources"],
- shared_libs: ["libsurfaceflinger"],
-}
-
-cc_library_shared {
- name: "libsurfaceflinger_ddmconnection",
- defaults: ["surfaceflinger_defaults"],
- srcs: ["DdmConnection.cpp"],
shared_libs: [
- "libcutils",
- "libdl",
- "liblog",
- "libprocessgroup",
+ "libsurfaceflinger",
+ "libSurfaceFlingerProperties",
],
- product_variables: {
- // uses jni which may not be available in PDK
- pdk: {
- enabled: false,
- },
- },
}
subdirs = [
@@ -247,3 +233,26 @@
"TimeStats/timestatsproto",
"tests",
]
+
+cc_library_shared {
+ name: "libSurfaceFlingerProperties",
+ srcs: [
+ "SurfaceFlingerProperties.cpp",
+ "sysprop/*.sysprop",
+ ],
+ shared_libs: [
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "android.hardware.configstore@1.2",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ ],
+}
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 164a3a6..89eee6b 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -19,6 +19,28 @@
#define LOG_TAG "BufferLayer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <cmath>
+#include <cstdlib>
+#include <mutex>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <cutils/compiler.h>
+#include <cutils/native_handle.h>
+#include <cutils/properties.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/NativeHandle.h>
+#include <utils/StopWatch.h>
+#include <utils/Trace.h>
+
#include "BufferLayer.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
@@ -26,37 +48,15 @@
#include "TimeStats/TimeStats.h"
-#include <renderengine/RenderEngine.h>
-
-#include <gui/BufferItem.h>
-#include <gui/BufferQueue.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-
-#include <ui/DebugUtils.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/NativeHandle.h>
-#include <utils/StopWatch.h>
-#include <utils/Trace.h>
-
-#include <cutils/compiler.h>
-#include <cutils/native_handle.h>
-#include <cutils/properties.h>
-
-#include <math.h>
-#include <stdlib.h>
-#include <mutex>
-
namespace android {
BufferLayer::BufferLayer(const LayerCreationArgs& args)
- : Layer(args), mTextureName(args.flinger->getNewTexture()) {
+ : Layer(args),
+ mTextureName(args.flinger->getNewTexture()),
+ mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
+ compositionengine::LayerCreationArgs{this})} {
ALOGV("Creating Layer %s", args.name.string());
- mTexture.init(renderengine::Texture::TEXTURE_EXTERNAL, mTextureName);
-
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -127,13 +127,11 @@
return inverse(tr);
}
-/*
- * onDraw will draw the current layer onto the presentable buffer
- */
-void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) {
+bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer) {
ATRACE_CALL();
-
+ Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
if (CC_UNLIKELY(mActiveBuffer == 0)) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
@@ -151,30 +149,27 @@
finished = true;
return;
}
- under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
+ under.orSelf(layer->visibleRegion);
});
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
- clearWithOpenGL(renderArea, 0, 0, 0, 1);
+ clearRegion.orSelf(holes);
}
- return;
+ return false;
}
-
- // Bind the current buffer to the GL texture, and wait for it to be
- // ready for us to draw into.
- status_t err = bindTextureImage();
- if (err != NO_ERROR) {
- ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
- // Go ahead and draw the buffer anyway; no matter what we do the screen
- // is probably going to have something visibly wrong.
- }
-
bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
-
- auto& engine(mFlinger->getRenderEngine());
-
+ const State& s(getDrawingState());
if (!blackOutLayer) {
+ layer.source.buffer.buffer = mActiveBuffer;
+ layer.source.buffer.isOpaque = isOpaque(s);
+ layer.source.buffer.fence = mActiveBufferFence;
+ layer.source.buffer.cacheHint = useCachedBufferForClientComposition()
+ ? renderengine::Buffer::CachingHint::USE_CACHE
+ : renderengine::Buffer::CachingHint::NO_CACHE;
+ layer.source.buffer.textureName = mTextureName;
+ layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+ layer.source.buffer.isY410BT2020 = isHdrY410();
// TODO: we could be more subtle with isFixedSize()
const bool useFiltering = needsFiltering() || renderArea.needsFiltering() || isFixedSize();
@@ -211,17 +206,31 @@
memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
}
- // Set things up for texturing.
- mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
- mTexture.setFiltering(useFiltering);
- mTexture.setMatrix(textureMatrix);
+ const Rect win{getBounds()};
+ const float bufferWidth = getBufferSize(s).getWidth();
+ const float bufferHeight = getBufferSize(s).getHeight();
- engine.setupLayerTexturing(mTexture);
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+ mat4::translate(vec4(-.5, -.5, 0, 1)) *
+ mat4::translate(vec4(translateX, translateY, 0, 1)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
+
+ layer.source.buffer.useTextureFiltering = useFiltering;
+ layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
} else {
- engine.setupLayerBlackedOut();
+ // If layer is blacked out, force alpha to 1 so that we draw a black color
+ // layer.
+ layer.source.buffer.buffer = nullptr;
+ layer.alpha = 1.0;
}
- drawWithOpenGL(renderArea, useIdentityTransform);
- engine.disableTexturing();
+
+ return true;
}
bool BufferLayer::isHdrY410() const {
@@ -363,20 +372,18 @@
return true;
}
-Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- const sp<Fence>& releaseFence) {
+bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ const sp<Fence>& releaseFence) {
ATRACE_CALL();
- std::optional<Region> sidebandStreamDirtyRegion = latchSidebandStream(recomputeVisibleRegions);
+ bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
- if (sidebandStreamDirtyRegion) {
- return *sidebandStreamDirtyRegion;
+ if (refreshRequired) {
+ return refreshRequired;
}
- Region dirtyRegion;
-
if (!hasReadyFrame()) {
- return dirtyRegion;
+ return false;
}
// if we've already called updateTexImage() without going through
@@ -385,14 +392,14 @@
// compositionComplete() call.
// we'll trigger an update in onPreComposition().
if (mRefreshPending) {
- return dirtyRegion;
+ return false;
}
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
if (!fenceHasSignaled()) {
mFlinger->signalLayerUpdate();
- return dirtyRegion;
+ return false;
}
// Capture the old state of the layer for comparisons later
@@ -402,24 +409,24 @@
if (!allTransactionsSignaled()) {
mFlinger->signalLayerUpdate();
- return dirtyRegion;
+ return false;
}
status_t err = updateTexImage(recomputeVisibleRegions, latchTime, releaseFence);
if (err != NO_ERROR) {
- return dirtyRegion;
+ return false;
}
err = updateActiveBuffer();
if (err != NO_ERROR) {
- return dirtyRegion;
+ return false;
}
mBufferLatched = true;
err = updateFrameNumber(latchTime);
if (err != NO_ERROR) {
- return dirtyRegion;
+ return false;
}
mRefreshPending = true;
@@ -459,11 +466,14 @@
Rect crop(getDrawingCrop());
const uint32_t transform(getDrawingTransform());
const uint32_t scalingMode(getDrawingScalingMode());
+ const bool transformToDisplayInverse(getTransformToDisplayInverse());
if ((crop != mCurrentCrop) || (transform != mCurrentTransform) ||
- (scalingMode != mCurrentScalingMode)) {
+ (scalingMode != mCurrentScalingMode) ||
+ (transformToDisplayInverse != mTransformToDisplayInverse)) {
mCurrentCrop = crop;
mCurrentTransform = transform;
mCurrentScalingMode = scalingMode;
+ mTransformToDisplayInverse = transformToDisplayInverse;
recomputeVisibleRegions = true;
}
@@ -500,9 +510,7 @@
}
}
- // FIXME: postedRegion should be dirty & bounds
- // transform the dirty region to window-manager space
- return getTransform().transform(Region(getBufferSize(s)));
+ return true;
}
// transaction
@@ -604,70 +612,6 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
-void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
- ATRACE_CALL();
- const State& s(getDrawingState());
-
- computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
-
- /*
- * NOTE: the way we compute the texture coordinates here produces
- * different results than when we take the HWC path -- in the later case
- * the "source crop" is rounded to texel boundaries.
- * This can produce significantly different results when the texture
- * is scaled by a large amount.
- *
- * The GL code below is more logical (imho), and the difference with
- * HWC is due to a limitation of the HWC API to integers -- a question
- * is suspend is whether we should ignore this problem or revert to
- * GL composition when a buffer scaling is applied (maybe with some
- * minimal value)? Or, we could make GL behave like HWC -- but this feel
- * like more of a hack.
- */
-
- // Convert to Rect so that bounds are clipped to integers.
- const Rect win{computeCrop(Rect::INVALID_RECT)};
- // computeCrop() returns the cropping rectangle in buffer space, so we
- // shouldn't use getBufferSize() since that may return a rectangle specified
- // in layer space. Otherwise we may compute incorrect texture coordinates.
- const float bufWidth = float(mActiveBuffer->getWidth());
- const float bufHeight = float(mActiveBuffer->getHeight());
-
- const float left = win.left / bufWidth;
- const float top = win.top / bufHeight;
- const float right = win.right / bufWidth;
- const float bottom = win.bottom / bufHeight;
-
- // TODO: we probably want to generate the texture coords with the mesh
- // here we assume that we only have 4 vertices
- renderengine::Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>());
- // flip texcoords vertically because BufferLayerConsumer expects them to be in GL convention
- texCoords[0] = vec2(left, 1.0f - top);
- texCoords[1] = vec2(left, 1.0f - bottom);
- texCoords[2] = vec2(right, 1.0f - bottom);
- texCoords[3] = vec2(right, 1.0f - top);
-
- const auto roundedCornerState = getRoundedCornerState();
- const auto cropRect = roundedCornerState.cropRect;
- setupRoundedCornersCropCoordinates(win, cropRect);
-
- auto& engine(mFlinger->getRenderEngine());
- engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
- getColor(), roundedCornerState.radius);
- engine.setSourceDataSpace(mCurrentDataSpace);
-
- if (isHdrY410()) {
- engine.setSourceY410BT2020(true);
- }
-
- engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
-
- engine.drawMesh(getBE().mMesh);
- engine.disableBlending();
-
- engine.setSourceY410BT2020(false);
-}
-
uint64_t BufferLayer::getHeadFrameNumber() const {
if (hasFrameUpdate()) {
return getFrameNumber();
@@ -706,6 +650,42 @@
return Rect(bufWidth, bufHeight);
}
+std::shared_ptr<compositionengine::Layer> BufferLayer::getCompositionLayer() const {
+ return mCompositionLayer;
+}
+
+FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+
+ // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+ // we cannot determine the buffer size.
+ if ((s.sidebandStream != nullptr) ||
+ (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ if (mActiveBuffer == nullptr) {
+ return parentBounds;
+ }
+
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mCurrentTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return FloatRect(0, 0, bufWidth, bufHeight);
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index e3b7f2f..0f8a350 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -16,34 +16,32 @@
#pragma once
-#include "BufferLayerConsumer.h"
-#include "Client.h"
-#include "Layer.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWComposerBufferCache.h"
-#include "FrameTracker.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "SurfaceFlinger.h"
+#include <sys/types.h>
+#include <cstdint>
+#include <list>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
+#include <renderengine/Image.h>
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
+#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
-
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Timers.h>
-#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <list>
+#include "BufferLayerConsumer.h"
+#include "Client.h"
+#include "DisplayHardware/HWComposer.h"
+#include "FrameTracker.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "SurfaceFlinger.h"
namespace android {
@@ -56,6 +54,8 @@
// Overriden from Layer
// -----------------------------------------------------------------------
public:
+ std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
@@ -78,10 +78,6 @@
// isFixedSize - true if content has a fixed size
bool isFixedSize() const override;
- // onDraw - draws the surface.
- void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) override;
-
bool isHdrY410() const override;
void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -100,8 +96,8 @@
// If there was a GL composition step rendering the previous frame, then
// releaseFence will be populated with a native fence that fires when
// composition has completed.
- Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- const sp<Fence>& releaseFence) override;
+ bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ const sp<Fence>& releaseFence) override;
bool isBufferLatched() const override { return mRefreshPending; }
@@ -138,7 +134,8 @@
virtual bool getAutoRefresh() const = 0;
virtual bool getSidebandStreamChanged() const = 0;
- virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+ // Latch sideband stream and returns true if the dirty region should be updated.
+ virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
virtual bool hasFrameUpdate() const = 0;
@@ -169,24 +166,33 @@
bool mRefreshPending{false};
+ // Returns true if, when drawing the active buffer during gpu compositon, we
+ // should use a cached buffer or not.
+ virtual bool useCachedBufferForClientComposition() const = 0;
+
+ // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer);
+
private:
// Returns true if this layer requires filtering
bool needsFiltering() const;
- // drawing
- void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const;
-
uint64_t getHeadFrameNumber() const;
uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+ bool mTransformToDisplayInverse{false};
+
// main thread.
bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
- // The texture used to draw the layer in GLES composition mode
- mutable renderengine::Texture mTexture;
-
Rect getBufferSize(const State& s) const override;
+
+ std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6826050..7ed8184 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -272,6 +272,7 @@
// Update the BufferLayerConsumer state.
mCurrentTexture = slot;
mCurrentTextureBuffer = nextTextureBuffer;
+ mCurrentTextureBufferStaleForGpu = false;
mCurrentTextureImageFreed = nullptr;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
@@ -294,48 +295,7 @@
status_t BufferLayerConsumer::bindTextureImageLocked() {
ATRACE_CALL();
- mRE.checkErrors();
-
- // It is possible for the current slot's buffer to be freed before a new one
- // is bound. In that scenario we still want to bind the image.
- if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) {
- BLC_LOGE("bindTextureImage: no currently-bound texture");
- mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
- return NO_INIT;
- }
-
- renderengine::Image* imageToRender;
-
- // mCurrentTextureImageFreed is non-null iff mCurrentTexture ==
- // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check.
- if (mCurrentTextureImageFreed) {
- imageToRender = mCurrentTextureImageFreed.get();
- } else if (mImages[mCurrentTexture]) {
- imageToRender = mImages[mCurrentTexture].get();
- } else {
- std::unique_ptr<renderengine::Image> image = mRE.createImage();
- bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(),
- mCurrentTextureBuffer->getUsage() &
- GRALLOC_USAGE_PROTECTED);
- if (!success) {
- BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64
- " fmt=%d",
- mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(),
- mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(),
- mCurrentTextureBuffer->getPixelFormat());
- BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
- mRE.bindExternalTextureImage(mTexName, *image);
- return UNKNOWN_ERROR;
- }
- imageToRender = image.get();
- // Cache the image here so that we can reuse it.
- mImages[mCurrentTexture] = std::move(image);
- }
-
- mRE.bindExternalTextureImage(mTexName, *imageToRender);
-
- // Wait for the new buffer to be ready.
- return doFenceWaitLocked();
+ return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false);
}
status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) {
@@ -433,16 +393,29 @@
return mCurrentApi;
}
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
Mutex::Autolock lock(mMutex);
if (outSlot != nullptr) {
*outSlot = mCurrentTexture;
}
+ if (outFence != nullptr) {
+ *outFence = mCurrentFence;
+ }
+
return mCurrentTextureBuffer;
}
+bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() {
+ Mutex::Autolock lock(mMutex);
+ bool useCache = mCurrentTextureBufferStaleForGpu;
+ // Set the staleness bit here, as this function is only called during a
+ // client composition path.
+ mCurrentTextureBufferStaleForGpu = true;
+ return useCache;
+}
+
Rect BufferLayerConsumer::getCurrentCrop() const {
Mutex::Autolock lock(mMutex);
return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index ea46245..e2ef399 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -150,10 +150,16 @@
// for use with bilinear filtering.
void setFilteringEnabled(bool enabled);
+ // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the
+ // buffer is now "stale" for GPU composition, and returns the old staleness
+ // bit as a caching hint.
+ bool getAndSetCurrentBufferCacheHint();
+
// getCurrentBuffer returns the buffer associated with the current image.
// When outSlot is not nullptr, the current buffer slot index is also
- // returned.
- sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
+ // returned. Simiarly, when outFence is not nullptr, the current output
+ // fence is returned.
+ sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
// getCurrentCrop returns the cropping rectangle of the current buffer.
Rect getCurrentCrop() const;
@@ -255,6 +261,10 @@
// must track it separately in order to support the getCurrentBuffer method.
sp<GraphicBuffer> mCurrentTextureBuffer;
+ // True if the buffer was used for the previous client composition frame,
+ // and false otherwise.
+ bool mCurrentTextureBufferStaleForGpu;
+
// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set each time updateTexImage is called.
Rect mCurrentCrop;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 42021d1..0313c32 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -190,7 +190,7 @@
return mSidebandStreamChanged;
}
-std::optional<Region> BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
bool sidebandStreamChanged = true;
if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
// mSidebandStreamChanged was changed to false
@@ -202,10 +202,9 @@
}
recomputeVisibleRegions = true;
- const State& s(getDrawingState());
- return getTransform().transform(Region(Rect(s.active_legacy.w, s.active_legacy.h)));
+ return true;
}
- return {};
+ return false;
}
bool BufferQueueLayer::hasFrameUpdate() const {
@@ -233,11 +232,30 @@
getTransformToDisplayInverse(), mFreezeGeometryUpdates);
const nsecs_t expectedPresentTime = mFlinger->mUseScheduler
- ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime()
+ ? mFlinger->mScheduler->expectedPresentTime()
: mFlinger->mPrimaryDispSync->expectedPresentTime();
+
+ // updateTexImage() below might drop the some buffers at the head of the queue if there is a
+ // buffer behind them which is timely to be presented. However this buffer may not be signaled
+ // yet. The code below makes sure that this wouldn't happen by setting maxFrameNumber to the
+ // last buffer that was signaled.
+ uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
+ {
+ Mutex::Autolock lock(mQueueItemLock);
+ for (int i = 0; i < mQueueItems.size(); i++) {
+ bool fenceSignaled =
+ mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ if (!fenceSignaled) {
+ break;
+ }
+ lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+ }
+ }
+ const uint64_t maxFrameNumberToAcquire =
+ std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber);
status_t updateResult =
mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, &queuedBuffer,
- mLastFrameNumberReceived, releaseFence);
+ maxFrameNumberToAcquire, releaseFence);
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
// layer update so we check again at the next opportunity.
@@ -306,7 +324,7 @@
status_t BufferQueueLayer::updateActiveBuffer() {
// update the active buffer
- mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot);
+ mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
getBE().compositionInfo.mBuffer = mActiveBuffer;
getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
@@ -317,6 +335,10 @@
return NO_ERROR;
}
+bool BufferQueueLayer::useCachedBufferForClientComposition() const {
+ return mConsumer->getAndSetCurrentBufferCacheHint();
+}
+
status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
mPreviousFrameNumber = mCurrentFrameNumber;
mCurrentFrameNumber = mConsumer->getFrameNumber();
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index d7c3f6a..d392a69 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -62,6 +62,9 @@
public:
bool fenceHasSignaled() const override;
+protected:
+ bool useCachedBufferForClientComposition() const override;
+
private:
nsecs_t getDesiredPresentTime() override;
std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
@@ -81,7 +84,7 @@
bool getAutoRefresh() const override;
bool getSidebandStreamChanged() const override;
- std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
bool hasFrameUpdate() const override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2132f59..2d6da76 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BufferStateLayer.h"
+#include "ColorLayer.h"
#include "TimeStats/TimeStats.h"
@@ -41,6 +42,7 @@
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
}
BufferStateLayer::~BufferStateLayer() = default;
@@ -95,7 +97,8 @@
bool BufferStateLayer::willPresentCurrentTransaction() const {
// Returns true if the most recent Transaction applied to CurrentState will be presented.
return getSidebandStreamChanged() || getAutoRefresh() ||
- (mCurrentState.modified && mCurrentState.buffer != nullptr);
+ (mCurrentState.modified &&
+ (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr));
}
bool BufferStateLayer::getTransformToDisplayInverse() const {
@@ -311,7 +314,7 @@
// if the display frame is not defined, use the parent bounds as the buffer size.
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- Rect parentBounds = Rect(p->computeBounds(Region()));
+ Rect parentBounds = Rect(p->getBounds(Region()));
if (!parentBounds.isEmpty()) {
return parentBounds;
}
@@ -323,6 +326,18 @@
}
return Rect::INVALID_RECT;
}
+
+FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ // if the display frame is not defined, use the parent bounds as the buffer size.
+ return parentBounds;
+}
+
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -419,7 +434,7 @@
return mSidebandStreamChanged.load();
}
-std::optional<Region> BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
if (mSidebandStreamChanged.exchange(false)) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
@@ -431,13 +446,14 @@
}
recomputeVisibleRegions = true;
- return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
+ return true;
}
- return {};
+ return false;
}
bool BufferStateLayer::hasFrameUpdate() const {
- return mCurrentStateModified && getCurrentState().buffer != nullptr;
+ const State& c(getCurrentState());
+ return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
void BufferStateLayer::setFilteringEnabled(bool enabled) {
@@ -449,46 +465,7 @@
const State& s(getDrawingState());
auto& engine(mFlinger->getRenderEngine());
- engine.checkErrors();
-
- // TODO(marissaw): once buffers are cached, don't create a new image everytime
- mTextureImage = engine.createImage();
-
- bool created =
- mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(),
- s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
- if (!created) {
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(),
- s.buffer->getUsage(), s.buffer->getPixelFormat());
- engine.bindExternalTextureImage(mTextureName, *engine.createImage());
- return NO_INIT;
- }
-
- engine.bindExternalTextureImage(mTextureName, *mTextureImage);
-
- // Wait for the new buffer to be ready.
- if (s.acquireFence->isValid()) {
- if (SyncFeatures::getInstance().useWaitSync()) {
- base::unique_fd fenceFd(s.acquireFence->dup());
- if (fenceFd == -1) {
- ALOGE("error dup'ing fence fd: %d", errno);
- return -errno;
- }
- if (!engine.waitFence(std::move(fenceFd))) {
- ALOGE("failed to wait on fence fd");
- return UNKNOWN_ERROR;
- }
- } else {
- status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage");
- if (err != NO_ERROR) {
- ALOGE("error waiting for fence: %d", err);
- return err;
- }
- }
- }
-
- return NO_ERROR;
+ return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false);
}
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
@@ -496,6 +473,11 @@
const State& s(getDrawingState());
if (!s.buffer) {
+ if (s.bgColorLayer) {
+ for (auto& handle : mDrawingState.callbackHandles) {
+ handle->latchTime = latchTime;
+ }
+ }
return NO_ERROR;
}
@@ -611,12 +593,18 @@
}
mActiveBuffer = s.buffer;
+ mActiveBufferFence = s.acquireFence;
getBE().compositionInfo.mBuffer = mActiveBuffer;
getBE().compositionInfo.mBufferSlot = 0;
return NO_ERROR;
}
+bool BufferStateLayer::useCachedBufferForClientComposition() const {
+ // TODO: Store a proper staleness bit to support EGLImage caching.
+ return false;
+}
+
status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
// TODO(marissaw): support frame history events
mCurrentFrameNumber = mFrameNumber;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 3f891d3..3320c5b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -88,6 +88,7 @@
uint64_t /*frameNumber*/) override {}
Rect getBufferSize(const State& s) const override;
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -95,6 +96,9 @@
// -----------------------------------------------------------------------
bool fenceHasSignaled() const override;
+protected:
+ bool useCachedBufferForClientComposition() const override;
+
private:
nsecs_t getDesiredPresentTime() override;
std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
@@ -114,7 +118,7 @@
bool getAutoRefresh() const override;
bool getSidebandStreamChanged() const override;
- std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
bool hasFrameUpdate() const override;
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
index c82ad7b..cb02d16 100644
--- a/services/surfaceflinger/BufferStateLayerCache.cpp
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -19,81 +19,84 @@
#define LOG_TAG "BufferStateLayerCache"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <cinttypes>
+
#include "BufferStateLayerCache.h"
-#define MAX_CACHE_SIZE 64
+#define VALID_CACHE_ID(id) ((id) >= 0 || (id) < (BUFFER_CACHE_MAX_SIZE))
namespace android {
-int32_t BufferStateLayerCache::add(const sp<IBinder>& processToken,
- const sp<GraphicBuffer>& buffer) {
- std::lock_guard lock(mMutex);
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferStateLayerCache);
- auto& processCache = getProccessCache(processToken);
+BufferStateLayerCache::BufferStateLayerCache() : mDeathRecipient(new CacheDeathRecipient) {}
- int32_t slot = findSlot(processCache);
- if (slot < 0) {
- return slot;
+void BufferStateLayerCache::add(sp<IBinder> processToken, int32_t id,
+ const sp<GraphicBuffer>& buffer) {
+ if (!VALID_CACHE_ID(id)) {
+ ALOGE("failed to cache buffer: invalid buffer id");
+ return;
}
- processCache[slot] = buffer;
+ if (!processToken) {
+ ALOGE("failed to cache buffer: invalid process token");
+ return;
+ }
- return slot;
-}
-
-void BufferStateLayerCache::release(const sp<IBinder>& processToken, int32_t id) {
- if (id < 0) {
- ALOGE("invalid buffer id");
+ if (!buffer) {
+ ALOGE("failed to cache buffer: invalid buffer");
return;
}
std::lock_guard lock(mMutex);
- auto& processCache = getProccessCache(processToken);
- if (id >= processCache.size()) {
- ALOGE("invalid buffer id");
- return;
- }
- processCache[id] = nullptr;
-}
-
-sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, int32_t id) {
- if (id < 0) {
- ALOGE("invalid buffer id");
- return nullptr;
- }
-
- std::lock_guard lock(mMutex);
- auto& processCache = getProccessCache(processToken);
-
- if (id >= processCache.size()) {
- ALOGE("invalid buffer id");
- return nullptr;
- }
- return processCache[id];
-}
-
-std::vector<sp<GraphicBuffer>>& BufferStateLayerCache::getProccessCache(
- const sp<IBinder>& processToken) {
- return mBuffers[processToken];
-}
-
-int32_t BufferStateLayerCache::findSlot(std::vector<sp<GraphicBuffer>>& processCache) {
- int32_t slot = 0;
-
- for (const sp<GraphicBuffer> buffer : processCache) {
- if (!buffer) {
- return slot;
+ // If this is a new process token, set a death recipient. If the client process dies, we will
+ // get a callback through binderDied.
+ if (mBuffers.find(processToken) == mBuffers.end()) {
+ status_t err = processToken->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("failed to cache buffer: could not link to death");
+ return;
}
- slot++;
}
- if (processCache.size() < MAX_CACHE_SIZE) {
- processCache.push_back(nullptr);
- return slot;
+ auto& processBuffers = mBuffers[processToken];
+ processBuffers[id] = buffer;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(sp<IBinder> processToken, int32_t id) {
+ if (!VALID_CACHE_ID(id)) {
+ ALOGE("failed to get buffer: invalid buffer id");
+ return nullptr;
}
- return -1;
+ if (!processToken) {
+ ALOGE("failed to cache buffer: invalid process token");
+ return nullptr;
+ }
+
+ std::lock_guard lock(mMutex);
+ auto itr = mBuffers.find(processToken);
+ if (itr == mBuffers.end()) {
+ ALOGE("failed to get buffer: process token not found");
+ return nullptr;
+ }
+
+ if (id >= itr->second.size()) {
+ ALOGE("failed to get buffer: id outside the bounds of the cache");
+ return nullptr;
+ }
+
+ return itr->second[id];
+}
+
+void BufferStateLayerCache::removeProcess(const wp<IBinder>& processToken) {
+ std::lock_guard lock(mMutex);
+ mBuffers.erase(processToken);
+}
+
+void BufferStateLayerCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
+ BufferStateLayerCache::getInstance().removeProcess(who);
}
}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
index 623f0c6..ede3feb 100644
--- a/services/surfaceflinger/BufferStateLayerCache.h
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -20,36 +20,36 @@
#include <binder/IBinder.h>
#include <ui/GraphicBuffer.h>
#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <array>
+#include <map>
#include <mutex>
-#include <unordered_map>
-#include <vector>
+
+#define BUFFER_CACHE_MAX_SIZE 64
namespace android {
-class BufferStateLayerCache {
+class BufferStateLayerCache : public Singleton<BufferStateLayerCache> {
public:
- int32_t add(const sp<IBinder>& processToken, const sp<GraphicBuffer>& buffer);
- void release(const sp<IBinder>& processToken, int32_t id);
+ BufferStateLayerCache();
- sp<GraphicBuffer> get(const sp<IBinder>& processToken, int32_t id);
+ void add(sp<IBinder> processToken, int32_t id, const sp<GraphicBuffer>& buffer);
+ sp<GraphicBuffer> get(sp<IBinder> processToken, int32_t id);
+
+ void removeProcess(const wp<IBinder>& processToken);
private:
std::mutex mMutex;
+ std::map<wp<IBinder> /*caching process*/, std::array<sp<GraphicBuffer>, BUFFER_CACHE_MAX_SIZE>>
+ mBuffers GUARDED_BY(mMutex);
- std::vector<sp<GraphicBuffer>>& getProccessCache(const sp<IBinder>& processToken)
- REQUIRES(mMutex);
-
- int32_t findSlot(std::vector<sp<GraphicBuffer>>& proccessCache) REQUIRES(mMutex);
-
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& strongPointer) const {
- return std::hash<IBinder*>{}(strongPointer.get());
- }
+ class CacheDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ void binderDied(const wp<IBinder>& who) override;
};
- std::unordered_map<sp<IBinder> /*caching process*/, std::vector<sp<GraphicBuffer>>, IBinderHash>
- mBuffers GUARDED_BY(mMutex);
+ sp<CacheDeathRecipient> mDeathRecipient;
};
}; // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0e447d8..0ca3759 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -73,14 +73,10 @@
return lbc;
}
-
-status_t Client::createSurface(
- const String8& name,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp)
-{
+status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parentHandle,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) {
sp<Layer> parent = nullptr;
if (parentHandle != nullptr) {
auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
@@ -91,14 +87,14 @@
}
// We rely on createLayer to check permissions.
- return mFlinger->createLayer(name, this, w, h, format, flags, windowType,
- ownerUid, handle, gbp, &parent);
+ return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+ &parent);
}
status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
- int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
+ LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) {
if (mFlinger->authenticateSurfaceTexture(parent) == false) {
return BAD_VALUE;
@@ -111,8 +107,7 @@
sp<IBinder> parentHandle = layer->getHandle();
- return createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, handle,
- gbp);
+ return createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), handle, gbp);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 0f5ee4c..74e4818 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -51,18 +51,16 @@
private:
// ISurfaceComposerClient interface
- virtual status_t createSurface(
- const String8& name,
- uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
- const sp<IBinder>& parent, int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp);
+ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp);
virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
- int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp);
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp);
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index f27f6aa..d82ff0e 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -22,6 +22,9 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerCreationArgs.h>
#include <renderengine/RenderEngine.h>
#include <ui/GraphicBuffer.h>
#include <utils/Errors.h>
@@ -34,38 +37,42 @@
namespace android {
// ---------------------------------------------------------------------------
-ColorLayer::ColorLayer(const LayerCreationArgs& args) : Layer(args) {}
+ColorLayer::ColorLayer(const LayerCreationArgs& args)
+ : Layer(args),
+ mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
+ compositionengine::LayerCreationArgs{this})} {}
ColorLayer::~ColorLayer() = default;
-void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
- bool useIdentityTransform) {
- half4 color = getColor();
- if (color.a > 0) {
- renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2);
- computeGeometry(renderArea, mesh, useIdentityTransform);
- auto& engine(mFlinger->getRenderEngine());
-
- Rect win{computeBounds()};
-
- const auto roundedCornerState = getRoundedCornerState();
- const auto cropRect = roundedCornerState.cropRect;
- setupRoundedCornersCropCoordinates(win, cropRect);
-
- engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
- true /* disableTexture */, color, roundedCornerState.radius);
-
- engine.setSourceDataSpace(mCurrentDataSpace);
- engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
- engine.drawMesh(mesh);
- engine.disableBlending();
- }
+bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer) {
+ Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
+ half4 color(getColor());
+ half3 solidColor(color.r, color.g, color.b);
+ layer.source.solidColor = solidColor;
+ return true;
}
bool ColorLayer::isVisible() const {
return !isHiddenByPolicy() && getAlpha() > 0.0f;
}
+bool ColorLayer::setColor(const half3& color) {
+ if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
+ mCurrentState.color.b == color.b) {
+ return false;
+ }
+
+ mCurrentState.sequence++;
+ mCurrentState.color.r = color.r;
+ mCurrentState.color.g = color.g;
+ mCurrentState.color.b = color.b;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
void ColorLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform,
const Rect& viewport, int32_t /* supportedPerFrameMetadata */) {
RETURN_IF_NO_HWC_LAYER(displayId);
@@ -127,6 +134,10 @@
getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
}
+std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const {
+ return mCompositionLayer;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index aecde9f..ed4c2d8 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -28,18 +28,26 @@
explicit ColorLayer(const LayerCreationArgs&);
~ColorLayer() override;
+ std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+
virtual const char* getTypeId() const { return "ColorLayer"; }
- virtual void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform);
bool isVisible() const override;
+ bool setColor(const half3& color) override;
+
void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
int32_t supportedPerFrameMetadata) override;
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
protected:
- FloatRect computeCrop(const Rect& /*windowbounds*/) const override { return {}; }
+ FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
+ virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer);
+
+private:
+ std::shared_ptr<compositionengine::Layer> mCompositionLayer;
};
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 49fa84a..ce6b06c 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -41,8 +41,11 @@
"src/DisplayColorProfile.cpp",
"src/DisplaySurface.cpp",
"src/DumpHelpers.cpp",
+ "src/HwcBufferCache.cpp",
+ "src/Layer.cpp",
"src/Output.cpp",
"src/OutputCompositionState.cpp",
+ "src/OutputLayer.cpp",
"src/RenderSurface.cpp",
],
local_include_dirs: ["include"],
@@ -57,7 +60,10 @@
"mock/Display.cpp",
"mock/DisplayColorProfile.cpp",
"mock/DisplaySurface.cpp",
+ "mock/Layer.cpp",
+ "mock/LayerFE.cpp",
"mock/Output.cpp",
+ "mock/OutputLayer.cpp",
"mock/RenderSurface.cpp",
],
static_libs: [
@@ -77,8 +83,11 @@
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
+ "tests/HwcBufferCacheTest.cpp",
+ "tests/LayerTest.cpp",
"tests/MockHWComposer.cpp",
"tests/OutputTest.cpp",
+ "tests/OutputLayerTest.cpp",
"tests/RenderSurfaceTest.cpp",
],
static_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 30cdb49..896f8aa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -29,8 +29,10 @@
namespace compositionengine {
class Display;
+class Layer;
struct DisplayCreationArgs;
+struct LayerCreationArgs;
/**
* Encapsulates all the interfaces and implementation details for performing
@@ -42,6 +44,7 @@
// Create a composition Display
virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
+ virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0;
virtual HWComposer& getHwComposer() const = 0;
virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
new file mode 100644
index 0000000..29a7dea
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+typedef int64_t nsecs_t;
+
+namespace compositionengine {
+
+class Display;
+class LayerFE;
+
+/**
+ * A layer contains the output-independent composition state for a front-end
+ * Layer
+ */
+class Layer {
+public:
+ virtual ~Layer();
+
+ // Gets the front-end interface for this layer. Can return nullptr if the
+ // front-end layer no longer exists.
+ virtual sp<LayerFE> getLayerFE() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
new file mode 100644
index 0000000..db3312b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class LayerFE;
+
+/**
+ * A parameter object for creating Layer instances
+ */
+struct LayerCreationArgs {
+ // A weak pointer to the front-end layer instance that the new layer will
+ // represent.
+ wp<LayerFE> layerFE;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
new file mode 100644
index 0000000..f9a3624
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+
+namespace android::compositionengine {
+
+// Defines the interface used by the CompositionEngine to make requests
+// of the front-end layer
+class LayerFE : public virtual RefBase {};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2d00f0c..84b2423 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,11 +23,15 @@
#include <ui/GraphicTypes.h>
#include <ui/Region.h>
#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
namespace android::compositionengine {
class DisplayColorProfile;
+class Layer;
+class LayerFE;
class RenderSurface;
+class OutputLayer;
namespace impl {
struct OutputCompositionState;
@@ -38,6 +42,10 @@
*/
class Output {
public:
+ using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
+
+ virtual ~Output();
+
// Returns true if the output is valid. This is meant to be checked post-
// construction and prior to use, as not everything is set up by the
// constructor.
@@ -52,11 +60,9 @@
// Sets the bounds to use
virtual void setBounds(const ui::Size&) = 0;
- // Sets the layer stack filter for this output. If singleLayerStack is true,
- // this output displays just the single layer stack specified by
- // singleLayerStackId. Otherwise all layer stacks will be visible on this
- // output.
- virtual void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) = 0;
+ // Sets the layer stack filtering settings for this output. See
+ // belongsInOutput for full details.
+ virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
// Sets the color transform matrix to use
virtual void setColorTransform(const mat4&) = 0;
@@ -91,17 +97,36 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual OutputCompositionState& editState() = 0;
- // Gets the physical space dirty region. If repaintEverything is true, this
- // will be the full display bounds. Internally the dirty region is stored in
- // logical (aka layer stack) space.
- virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
+ // Gets the dirty region in layer stack space.
+ // If repaintEverything is true, this will be the full display bounds.
+ virtual Region getDirtyRegion(bool repaintEverything) const = 0;
- // Tests whether a given layerStackId belongs in this output
- virtual bool belongsInOutput(uint32_t layerStackId) const = 0;
+ // Tests whether a given layerStackId belongs in this output.
+ // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
+ // unless the layer should display on the primary output only and this is not the primary output
+
+ // A layer belongs to the output if its layerStackId matches. Additionally
+ // if the layer should only show in the internal (primary) display only and
+ // this output allows that.
+ virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+
+ // Returns a pointer to the output layer corresponding to the given layer on
+ // this output, or nullptr if the layer does not have one
+ virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
+
+ // Gets the OutputLayer corresponding to the input Layer instance from the
+ // current ordered set of output layers. If there is no such layer, a new
+ // one is created and returned.
+ virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::shared_ptr<Layer>,
+ sp<LayerFE>) = 0;
+
+ // Sets the new ordered set of output layers for this output
+ virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0;
+
+ // Gets the ordered set of output layers for this output
+ virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
protected:
- ~Output() = default;
-
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
new file mode 100644
index 0000000..4b8ef38
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/StrongPointer.h>
+
+namespace android::compositionengine {
+
+class Output;
+class Layer;
+class LayerFE;
+
+/**
+ * An output layer contains the output-dependent composition state for a layer
+ */
+class OutputLayer {
+public:
+ virtual ~OutputLayer();
+
+ // Gets the output which owns this output layer
+ virtual const Output& getOutput() const = 0;
+
+ // Gets the display-independent layer which this output layer represents
+ virtual Layer& getLayer() const = 0;
+
+ // Gets the front-end layer interface this output layer represents
+ virtual LayerFE& getLayerFE() const = 0;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index dd01b05..ddeb730 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -75,16 +75,13 @@
// Allocates a buffer as scratch space for GPU composition
virtual sp<GraphicBuffer> dequeueBuffer() = 0;
- // Queues the drawn buffer for consumption by HWC
- virtual void queueBuffer() = 0;
+ // Queues the drawn buffer for consumption by HWC. readyFence is the fence
+ // which will fire when the buffer is ready for consumption.
+ virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
// Called after the HWC calls are made to present the display
virtual void onPresentDisplayCompleted() = 0;
- // Marks the current buffer has finished, so that it can be presented and
- // swapped out
- virtual void finishBuffer() = 0;
-
// Called to set the viewport and projection state for rendering into this
// surface
virtual void setViewportAndProjection() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index e23c431..b01eb64 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -27,6 +27,8 @@
std::shared_ptr<compositionengine::Display> createDisplay(
compositionengine::DisplayCreationArgs&&) override;
+ std::shared_ptr<compositionengine::Layer> createLayer(
+ compositionengine::LayerCreationArgs&&) override;
HWComposer& getHwComposer() const override;
void setHwComposer(std::unique_ptr<HWComposer>) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
similarity index 79%
rename from services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index a008ca9..b45de5a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -14,20 +14,19 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
-#define ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+#pragma once
-#include <stdint.h>
+#include <cstdint>
+#include <vector>
#include <utils/StrongPointer.h>
-#include <vector>
-
namespace android {
-// ---------------------------------------------------------------------------
class GraphicBuffer;
+namespace compositionengine::impl {
+
// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
// HWC display and layer. When updating a display target or a layer buffer,
// we have the option to send the buffer handle over or to request the HAL to
@@ -37,17 +36,17 @@
//
// To be able to find out whether a buffer is already in the HAL's cache, we
// use HWComposerBufferCache to mirror the cache in SF.
-class HWComposerBufferCache {
+class HwcBufferCache {
public:
- HWComposerBufferCache();
+ HwcBufferCache();
// Given a buffer queue slot and buffer, return the HWC cache slot and
// buffer to be sent to HWC.
//
// outBuffer is set to buffer when buffer is not in the HWC cache;
// otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
- uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer);
private:
// a vector as we expect "slot" to be in the range of [0, 63] (that is,
@@ -55,7 +54,5 @@
std::vector<sp<GraphicBuffer>> mBuffers;
};
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
new file mode 100644
index 0000000..631351b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <compositionengine/Layer.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class LayerFE;
+
+struct LayerCreationArgs;
+
+namespace impl {
+
+class Display;
+
+class Layer : public compositionengine::Layer {
+public:
+ Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&);
+ ~Layer() override;
+
+ sp<LayerFE> getLayerFE() const override;
+
+private:
+ const compositionengine::CompositionEngine& mCompositionEngine;
+ const wp<LayerFE> mLayerFE;
+};
+
+std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
+ compositionengine::LayerCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 521e1d7..3fd057c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -17,6 +17,8 @@
#pragma once
#include <memory>
+#include <utility>
+#include <vector>
#include <compositionengine/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -24,13 +26,15 @@
namespace android::compositionengine {
class CompositionEngine;
+class Layer;
+class OutputLayer;
namespace impl {
class Output : public virtual compositionengine::Output {
public:
Output(const CompositionEngine&);
- virtual ~Output();
+ ~Output() override;
bool isValid() const override;
@@ -38,7 +42,7 @@
void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
void setBounds(const ui::Size&) override;
- void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) override;
+ void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const mat4&) override;
void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
@@ -57,8 +61,15 @@
const OutputCompositionState& getState() const override;
OutputCompositionState& editState() override;
- Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
- bool belongsInOutput(uint32_t) const override;
+ Region getDirtyRegion(bool repaintEverything) const override;
+ bool belongsInOutput(uint32_t, bool) const override;
+
+ compositionengine::OutputLayer* getOutputLayerForLayer(
+ compositionengine::Layer*) const override;
+ std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer(
+ std::shared_ptr<compositionengine::Layer>, sp<LayerFE>) override;
+ void setOutputLayersOrderedByZ(OutputLayers&&) override;
+ const OutputLayers& getOutputLayersOrderedByZ() const override;
// Testing
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
@@ -79,6 +90,8 @@
std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
+
+ OutputLayers mOutputLayersOrderedByZ;
};
} // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 96a0e0f..024ed45 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -34,12 +34,11 @@
// If false, this output is not considered secure
bool isSecure{false};
- // If true, only layer stacks with a matching layerStack value will be
- // displayed. If false, all layer stacks will be displayed.
- bool singleLayerStack{true};
+ // If true, this output displays layers that are internal-only
+ bool layerStackInternal{false};
// The layer stack to display on this display
- uint32_t singleLayerStackId{~0u};
+ uint32_t layerStackId{~0u};
// The physical space screen bounds
Rect bounds;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
new file mode 100644
index 0000000..f3d0258
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <compositionengine/OutputLayer.h>
+
+namespace android::compositionengine::impl {
+
+class OutputLayer : public compositionengine::OutputLayer {
+public:
+ OutputLayer(const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>,
+ sp<compositionengine::LayerFE>);
+ ~OutputLayer() override;
+
+ const compositionengine::Output& getOutput() const override;
+ compositionengine::Layer& getLayer() const override;
+ compositionengine::LayerFE& getLayerFE() const override;
+
+private:
+ const compositionengine::Output& mOutput;
+ std::shared_ptr<compositionengine::Layer> mLayer;
+ sp<compositionengine::LayerFE> mLayerFE;
+};
+
+std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>,
+ sp<compositionengine::LayerFE>);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 0489310..2f0fceb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -53,9 +53,8 @@
status_t beginFrame(bool mustRecompose) override;
status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
sp<GraphicBuffer> dequeueBuffer() override;
- void queueBuffer() override;
+ void queueBuffer(base::unique_fd&& readyFence) override;
void onPresentDisplayCompleted() override;
- void finishBuffer() override;
void setViewportAndProjection() override;
void flip() override;
@@ -77,9 +76,6 @@
const sp<ANativeWindow> mNativeWindow;
// Current buffer being rendered into
sp<GraphicBuffer> mGraphicBuffer;
- // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
- // that drawing to the buffer is now complete.
- base::unique_fd mBufferReady;
const sp<DisplaySurface> mDisplaySurface;
ui::Size mSize;
std::uint32_t mPageFlipCount{0};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 1967666..0f57685 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -18,6 +18,7 @@
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/LayerCreationArgs.h>
#include <gmock/gmock.h>
#include <renderengine/RenderEngine.h>
@@ -31,6 +32,7 @@
~CompositionEngine() override;
MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
+ MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&));
MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
new file mode 100644
index 0000000..a7cc08e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Layer : public compositionengine::Layer {
+public:
+ Layer();
+ virtual ~Layer();
+
+ MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
new file mode 100644
index 0000000..92e0070
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/LayerFE.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+// Defines the interface used by the CompositionEngine to make requests
+// of the front-end layer.
+class LayerFE : public compositionengine::LayerFE {
+public:
+ LayerFE();
+ virtual ~LayerFE();
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 37f7007..2972ad7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -17,7 +17,10 @@
#pragma once
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/Output.h>
+#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <gmock/gmock.h>
@@ -35,7 +38,7 @@
MOCK_METHOD6(setProjection,
void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
MOCK_METHOD1(setBounds, void(const ui::Size&));
- MOCK_METHOD2(setLayerStackFilter, void(bool, uint32_t));
+ MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const mat4&));
MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
@@ -53,8 +56,17 @@
MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
MOCK_METHOD0(editState, OutputCompositionState&());
- MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
- MOCK_CONST_METHOD1(belongsInOutput, bool(uint32_t));
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+
+ MOCK_CONST_METHOD1(getOutputLayerForLayer,
+ compositionengine::OutputLayer*(compositionengine::Layer*));
+ MOCK_METHOD2(getOrCreateOutputLayer,
+ std::unique_ptr<compositionengine::OutputLayer>(
+ std::shared_ptr<compositionengine::Layer>,
+ sp<compositionengine::LayerFE>));
+ MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
+ MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
new file mode 100644
index 0000000..f5e5026
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/OutputLayer.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class OutputLayer : public compositionengine::OutputLayer {
+public:
+ OutputLayer();
+ virtual ~OutputLayer();
+
+ MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
+ MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&());
+ MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index 2269e57..e9ff330 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,9 +39,8 @@
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD1(prepareFrame, status_t(std::vector<CompositionInfo>& compositionData));
MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>());
- MOCK_METHOD0(queueBuffer, void());
+ MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
MOCK_METHOD0(onPresentDisplayCompleted, void());
- MOCK_METHOD0(finishBuffer, void());
MOCK_METHOD0(setViewportAndProjection, void());
MOCK_METHOD0(flip, void());
MOCK_CONST_METHOD1(dump, void(std::string& result));
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
new file mode 100644
index 0000000..08483cb
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/mock/Layer.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Layer::Layer() = default;
+Layer::~Layer() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
new file mode 100644
index 0000000..607eaad
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/mock/LayerFE.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+LayerFE::LayerFE() = default;
+LayerFE::~LayerFE() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
new file mode 100644
index 0000000..4da9377
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/mock/OutputLayer.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+OutputLayer::OutputLayer() = default;
+OutputLayer::~OutputLayer() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 127d729..cb08b81 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -16,6 +16,7 @@
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/Layer.h>
#include <renderengine/RenderEngine.h>
#include "DisplayHardware/HWComposer.h"
@@ -38,6 +39,10 @@
return compositionengine::impl::createDisplay(*this, std::move(args));
}
+std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) {
+ return compositionengine::impl::createLayer(*this, std::move(args));
+}
+
HWComposer& CompositionEngine::getHwComposer() const {
return *mHwComposer.get();
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
similarity index 75%
rename from services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
rename to services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index a234b63..6f340b9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -14,21 +14,18 @@
* limitations under the License.
*/
-#include "HWComposerBufferCache.h"
-
+#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
-namespace android {
+namespace android::compositionengine::impl {
-HWComposerBufferCache::HWComposerBufferCache()
-{
+HwcBufferCache::HwcBufferCache() {
mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
}
-void HWComposerBufferCache::getHwcBuffer(int slot,
- const sp<GraphicBuffer>& buffer,
- uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
-{
+void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer) {
if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
// default to slot 0
slot = 0;
@@ -51,4 +48,4 @@
}
}
-} // namespace android
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
new file mode 100644
index 0000000..aaa758e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/impl/Layer.h>
+
+namespace android::compositionengine {
+
+Layer::~Layer() = default;
+
+namespace impl {
+
+std::shared_ptr<compositionengine::Layer> createLayer(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::LayerCreationArgs&& args) {
+ return std::make_shared<Layer>(compositionEngine, std::move(args));
+}
+
+Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args)
+ : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) {
+ static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning
+}
+
+Layer::~Layer() = default;
+
+sp<LayerFE> Layer::getLayerFE() const {
+ return mLayerFE.promote();
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dbf77b0..e103ebe 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -17,11 +17,17 @@
#include <android-base/stringprintf.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputLayer.h>
#include <ui/DebugUtils.h>
-namespace android::compositionengine::impl {
+namespace android::compositionengine {
+
+Output::~Output() = default;
+
+namespace impl {
Output::Output(const CompositionEngine& compositionEngine)
: mCompositionEngine(compositionEngine) {}
@@ -75,22 +81,34 @@
dirtyEntireOutput();
}
-void Output::setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) {
- mState.singleLayerStack = singleLayerStack;
- mState.singleLayerStackId = singleLayerStackId;
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+ mState.layerStackId = layerStackId;
+ mState.layerStackInternal = isInternal;
dirtyEntireOutput();
}
void Output::setColorTransform(const mat4& transform) {
const bool isIdentity = (transform == mat4());
-
- mState.colorTransform =
+ const auto newColorTransform =
isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+
+ if (mState.colorTransform == newColorTransform) {
+ return;
+ }
+
+ mState.colorTransform = newColorTransform;
+
+ dirtyEntireOutput();
}
void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
ui::RenderIntent renderIntent) {
+ if (mState.colorMode == mode && mState.dataspace == dataspace &&
+ mState.renderIntent == renderIntent) {
+ return;
+ }
+
mState.colorMode = mode;
mState.dataspace = dataspace;
mState.renderIntent = renderIntent;
@@ -100,6 +118,8 @@
ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
renderIntent);
+
+ dirtyEntireOutput();
}
void Output::dump(std::string& out) const {
@@ -164,23 +184,51 @@
return mState;
}
-Region Output::getPhysicalSpaceDirtyRegion(bool repaintEverything) const {
- Region dirty;
- if (repaintEverything) {
- dirty.set(mState.bounds);
- } else {
- dirty = mState.transform.transform(mState.dirtyRegion);
- dirty.andSelf(mState.bounds);
+Region Output::getDirtyRegion(bool repaintEverything) const {
+ Region dirty(mState.viewport);
+ if (!repaintEverything) {
+ dirty.andSelf(mState.dirtyRegion);
}
return dirty;
}
-bool Output::belongsInOutput(uint32_t layerStackId) const {
- return !mState.singleLayerStack || (layerStackId == mState.singleLayerStackId);
+bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+ // The layerStackId's must match, and also the layer must not be internal
+ // only when not on an internal output.
+ return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+}
+
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(
+ compositionengine::Layer* layer) const {
+ for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+ if (outputLayer && &outputLayer->getLayer() == layer) {
+ return outputLayer.get();
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
+ std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
+ for (auto& outputLayer : mOutputLayersOrderedByZ) {
+ if (outputLayer && &outputLayer->getLayer() == layer.get()) {
+ return std::move(outputLayer);
+ }
+ }
+ return createOutputLayer(*this, layer, layerFE);
+}
+
+void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
+ mOutputLayersOrderedByZ = std::move(layers);
+}
+
+const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
+ return mOutputLayersOrderedByZ;
}
void Output::dirtyEntireOutput() {
mState.dirtyRegion.set(mState.bounds);
}
-} // namespace android::compositionengine::impl
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 7d8765f..78807ff 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -22,11 +22,9 @@
void OutputCompositionState::dump(std::string& out) const {
dumpVal(out, "isEnabled", isEnabled);
dumpVal(out, "isSecure", isSecure);
- if (singleLayerStack) {
- out.append("layerStack=<any>");
- } else {
- dumpVal(out, "layerStack", singleLayerStackId);
- }
+
+ dumpVal(out, "layerStack", layerStackId);
+ dumpVal(out, "layerStackInternal", layerStackInternal);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
new file mode 100644
index 0000000..e95f3a6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputLayer.h>
+
+namespace android::compositionengine {
+
+OutputLayer::~OutputLayer() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const compositionengine::Output& display, std::shared_ptr<compositionengine::Layer> layer,
+ sp<compositionengine::LayerFE> layerFE) {
+ return std::make_unique<OutputLayer>(display, layer, layerFE);
+}
+
+OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE)
+ : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+
+OutputLayer::~OutputLayer() = default;
+
+const compositionengine::Output& OutputLayer::getOutput() const {
+ return mOutput;
+}
+
+compositionengine::Layer& OutputLayer::getLayer() const {
+ return *mLayer;
+}
+
+compositionengine::LayerFE& OutputLayer::getLayerFE() const {
+ return *mLayerFE;
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index d546fc8..3f841d2 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -156,7 +156,7 @@
return mGraphicBuffer;
}
-void RenderSurface::queueBuffer() {
+void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
auto& hwc = mCompositionEngine.getHwComposer();
const auto id = mDisplay.getId();
@@ -178,9 +178,9 @@
if (mGraphicBuffer == nullptr) {
ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
} else {
- status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
- mGraphicBuffer->getNativeBuffer(),
- dup(mBufferReady));
+ status_t result =
+ mNativeWindow->queueBuffer(mNativeWindow.get(),
+ mGraphicBuffer->getNativeBuffer(), dup(readyFence));
if (result != NO_ERROR) {
ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
result);
@@ -190,12 +190,10 @@
LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
} else {
mNativeWindow->cancelBuffer(mNativeWindow.get(),
- mGraphicBuffer->getNativeBuffer(),
- dup(mBufferReady));
+ mGraphicBuffer->getNativeBuffer(), dup(readyFence));
}
}
- mBufferReady.reset();
mGraphicBuffer = nullptr;
}
}
@@ -217,14 +215,6 @@
ui::Transform::ROT_0);
}
-void RenderSurface::finishBuffer() {
- auto& renderEngine = mCompositionEngine.getRenderEngine();
- mBufferReady = renderEngine.flush();
- if (mBufferReady.get() < 0) {
- renderEngine.finish();
- }
-}
-
void RenderSurface::flip() {
mPageFlipCount++;
}
@@ -263,9 +253,5 @@
return mGraphicBuffer;
}
-base::unique_fd& RenderSurface::mutableBufferReadyForTest() {
- return mBufferReady;
-}
-
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8cb6936..cd2d454 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -156,17 +156,17 @@
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
// Otherwise if the values are different, updates happen
- EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+ EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
EXPECT_CALL(mHwComposer,
- setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::BT2100_PQ,
+ setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::DISPLAY_P3,
ui::RenderIntent::TONE_MAP_COLORIMETRIC))
.Times(1);
- mDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
ui::RenderIntent::TONE_MAP_COLORIMETRIC);
- EXPECT_EQ(ui::ColorMode::BT2100_PQ, mDisplay.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::SRGB, mDisplay.getState().dataspace);
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
}
@@ -174,7 +174,7 @@
impl::Display virtualDisplay{mCompositionEngine,
DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
- virtualDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
ui::RenderIntent::TONE_MAP_COLORIMETRIC);
EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
new file mode 100644
index 0000000..f2a1aad
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/HwcBufferCache.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine {
+namespace {
+
+class HwcBufferCacheTest : public testing::Test {
+public:
+ ~HwcBufferCacheTest() override = default;
+
+ void testSlot(const int inSlot, const uint32_t expectedSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // Setting a slot to use nullptr lookslike works, but note that
+ // the output values make it look like no new buffer is being set....
+ mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+ }
+
+ impl::HwcBufferCache mCache;
+ sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+};
+
+TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
+ testSlot(0, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
+ testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
+ testSlot(-123, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) {
+ testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
new file mode 100644
index 0000000..26115a3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/impl/Layer.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/LayerFE.h>
+
+namespace android::compositionengine {
+namespace {
+
+using testing::StrictMock;
+
+class LayerTest : public testing::Test {
+public:
+ ~LayerTest() override = default;
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
+ impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(LayerTest, canInstantiateLayer) {}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
new file mode 100644
index 0000000..0a929ac
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/Output.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+using testing::StrictMock;
+
+class OutputLayerTest : public testing::Test {
+public:
+ ~OutputLayerTest() override = default;
+
+ compositionengine::mock::Output mOutput;
+ std::shared_ptr<compositionengine::mock::Layer> mLayer{
+ new StrictMock<compositionengine::mock::Layer>()};
+ sp<compositionengine::mock::LayerFE> mLayerFE{
+ new StrictMock<compositionengine::mock::LayerFE>()};
+ impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputLayerTest, canInstantiateOutputLayer) {}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index f060cff..cb71821 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -19,6 +19,9 @@
#include <compositionengine/impl/Output.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
#include <ui/Rect.h>
@@ -40,15 +43,21 @@
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ mOutput.editState().bounds = kDefaultDisplaySize;
}
~OutputTest() override = default;
+ static const Rect kDefaultDisplaySize;
+
StrictMock<mock::CompositionEngine> mCompositionEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
impl::Output mOutput{mCompositionEngine};
};
+const Rect OutputTest::kDefaultDisplaySize{100, 200};
+
/* ------------------------------------------------------------------------
* Basic construction
*/
@@ -73,8 +82,6 @@
*/
TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
mOutput.editState().isEnabled = true;
mOutput.setCompositionEnabled(true);
@@ -84,25 +91,21 @@
}
TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
mOutput.editState().isEnabled = false;
mOutput.setCompositionEnabled(true);
EXPECT_TRUE(mOutput.getState().isEnabled);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
mOutput.editState().isEnabled = true;
mOutput.setCompositionEnabled(false);
EXPECT_FALSE(mOutput.getState().isEnabled);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/* ------------------------------------------------------------------------
@@ -132,7 +135,7 @@
*/
TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
- const ui::Size displaySize{100, 200};
+ const ui::Size displaySize{200, 400};
EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
@@ -149,16 +152,13 @@
*/
TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
-
const uint32_t layerStack = 123u;
- mOutput.setLayerStackFilter(true, layerStack);
+ mOutput.setLayerStackFilter(layerStack, true);
- EXPECT_TRUE(mOutput.getState().singleLayerStack);
- EXPECT_EQ(layerStack, mOutput.getState().singleLayerStackId);
+ EXPECT_TRUE(mOutput.getState().layerStackInternal);
+ EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
- EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/* ------------------------------------------------------------------------
@@ -173,27 +173,45 @@
EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+ // Since identity is the default, the dirty region should be unchanged (empty)
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+
// Non-identity matrix sets a non-identity state value
const mat4 nonIdentity = mat4() * 2;
mOutput.setColorTransform(nonIdentity);
EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+
+ // Since this is a state change, the entire output should now be dirty.
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/* ------------------------------------------------------------------------
* Output::setColorMode
*/
-TEST_F(OutputTest, setColorModeSetsModeUnlessNoChange) {
- EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+ EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
- mOutput.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
ui::RenderIntent::TONE_MAP_COLORIMETRIC);
- EXPECT_EQ(ui::ColorMode::BT2100_PQ, mOutput.getState().colorMode);
- EXPECT_EQ(ui::Dataspace::SRGB, mOutput.getState().dataspace);
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
+ mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
+ mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
+ mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+
+ mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
}
/* ------------------------------------------------------------------------
@@ -212,51 +230,137 @@
}
/* ------------------------------------------------------------------------
- * Output::getPhysicalSpaceDirtyRegion()
+ * Output::getDirtyRegion()
*/
-TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+ const Rect viewport{100, 200};
+ mOutput.editState().viewport = viewport;
mOutput.editState().dirtyRegion.set(50, 300);
{
- Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+ Region result = mOutput.getDirtyRegion(true);
- EXPECT_THAT(result, RegionEq(Region(displaySize)));
- }
-
- // For repaint everything == true, the returned value does not depend on the display
- // rotation.
- mOutput.editState().transform.set(ui::Transform::ROT_90, 0, 0);
-
- {
- Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
-
- EXPECT_THAT(result, RegionEq(Region(displaySize)));
+ EXPECT_THAT(result, RegionEq(Region(viewport)));
}
}
-TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
- const Rect displaySize{100, 200};
- mOutput.editState().bounds = displaySize;
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
+ const Rect viewport{100, 200};
+ mOutput.editState().viewport = viewport;
mOutput.editState().dirtyRegion.set(50, 300);
{
- Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+ Region result = mOutput.getDirtyRegion(false);
// The dirtyRegion should be clipped to the display bounds.
EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
}
+}
- mOutput.editState().transform.set(ui::Transform::ROT_90, displaySize.getWidth(),
- displaySize.getHeight());
+/* ------------------------------------------------------------------------
+ * Output::belongsInOutput()
+ */
+
+TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
+ const uint32_t layerStack1 = 123u;
+ const uint32_t layerStack2 = 456u;
+
+ // If the output accepts layerStack1 and internal-only layers....
+ mOutput.setLayerStackFilter(layerStack1, true);
+
+ // Any layer with layerStack1 belongs to it, internal-only or not.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+
+ // If the output accepts layerStack21 but not internal-only layers...
+ mOutput.setLayerStackFilter(layerStack1, false);
+
+ // Only non-internal layers with layerStack1 belong to it.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getOutputLayerForLayer()
+ */
+
+TEST_F(OutputTest, getOutputLayerForLayerWorks) {
+ mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
+ mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1));
+ outputLayers.emplace_back(nullptr);
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ StrictMock<mock::Layer> layer;
+ StrictMock<mock::Layer> otherLayer;
+
+ // If the input layer matches the first OutputLayer, it will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
+ EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer));
+
+ // If the input layer matches the second OutputLayer, it will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
+ EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer));
+
+ // If the input layer does not match an output layer, null will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getOrCreateOutputLayer()
+ */
+
+TEST_F(OutputTest, getOrCreateOutputLayerWorks) {
+ mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>();
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(nullptr);
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()};
+ sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()};
+
+ StrictMock<mock::Layer> otherLayer;
{
- Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+ // If there is no OutputLayer corresponding to the input layer, a
+ // new OutputLayer is constructed and returned.
+ EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer));
+ auto result = mOutput.getOrCreateOutputLayer(layer, layerFE);
+ EXPECT_NE(existingOutputLayer, result.get());
+ EXPECT_TRUE(result.get() != nullptr);
+ EXPECT_EQ(layer.get(), &result->getLayer());
+ EXPECT_EQ(layerFE.get(), &result->getLayerFE());
- // The dirtyRegion should be rotated and clipped to the display bounds.
- EXPECT_THAT(result, RegionEq(Region(Rect(100, 50))));
+ // The entries in the ordered array should be unchanged.
+ auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
+ EXPECT_EQ(nullptr, outputLayers[0].get());
+ EXPECT_EQ(existingOutputLayer, outputLayers[1].get());
+ }
+
+ {
+ // If there is an existing OutputLayer for the requested layer, an owned
+ // pointer is returned
+ EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer));
+ auto result = mOutput.getOrCreateOutputLayer(layer, layerFE);
+ EXPECT_EQ(existingOutputLayer, result.get());
+
+ // The corresponding entry in the ordered array should be cleared.
+ auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
+ EXPECT_EQ(nullptr, outputLayers[0].get());
+ EXPECT_EQ(nullptr, outputLayers[1].get());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 13cc663..c56d92a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -373,7 +373,7 @@
.WillOnce(Return(false));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
- mSurface.queueBuffer();
+ mSurface.queueBuffer(base::unique_fd());
EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
}
@@ -387,7 +387,7 @@
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
- mSurface.queueBuffer();
+ mSurface.queueBuffer(base::unique_fd());
EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
}
@@ -402,7 +402,7 @@
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
- mSurface.queueBuffer();
+ mSurface.queueBuffer(base::unique_fd());
EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
}
@@ -419,7 +419,7 @@
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
- mSurface.queueBuffer();
+ mSurface.queueBuffer(base::unique_fd());
EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
}
@@ -436,7 +436,7 @@
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
- mSurface.queueBuffer();
+ mSurface.queueBuffer(base::unique_fd());
EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
}
@@ -452,29 +452,6 @@
}
/* ------------------------------------------------------------------------
- * RenderSurface::finishBuffer()
- */
-
-TEST_F(RenderSurfaceTest, finishBufferJustFlushesRenderEngine) {
- int fd = dup(1);
-
- EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(fd))));
-
- mSurface.finishBuffer();
-
- EXPECT_EQ(fd, mSurface.mutableBufferReadyForTest().release());
-}
-
-TEST_F(RenderSurfaceTest, finishBufferFlushesAndFinishesRenderEngine) {
- EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(-2))));
- EXPECT_CALL(mRenderEngine, finish()).Times(1);
-
- mSurface.finishBuffer();
-
- EXPECT_EQ(-2, mSurface.mutableBufferReadyForTest().release());
-}
-
-/* ------------------------------------------------------------------------
* RenderSurface::setViewportAndProjection()
*/
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index ca49f6c..941b4ad 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -26,9 +26,16 @@
ContainerLayer::~ContainerLayer() = default;
-void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) {}
+bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&,
+ renderengine::LayerSettings&) {
+ return false;
+}
bool ContainerLayer::isVisible() const {
+ return false;
+}
+
+bool ContainerLayer::canReceiveInput() const {
return !isHiddenByPolicy();
}
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 413844b..fec82d7 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -29,16 +29,21 @@
~ContainerLayer() override;
const char* getTypeId() const override { return "ContainerLayer"; }
- void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) override;
bool isVisible() const override;
+ bool canReceiveInput() const override;
+
void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
int32_t supportedPerFrameMetadata) override;
bool isCreatedFromMainThread() const override { return true; }
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer);
};
} // namespace android
diff --git a/services/surfaceflinger/DdmConnection.cpp b/services/surfaceflinger/DdmConnection.cpp
deleted file mode 100644
index 35d55f5..0000000
--- a/services/surfaceflinger/DdmConnection.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "jni.h"
-#include "DdmConnection.h"
-
-namespace android {
-
-void DdmConnection_start(const char* name) {
- ALOGI("DdmConnection_start");
- DdmConnection::start(name);
-}
-
-void DdmConnection::start(const char* name) {
- JavaVM* vm;
- JNIEnv* env;
-
- // start a VM
- JavaVMInitArgs args;
- JavaVMOption opt;
-
- opt.optionString =
- "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
-
- args.version = JNI_VERSION_1_4;
- args.options = &opt;
- args.nOptions = 1;
- args.ignoreUnrecognized = JNI_FALSE;
-
-
- // TODO: Should this just link against libnativehelper and use its
- // JNI_CreateJavaVM wrapper that essential does this dlopen/dlsym
- // work based on the current system default runtime?
- void* libart_dso = dlopen("libart.so", RTLD_NOW);
- ALOGE_IF(!libart_dso, "DdmConnection: %s", dlerror());
-
- void* libandroid_runtime_dso = dlopen("libandroid_runtime.so", RTLD_NOW);
- ALOGE_IF(!libandroid_runtime_dso, "DdmConnection: %s", dlerror());
-
- if (!libart_dso || !libandroid_runtime_dso) {
- goto error;
- }
-
- jint (*JNI_CreateJavaVM)(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
- JNI_CreateJavaVM = reinterpret_cast<decltype(JNI_CreateJavaVM)>(
- dlsym(libart_dso, "JNI_CreateJavaVM"));
- ALOGE_IF(!JNI_CreateJavaVM, "DdmConnection: %s", dlerror());
-
- jint (*registerNatives)(JNIEnv* env, jclass clazz);
- registerNatives = reinterpret_cast<decltype(registerNatives)>(
- dlsym(libandroid_runtime_dso,
- "Java_com_android_internal_util_WithFramework_registerNatives"));
- ALOGE_IF(!registerNatives, "DdmConnection: %s", dlerror());
-
- if (!JNI_CreateJavaVM || !registerNatives) {
- goto error;
- }
-
- if (JNI_CreateJavaVM(&vm, &env, &args) == 0) {
- jclass startClass;
- jmethodID startMeth;
-
- // register native code
- if (registerNatives(env, 0) == 0) {
- // set our name by calling DdmHandleAppName.setAppName()
- startClass = env->FindClass("android/ddm/DdmHandleAppName");
- if (startClass) {
- startMeth = env->GetStaticMethodID(startClass,
- "setAppName", "(Ljava/lang/String;I)V");
- if (startMeth) {
- jstring str = env->NewStringUTF(name);
- env->CallStaticVoidMethod(startClass, startMeth, str, getuid());
- env->DeleteLocalRef(str);
- }
- }
-
- // initialize DDMS communication by calling
- // DdmRegister.registerHandlers()
- startClass = env->FindClass("android/ddm/DdmRegister");
- if (startClass) {
- startMeth = env->GetStaticMethodID(startClass,
- "registerHandlers", "()V");
- if (startMeth) {
- env->CallStaticVoidMethod(startClass, startMeth);
- }
- }
- }
- }
- return;
-
-error:
- if (libandroid_runtime_dso) {
- dlclose(libandroid_runtime_dso);
- }
- if (libart_dso) {
- dlclose(libart_dso);
- }
-}
-
-}; // namespace android
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
deleted file mode 100644
index 938d14b..0000000
--- a/services/surfaceflinger/DdmConnection.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_DDM_CONNECTION
-#define ANDROID_SF_DDM_CONNECTION
-
-namespace android {
-
-// wrapper for dlsym
-extern "C" void DdmConnection_start(const char* name);
-
-class DdmConnection {
-public:
- // Creates a JVM and registers all handlers to DDMS.
- // This allows tools relying on DDMS to find surfaceflinger
- // (e.g: Memory Leak finder, heap analyzer, ...)
- static void start(const char* name);
-};
-
-}; // namespace android
-
-#endif /* ANDROID_SF_DDM_CONNECTION */
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 54111de..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -166,7 +166,7 @@
// ----------------------------------------------------------------------------
void DisplayDevice::setLayerStack(uint32_t stack) {
- mCompositionDisplay->setLayerStackFilter(!isPrimary(), stack);
+ mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
}
// ----------------------------------------------------------------------------
@@ -330,7 +330,7 @@
}
uint32_t DisplayDevice::getLayerStack() const {
- return mCompositionDisplay->getState().singleLayerStackId;
+ return mCompositionDisplay->getState().layerStackId;
}
const ui::Transform& DisplayDevice::getTransform() const {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 03eafd5..8343e5a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -260,17 +260,20 @@
const uint32_t bufferSlotCount = 1;
Error error = kDefaultError;
if (mClient_2_2) {
- mClient_2_2->createVirtualDisplay_2_2(width, height, *format, bufferSlotCount,
- [&](const auto& tmpError, const auto& tmpDisplay,
- const auto& tmpFormat) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ mClient_2_2->createVirtualDisplay_2_2(width, height,
+ static_cast<types::V1_1::PixelFormat>(*format),
+ bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outDisplay = tmpDisplay;
- *format = tmpFormat;
- });
+ *outDisplay = tmpDisplay;
+ *format = static_cast<types::V1_2::PixelFormat>(
+ tmpFormat);
+ });
} else {
mClient->createVirtualDisplay(width, height,
static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index ba3d2a6..542e840 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -48,11 +48,11 @@
using types::V1_0::ColorTransform;
using types::V1_0::Transform;
-using types::V1_1::PixelFormat;
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
using V2_1::Config;
using V2_1::Display;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 1599995..d63cd79 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -23,10 +23,12 @@
#include <string_view>
#include <vector>
+#include <ui/GraphicTypes.h>
+
namespace android {
struct DisplayId {
- using Type = uint64_t;
+ using Type = PhysicalDisplayId;
Type value;
uint16_t manufacturerId() const;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 18c524f..7f451a5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,10 +21,10 @@
#include <sys/types.h>
#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
#include "DisplayIdentification.h"
-#include "HWComposerBufferCache.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -88,7 +88,7 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- HWComposerBufferCache mHwcBufferCache;
+ compositionengine::impl::HwcBufferCache mHwcBufferCache;
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 392b608..68e7876 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -143,8 +143,8 @@
return error;
}
- auto display = std::make_unique<Display>(
- *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, DisplayType::Virtual);
+ auto display = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, mCapabilities,
+ displayId, DisplayType::Virtual);
display->setConnected(true);
*outDisplay = display.get();
mDisplays.emplace(displayId, std::move(display));
@@ -182,8 +182,8 @@
return;
}
- auto newDisplay = std::make_unique<Display>(
- *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
+ auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor,
+ mCapabilities, displayId, displayType);
newDisplay->setConnected(true);
mDisplays.emplace(displayId, std::move(newDisplay));
} else if (connection == Connection::Disconnected) {
@@ -224,7 +224,36 @@
}
// Display methods
+Display::~Display() = default;
+Display::Config::Config(Display& display, hwc2_config_t id)
+ : mDisplay(display),
+ mId(id),
+ mWidth(-1),
+ mHeight(-1),
+ mVsyncPeriod(-1),
+ mDpiX(-1),
+ mDpiY(-1) {}
+
+Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+ : mConfig(new Config(display, id)) {}
+
+float Display::Config::Builder::getDefaultDensity() {
+ // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+ // resolution displays get TV density. Maybe eventually we'll need to update
+ // it for 4k displays, though hopefully those will just report accurate DPI
+ // information to begin with. This is also used for virtual displays and
+ // older HWC implementations, so be careful about orientation.
+
+ auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
+ if (longDimension >= 1080) {
+ return ACONFIGURATION_DENSITY_XHIGH;
+ } else {
+ return ACONFIGURATION_DENSITY_TV;
+ }
+}
+
+namespace impl {
Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
DisplayType type)
@@ -272,35 +301,7 @@
}
}
-Display::Config::Config(Display& display, hwc2_config_t id)
- : mDisplay(display),
- mId(id),
- mWidth(-1),
- mHeight(-1),
- mVsyncPeriod(-1),
- mDpiX(-1),
- mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
- : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
- // Default density is based on TVs: 1080p displays get XHIGH density, lower-
- // resolution displays get TV density. Maybe eventually we'll need to update
- // it for 4k displays, though hopefully those will just report accurate DPI
- // information to begin with. This is also used for virtual displays and
- // older HWC implementations, so be careful about orientation.
-
- auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
- if (longDimension >= 1080) {
- return ACONFIGURATION_DENSITY_XHIGH;
- } else {
- return ACONFIGURATION_DENSITY_TV;
- }
-}
-
// Required by HWC2 display
-
Error Display::acceptChanges()
{
auto intError = mComposer.acceptDisplayChanges(mId);
@@ -794,6 +795,7 @@
return mLayers.at(id).get();
}
+} // namespace impl
// Layer methods
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index f5cb97e..c1f481a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -130,16 +130,11 @@
};
// Convenience C++ class to access hwc2_device_t Display functions directly.
-class Display
-{
+class Display {
public:
- Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
- const std::unordered_set<Capability>& capabilities,
- hwc2_display_t id, DisplayType type);
- ~Display();
+ virtual ~Display();
- class Config
- {
+ class Config {
public:
class Builder
{
@@ -207,78 +202,136 @@
float mDpiY;
};
- // Required by HWC2
+ virtual hwc2_display_t getId() const = 0;
+ virtual bool isConnected() const = 0;
+ virtual void setConnected(bool connected) = 0; // For use by Device only
+ virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
- [[clang::warn_unused_result]] Error acceptChanges();
- [[clang::warn_unused_result]] Error createLayer(Layer** outLayer);
- [[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
- [[clang::warn_unused_result]] Error getActiveConfig(
- std::shared_ptr<const Config>* outConfig) const;
- [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
- [[clang::warn_unused_result]] Error getChangedCompositionTypes(
- std::unordered_map<Layer*, Composition>* outTypes);
- [[clang::warn_unused_result]] Error getColorModes(
- std::vector<android::ui::ColorMode>* outModes) const;
+ [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
+ [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
+ [[clang::warn_unused_result]] virtual Error destroyLayer(Layer* layer) = 0;
+ [[clang::warn_unused_result]] virtual Error getActiveConfig(
+ std::shared_ptr<const Config>* outConfig) const = 0;
+ [[clang::warn_unused_result]] virtual Error getActiveConfigIndex(int* outIndex) const = 0;
+ [[clang::warn_unused_result]] virtual Error getChangedCompositionTypes(
+ std::unordered_map<Layer*, Composition>* outTypes) = 0;
+ [[clang::warn_unused_result]] virtual Error getColorModes(
+ std::vector<android::ui::ColorMode>* outModes) const = 0;
// Returns a bitmask which contains HdrMetadata::Type::*.
- [[clang::warn_unused_result]] int32_t getSupportedPerFrameMetadata() const;
- [[clang::warn_unused_result]] Error getRenderIntents(
+ [[clang::warn_unused_result]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
+ [[clang::warn_unused_result]] virtual Error getRenderIntents(
android::ui::ColorMode colorMode,
- std::vector<android::ui::RenderIntent>* outRenderIntents) const;
- [[clang::warn_unused_result]] Error getDataspaceSaturationMatrix(
- android::ui::Dataspace dataspace, android::mat4* outMatrix);
+ std::vector<android::ui::RenderIntent>* outRenderIntents) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDataspaceSaturationMatrix(
+ android::ui::Dataspace dataspace, android::mat4* outMatrix) = 0;
+
+ // Doesn't call into the HWC2 device, so no Errors are possible
+ virtual std::vector<std::shared_ptr<const Config>> getConfigs() const = 0;
+
+ [[clang::warn_unused_result]] virtual Error getName(std::string* outName) const = 0;
+ [[clang::warn_unused_result]] virtual Error getRequests(
+ DisplayRequest* outDisplayRequests,
+ std::unordered_map<Layer*, LayerRequest>* outLayerRequests) = 0;
+ [[clang::warn_unused_result]] virtual Error getType(DisplayType* outType) const = 0;
+ [[clang::warn_unused_result]] virtual Error supportsDoze(bool* outSupport) const = 0;
+ [[clang::warn_unused_result]] virtual Error getHdrCapabilities(
+ android::HdrCapabilities* outCapabilities) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayedContentSamplingAttributes(
+ android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const = 0;
+ [[clang::warn_unused_result]] virtual Error setDisplayContentSamplingEnabled(
+ bool enabled, uint8_t componentMask, uint64_t maxFrames) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayedContentSample(
+ uint64_t maxFrames, uint64_t timestamp,
+ android::DisplayedFrameStats* outStats) const = 0;
+ [[clang::warn_unused_result]] virtual Error getReleaseFences(
+ std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
+ [[clang::warn_unused_result]] virtual Error present(
+ android::sp<android::Fence>* outPresentFence) = 0;
+ [[clang::warn_unused_result]] virtual Error setActiveConfig(
+ const std::shared_ptr<const Config>& config) = 0;
+ [[clang::warn_unused_result]] virtual Error setClientTarget(
+ uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+ const android::sp<android::Fence>& acquireFence, android::ui::Dataspace dataspace) = 0;
+ [[clang::warn_unused_result]] virtual Error setColorMode(
+ android::ui::ColorMode mode, android::ui::RenderIntent renderIntent) = 0;
+ [[clang::warn_unused_result]] virtual Error setColorTransform(
+ const android::mat4& matrix, android_color_transform_t hint) = 0;
+ [[clang::warn_unused_result]] virtual Error setOutputBuffer(
+ const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& releaseFence) = 0;
+ [[clang::warn_unused_result]] virtual Error setPowerMode(PowerMode mode) = 0;
+ [[clang::warn_unused_result]] virtual Error setVsyncEnabled(Vsync enabled) = 0;
+ [[clang::warn_unused_result]] virtual Error validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests) = 0;
+ [[clang::warn_unused_result]] virtual Error presentOrValidate(
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
+};
+
+namespace impl {
+
+class Display : public HWC2::Display {
+public:
+ Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
+ const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
+ DisplayType type);
+ ~Display() override;
+
+ // Required by HWC2
+ Error acceptChanges() override;
+ Error createLayer(Layer** outLayer) override;
+ Error destroyLayer(Layer* layer) override;
+ Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
+ Error getActiveConfigIndex(int* outIndex) const override;
+ Error getChangedCompositionTypes(std::unordered_map<Layer*, Composition>* outTypes) override;
+ Error getColorModes(std::vector<android::ui::ColorMode>* outModes) const override;
+ // Returns a bitmask which contains HdrMetadata::Type::*.
+ int32_t getSupportedPerFrameMetadata() const override;
+ Error getRenderIntents(android::ui::ColorMode colorMode,
+ std::vector<android::ui::RenderIntent>* outRenderIntents) const override;
+ Error getDataspaceSaturationMatrix(android::ui::Dataspace dataspace,
+ android::mat4* outMatrix) override;
// Doesn't call into the HWC2 device, so no errors are possible
- std::vector<std::shared_ptr<const Config>> getConfigs() const;
+ std::vector<std::shared_ptr<const Config>> getConfigs() const override;
- [[clang::warn_unused_result]] Error getName(std::string* outName) const;
- [[clang::warn_unused_result]] Error getRequests(
- DisplayRequest* outDisplayRequests,
- std::unordered_map<Layer*, LayerRequest>* outLayerRequests);
- [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
- [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
- [[clang::warn_unused_result]] Error getHdrCapabilities(
- android::HdrCapabilities* outCapabilities) const;
- [[clang::warn_unused_result]] Error getDisplayedContentSamplingAttributes(
- android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
- uint8_t* outComponentMask) const;
- [[clang::warn_unused_result]] Error setDisplayContentSamplingEnabled(bool enabled,
- uint8_t componentMask,
- uint64_t maxFrames) const;
- [[clang::warn_unused_result]] Error getDisplayedContentSample(
- uint64_t maxFrames, uint64_t timestamp, android::DisplayedFrameStats* outStats) const;
- [[clang::warn_unused_result]] Error getReleaseFences(
- std::unordered_map<Layer*,
- android::sp<android::Fence>>* outFences) const;
- [[clang::warn_unused_result]] Error present(
- android::sp<android::Fence>* outPresentFence);
- [[clang::warn_unused_result]] Error setActiveConfig(
- const std::shared_ptr<const Config>& config);
- [[clang::warn_unused_result]] Error setClientTarget(
- uint32_t slot, const android::sp<android::GraphicBuffer>& target,
- const android::sp<android::Fence>& acquireFence,
- android::ui::Dataspace dataspace);
- [[clang::warn_unused_result]] Error setColorMode(
- android::ui::ColorMode mode,
- android::ui::RenderIntent renderIntent);
- [[clang::warn_unused_result]] Error setColorTransform(
- const android::mat4& matrix, android_color_transform_t hint);
- [[clang::warn_unused_result]] Error setOutputBuffer(
- const android::sp<android::GraphicBuffer>& buffer,
- const android::sp<android::Fence>& releaseFence);
- [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode);
- [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
- [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
- uint32_t* outNumRequests);
- [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
- uint32_t* outNumRequests,
- android::sp<android::Fence>* outPresentFence, uint32_t* state);
+ Error getName(std::string* outName) const override;
+ Error getRequests(DisplayRequest* outDisplayRequests,
+ std::unordered_map<Layer*, LayerRequest>* outLayerRequests) override;
+ Error getType(DisplayType* outType) const override;
+ Error supportsDoze(bool* outSupport) const override;
+ Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+ Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
+ android::ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const override;
+ Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) const override;
+ Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+ android::DisplayedFrameStats* outStats) const override;
+ Error getReleaseFences(
+ std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+ Error present(android::sp<android::Fence>* outPresentFence) override;
+ Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
+ Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+ const android::sp<android::Fence>& acquireFence,
+ android::ui::Dataspace dataspace) override;
+ Error setColorMode(android::ui::ColorMode mode,
+ android::ui::RenderIntent renderIntent) override;
+ Error setColorTransform(const android::mat4& matrix, android_color_transform_t hint) override;
+ Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& releaseFence) override;
+ Error setPowerMode(PowerMode mode) override;
+ Error setVsyncEnabled(Vsync enabled) override;
+ Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
+ Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+ android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
// Other Display methods
-
- hwc2_display_t getId() const { return mId; }
- bool isConnected() const { return mIsConnected; }
- void setConnected(bool connected); // For use by Device only
- const std::unordered_set<DisplayCapability>& getCapabilities() const {
+ hwc2_display_t getId() const override { return mId; }
+ bool isConnected() const override { return mIsConnected; }
+ void setConnected(bool connected) override; // For use by Device only
+ const std::unordered_set<DisplayCapability>& getCapabilities() const override {
return mDisplayCapabilities;
};
@@ -309,6 +362,7 @@
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
std::unordered_set<DisplayCapability> mDisplayCapabilities;
};
+} // namespace impl
// Convenience C++ class to access hwc2_device_t Layer functions directly.
class Layer
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 87ae7dd..d6543d1 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -21,11 +21,11 @@
#include <string>
#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
#include "DisplayIdentification.h"
-#include "HWComposerBufferCache.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -253,7 +253,7 @@
bool mMustRecompose;
- HWComposerBufferCache mHwcBufferCache;
+ compositionengine::impl::HwcBufferCache mHwcBufferCache;
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index aa9bc15..51e1f00 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -46,6 +46,7 @@
#include <gui/Surface.h>
#include "BufferLayer.h"
+#include "ColorLayer.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
#include "Layer.h"
@@ -94,8 +95,6 @@
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
mCurrentState.requested_legacy = mCurrentState.active_legacy;
- mCurrentState.appId = 0;
- mCurrentState.type = 0;
mCurrentState.active.w = UINT32_MAX;
mCurrentState.active.h = UINT32_MAX;
mCurrentState.active.transform.set(0, 0);
@@ -295,80 +294,83 @@
return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
}
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
- const State& s(getDrawingState());
- Region transparentRegion = reduceTransparentRegion ? getActiveTransparentRegion(s) : Region();
- FloatRect bounds = computeBounds(transparentRegion);
+Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
+ if (!reduceTransparentRegion) {
+ return Rect{mScreenBounds};
+ }
+
+ FloatRect bounds = getBounds();
ui::Transform t = getTransform();
// Transform to screen space.
bounds = t.transform(bounds);
return Rect{bounds};
}
-FloatRect Layer::computeBounds() const {
+FloatRect Layer::getBounds() const {
const State& s(getDrawingState());
- return computeBounds(getActiveTransparentRegion(s));
+ return getBounds(getActiveTransparentRegion(s));
}
-FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
- const State& s(getDrawingState());
- Rect bounds = getCroppedBufferSize(s);
- FloatRect floatBounds = bounds.toFloatRect();
- if (bounds.isValid()) {
- // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
- // that they can clip it.
- floatBounds = cropChildBounds(floatBounds);
- } else {
- // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
- // parent bounds and inverting the transform to get the maximum bounds we can have that
- // will fit within our parent bounds.
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- ui::Transform t = s.active_legacy.transform;
- // When calculating the parent bounds for purposes of clipping, we don't need to
- // constrain the parent to its transparent region. The transparent region is an
- // optimization based on the buffer contents of the layer, but does not affect the
- // space allocated to it by policy, and thus children should be allowed to extend into
- // the parent's transparent region.
- // One of the main uses is a parent window with a child sitting behind the parent
- // window, marked by a transparent region. When computing the parent bounds from the
- // parent's perspective we pass in the transparent region to reduce buffer allocation
- // size. When computing the parent bounds from the child's perspective, we pass in an
- // empty transparent region in order to extend into the the parent bounds.
- floatBounds = p->computeBounds(Region());
- // Transform back to layer space.
- floatBounds = t.inverse().transform(floatBounds);
- }
- }
-
+FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
// Subtract the transparent region and snap to the bounds.
- return reduce(floatBounds, activeTransparentRegion);
+ return reduce(mBounds, activeTransparentRegion);
}
-FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+ui::Transform Layer::getTransformWithScale() const {
+ // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
+ // it isFixedSize) then there may be additional scaling not accounted
+ // for in the transform. We need to mirror this scaling to child surfaces
+ // or we will break the contract where WM can treat child surfaces as
+ // pixels in the parent surface.
+ if (!isFixedSize() || !getBE().compositionInfo.mBuffer) {
+ return mEffectiveTransform;
+ }
+
+ int bufferWidth;
+ int bufferHeight;
+ if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
+ bufferWidth = getBE().compositionInfo.mBuffer->getWidth();
+ bufferHeight = getBE().compositionInfo.mBuffer->getHeight();
+ } else {
+ bufferHeight = getBE().compositionInfo.mBuffer->getWidth();
+ bufferWidth = getBE().compositionInfo.mBuffer->getHeight();
+ }
+ float sx = getActiveWidth(getDrawingState()) / static_cast<float>(bufferWidth);
+ float sy = getActiveHeight(getDrawingState()) / static_cast<float>(bufferHeight);
+ ui::Transform extraParentScaling;
+ extraParentScaling.set(sx, 0, 0, sy);
+ return mEffectiveTransform * extraParentScaling;
+}
+
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
const State& s(getDrawingState());
- Rect bounds = getCroppedBufferSize(s);
- FloatRect croppedBounds = childBounds;
- // If the layer has bounds, then crop the passed in child bounds and pass
- // it to our parents so they can crop it as well. If the layer has no bounds,
- // then pass on the child bounds.
- if (bounds.isValid()) {
- croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
- }
+ // Calculate effective layer transform
+ mEffectiveTransform = parentTransform * getActiveTransform(s);
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- // Transform to parent space and allow parent layer to crop the
- // child bounds as well.
- ui::Transform t = s.active_legacy.transform;
- croppedBounds = t.transform(croppedBounds);
- croppedBounds = p->cropChildBounds(croppedBounds);
- croppedBounds = t.inverse().transform(croppedBounds);
+ // Transform parent bounds to layer space
+ parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
+
+ // Calculate display frame
+ mSourceBounds = computeSourceBounds(parentBounds);
+
+ // Calculate bounds by croping diplay frame with layer crop and parent bounds
+ FloatRect bounds = mSourceBounds;
+ const Rect layerCrop = getCrop(s);
+ if (!layerCrop.isEmpty()) {
+ bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
}
- return croppedBounds;
+ bounds = bounds.intersect(parentBounds);
+
+ mBounds = bounds;
+ mScreenBounds = mEffectiveTransform.transform(mBounds);
+ for (const sp<Layer>& child : mDrawingChildren) {
+ child->computeBounds(mBounds, getTransformWithScale());
+ }
}
+
+
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
Rect crop = getCrop(s);
@@ -380,7 +382,7 @@
return size;
}
-Rect Layer::computeInitialCrop(const Rect& windowBounds) const {
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
// the crop is the area of the window that gets cropped, but not
// scaled in any ways.
const State& s(getDrawingState());
@@ -390,18 +392,13 @@
// if there are no window scaling involved, this operation will map to full
// pixels in the buffer.
- FloatRect activeCropFloat = computeBounds();
-
- // If we have valid window boundaries then we need to crop to the window
- // boundaries in layer space.
- if (windowBounds.isValid()) {
- const ui::Transform t = getTransform();
- // Transform to screen space.
- activeCropFloat = t.transform(activeCropFloat);
- activeCropFloat = activeCropFloat.intersect(windowBounds.toFloatRect());
- // Back to layer space to work with the content crop.
- activeCropFloat = t.inverse().transform(activeCropFloat);
- }
+ FloatRect activeCropFloat = getBounds();
+ ui::Transform t = getTransform();
+ // Transform to screen space.
+ activeCropFloat = t.transform(activeCropFloat);
+ activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
+ // Back to layer space to work with the content crop.
+ activeCropFloat = t.inverse().transform(activeCropFloat);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
// returning. This means
@@ -431,7 +428,7 @@
cropCoords[3] = vec2(win.right, win.top);
}
-FloatRect Layer::computeCrop(const Rect& windowBounds) const {
+FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
// the content crop is the area of the content that gets scaled to the
// layer's size. This is in buffer space.
FloatRect crop = getContentCrop().toFloatRect();
@@ -439,7 +436,7 @@
// In addition there is a WM-specified crop we pull from our drawing state.
const State& s(getDrawingState());
- Rect activeCrop = computeInitialCrop(windowBounds);
+ Rect activeCrop = computeInitialCrop(display);
Rect bufferSize = getBufferSize(s);
// Transform the window crop to match the buffer coordinate system,
@@ -556,9 +553,9 @@
Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
}
- // computeBounds returns a FloatRect to provide more accuracy during the
+ // getBounds returns a FloatRect to provide more accuracy during the
// transformation. We then round upon constructing 'frame'.
- Rect frame{t.transform(computeBounds(activeTransparentRegion))};
+ Rect frame{t.transform(getBounds(activeTransparentRegion))};
if (!frame.intersect(display->getViewport(), &frame)) {
frame.clear();
}
@@ -599,14 +596,16 @@
to_string(error).c_str(), static_cast<int32_t>(error));
getBE().compositionInfo.hwc.z = z;
- int type = s.type;
- int appId = s.appId;
+ int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0);
sp<Layer> parent = mDrawingParent.promote();
if (parent.get()) {
auto& parentState = parent->getDrawingState();
- if (parentState.type >= 0 || parentState.appId >= 0) {
- type = parentState.type;
- appId = parentState.appId;
+ const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
+ if (parentType >= 0 || parentAppId >= 0) {
+ type = parentType;
+ appId = parentAppId;
}
}
@@ -643,7 +642,7 @@
* Here we cancel out the orientation component of the WM transform.
* The scaling and translate components are already included in our bounds
* computation so it's enough to just omit it in the composition.
- * See comment in onDraw with ref to b/36727915 for why.
+ * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
*/
transform = ui::Transform(invTransform) * tr * bufferOrientation;
}
@@ -685,7 +684,7 @@
}
// This gives us only the "orientation" component of the transform
- const State& s(getCurrentState());
+ const State& s(getDrawingState());
// Apply the layer's transform, followed by the display's global transform
// Here we're guaranteed that the layer's transform preserves rects
@@ -711,12 +710,51 @@
// drawing...
// ---------------------------------------------------------------------------
-void Layer::draw(const RenderArea& renderArea, const Region& clip) {
- onDraw(renderArea, clip, false);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ Region& clearRegion, renderengine::LayerSettings& layer) {
+ return prepareClientLayer(renderArea, clip, false, clearRegion, layer);
}
-void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) {
- onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+ Region& clearRegion, renderengine::LayerSettings& layer) {
+ return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
+ clearRegion, layer);
+}
+
+bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
+ bool useIdentityTransform, Region& /*clearRegion*/,
+ renderengine::LayerSettings& layer) {
+ FloatRect bounds = getBounds();
+ half alpha = getAlpha();
+ layer.geometry.boundaries = bounds;
+ if (useIdentityTransform) {
+ layer.geometry.positionTransform = mat4();
+ } else {
+ const ui::Transform transform = getTransform();
+ mat4 m;
+ m[0][0] = transform[0][0];
+ m[0][1] = transform[0][1];
+ m[0][3] = transform[0][2];
+ m[1][0] = transform[1][0];
+ m[1][1] = transform[1][1];
+ m[1][3] = transform[1][2];
+ m[3][0] = transform[2][0];
+ m[3][1] = transform[2][1];
+ m[3][3] = transform[2][2];
+ layer.geometry.positionTransform = m;
+ }
+
+ if (hasColorTransform()) {
+ layer.colorTransform = getColorTransform();
+ }
+
+ const auto roundedCornerState = getRoundedCornerState();
+ layer.geometry.roundedCornersRadius = roundedCornerState.radius;
+ layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+ layer.alpha = alpha;
+ layer.sourceDataspace = mCurrentDataSpace;
+ return true;
}
void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
@@ -806,7 +844,7 @@
renderengine::Mesh& mesh,
bool useIdentityTransform) const {
const ui::Transform renderAreaTransform(renderArea.getTransform());
- FloatRect win = computeBounds();
+ FloatRect win = getBounds();
vec2 lt = vec2(win.left, win.top);
vec2 lb = vec2(win.left, win.bottom);
@@ -1043,13 +1081,18 @@
ATRACE_CALL();
if (mLayerDetached) {
- return 0;
+ return flags;
+ }
+
+ if (mChildrenChanged) {
+ flags |= eVisibleRegion;
+ mChildrenChanged = false;
}
pushPendingState();
State c = getCurrentState();
if (!applyPendingStates(&c)) {
- return 0;
+ return flags;
}
flags = doTransactionResize(flags, &c);
@@ -1237,17 +1280,40 @@
return true;
}
-bool Layer::setColor(const half3& color) {
- if (color.r == mCurrentState.color.r && color.g == mCurrentState.color.g &&
- color.b == mCurrentState.color.b)
+bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) {
+ if (!mCurrentState.bgColorLayer && alpha == 0) {
return false;
-
+ }
mCurrentState.sequence++;
- mCurrentState.color.r = color.r;
- mCurrentState.color.g = color.g;
- mCurrentState.color.b = color.b;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+
+ if (!mCurrentState.bgColorLayer && alpha != 0) {
+ // create background color layer if one does not yet exist
+ uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
+ const String8& name = mName + "BackgroundColorLayer";
+ mCurrentState.bgColorLayer =
+ new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags));
+
+ // add to child list
+ addChild(mCurrentState.bgColorLayer);
+ mFlinger->mLayersAdded = true;
+ // set up SF to handle added color layer
+ if (isRemovedFromCurrentState()) {
+ mCurrentState.bgColorLayer->onRemovedFromCurrentState();
+ }
+ mFlinger->setTransactionFlags(eTransactionNeeded);
+ } else if (mCurrentState.bgColorLayer && alpha == 0) {
+ mCurrentState.bgColorLayer->reparent(nullptr);
+ mCurrentState.bgColorLayer = nullptr;
+ return true;
+ }
+
+ mCurrentState.bgColorLayer->setColor(color);
+ mCurrentState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min());
+ mCurrentState.bgColorLayer->setAlpha(alpha);
+ mCurrentState.bgColorLayer->setDataspace(dataspace);
+
return true;
}
@@ -1315,11 +1381,14 @@
return true;
}
-void Layer::setInfo(int32_t type, int32_t appId) {
- mCurrentState.appId = appId;
- mCurrentState.type = type;
+bool Layer::setMetadata(LayerMetadata data) {
+ bool changed = data.mMap != mCurrentState.metadata.mMap;
+ if (!changed) return false;
+ mCurrentState.metadata = std::move(data);
+ mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+ return true;
}
bool Layer::setLayerStack(uint32_t layerStack) {
@@ -1572,13 +1641,18 @@
}
void Layer::addChild(const sp<Layer>& layer) {
+ mChildrenChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+
mCurrentChildren.add(layer);
layer->setParent(this);
}
ssize_t Layer::removeChild(const sp<Layer>& layer) {
- layer->setParent(nullptr);
+ mChildrenChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+ layer->setParent(nullptr);
return mCurrentChildren.remove(layer);
}
@@ -1609,6 +1683,7 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
+ child->computeBounds(newParent->mBounds, newParent->getTransformWithScale());
}
}
@@ -1624,19 +1699,26 @@
return false;
}
+ sp<Layer> newParent;
+ if (newParentHandle != nullptr) {
+ auto handle = static_cast<Handle*>(newParentHandle.get());
+ newParent = handle->owner.promote();
+ if (newParent == nullptr) {
+ ALOGE("Unable to promote Layer handle");
+ return false;
+ }
+ if (newParent == this) {
+ ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str());
+ return false;
+ }
+ }
+
sp<Layer> parent = getParent();
if (parent != nullptr) {
parent->removeChild(this);
}
if (newParentHandle != nullptr) {
- auto handle = static_cast<Handle*>(newParentHandle.get());
- sp<Layer> newParent = handle->owner.promote();
- if (newParent == nullptr) {
- ALOGE("Unable to promote Layer handle");
- return false;
- }
-
newParent->addChild(this);
if (!newParent->isRemovedFromCurrentState()) {
addToCurrentState();
@@ -1929,34 +2011,7 @@
}
ui::Transform Layer::getTransform() const {
- ui::Transform t;
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- t = p->getTransform();
-
- // If the parent is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
- // it isFixedSize) then there may be additional scaling not accounted
- // for in the transform. We need to mirror this scaling in child surfaces
- // or we will break the contract where WM can treat child surfaces as
- // pixels in the parent surface.
- if (p->isFixedSize() && p->getBE().compositionInfo.mBuffer != nullptr) {
- int bufferWidth;
- int bufferHeight;
- if ((p->mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
- bufferWidth = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferHeight = p->getBE().compositionInfo.mBuffer->getHeight();
- } else {
- bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight();
- }
- float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth);
- float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight);
- ui::Transform extraParentScaling;
- extraParentScaling.set(sx, 0, 0, sy);
- t = t * extraParentScaling;
- }
- }
- return t * getActiveTransform(getDrawingState());
+ return mEffectiveTransform;
}
half Layer::getAlpha() const {
@@ -1988,7 +2043,7 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+ return radius > 0 ? RoundedCornerState(getBounds(), radius) : RoundedCornerState();
}
void Layer::commitChildList() {
@@ -2087,8 +2142,6 @@
layerInfo->set_queued_frames(getQueuedFrameCount());
layerInfo->set_refresh_pending(isBufferLatched());
- layerInfo->set_window_type(state.type);
- layerInfo->set_app_id(state.appId);
layerInfo->set_curr_frame(mCurrentFrameNumber);
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
@@ -2100,6 +2153,15 @@
barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
}
}
+
+ auto protoMap = layerInfo->mutable_metadata();
+ for (const auto& entry : state.metadata.mMap) {
+ (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+ }
+ LayerProtoHelper::writeToProto(mEffectiveTransform, layerInfo->mutable_effective_transform());
+ LayerProtoHelper::writeToProto(mSourceBounds, layerInfo->mutable_source_bounds());
+ LayerProtoHelper::writeToProto(mScreenBounds, layerInfo->mutable_screen_bounds());
+ LayerProtoHelper::writeToProto(mBounds, layerInfo->mutable_bounds());
}
void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) {
@@ -2138,6 +2200,10 @@
InputWindowInfo Layer::fillInputInfo() {
InputWindowInfo info = mDrawingState.inputInfo;
+ if (info.displayId == ADISPLAY_ID_NONE) {
+ info.displayId = mDrawingState.layerStack;
+ }
+
ui::Transform t = getTransform();
const float xScale = t.sx();
const float yScale = t.sy();
@@ -2148,7 +2214,10 @@
}
// Transform layer size to screen space and inset it by surface insets.
- Rect layerBounds = getBufferSize(getDrawingState());
+ // If this is a portal window, set the touchableRegion to the layerBounds.
+ Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+ ? getBufferSize(getDrawingState())
+ : info.touchableRegion.getBounds();
if (!layerBounds.isValid()) {
layerBounds = getCroppedBufferSize(getDrawingState());
}
@@ -2164,7 +2233,7 @@
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
- info.visible = isVisible();
+ info.visible = canReceiveInput();
return info;
}
@@ -2172,6 +2241,14 @@
return mDrawingState.inputInfo.token != nullptr;
}
+std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const {
+ return nullptr;
+}
+
+bool Layer::canReceiveInput() const {
+ return isVisible();
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 95a8630..9241e71 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
+#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
@@ -51,7 +52,6 @@
#include "TransactionCompletedThread.h"
#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWComposerBufferCache.h"
#include "RenderArea.h"
using namespace android::surfaceflinger;
@@ -68,6 +68,10 @@
class LayerDebugInfo;
class LayerBE;
+namespace compositionengine {
+class Layer;
+}
+
namespace impl {
class SurfaceInterceptor;
}
@@ -87,7 +91,7 @@
uint32_t flags;
};
-class Layer : public virtual RefBase {
+class Layer : public virtual compositionengine::LayerFE {
static std::atomic<int32_t> sSequence;
public:
@@ -167,8 +171,7 @@
Region activeTransparentRegion_legacy;
Region requestedTransparentRegion_legacy;
- int32_t appId;
- int32_t type;
+ LayerMetadata metadata;
// If non-null, a Surface this Surface's Z-order is interpreted relative to.
wp<Layer> zOrderRelativeOf;
@@ -202,6 +205,11 @@
mat4 colorTransform;
bool hasColorTransform;
+ // pointer to background color layer that, if set, appears below the buffer state layer
+ // and the buffer state layer's children. Z order will be set to
+ // INT_MIN
+ sp<Layer> bgColorLayer;
+
// The deque of callback handles for this frame. The back of the deque contains the most
// recent callback handle.
std::deque<sp<CallbackHandle>> callbackHandles;
@@ -211,6 +219,7 @@
virtual ~Layer();
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+ bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
// ------------------------------------------------------------------------
// Geometry setting functions.
@@ -265,7 +274,7 @@
virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
virtual bool setAlpha(float alpha);
- virtual bool setColor(const half3& color);
+ virtual bool setColor(const half3& /*color*/) { return false; };
// Set rounded corner radius for this layer and its children.
//
@@ -281,7 +290,7 @@
uint64_t frameNumber);
virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
- virtual void setInfo(int32_t type, int32_t appId);
+ virtual bool setMetadata(LayerMetadata data);
virtual bool reparentChildren(const sp<IBinder>& layer);
virtual void setChildrenDrawingParent(const sp<Layer>& layer);
virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -308,6 +317,7 @@
const std::vector<sp<CallbackHandle>>& /*handles*/) {
return false;
};
+ virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
@@ -318,6 +328,8 @@
// visually.
bool isLegacyDataSpace() const;
+ virtual std::shared_ptr<compositionengine::Layer> getCompositionLayer() const;
+
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
@@ -328,14 +340,24 @@
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
+ // Deprecated, please use compositionengine::Output::belongsInOutput()
+ // instead.
+ // TODO(lpique): Move the remaining callers (screencap) to the new function.
bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
}
void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
bool useIdentityTransform) const;
- FloatRect computeBounds(const Region& activeTransparentRegion) const;
- FloatRect computeBounds() const;
+ FloatRect getBounds(const Region& activeTransparentRegion) const;
+ FloatRect getBounds() const;
+
+ // Compute bounds for the layer and cache the results.
+ void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+
+ // Get effective layer transform, taking into account all its parent transform with any
+ // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
+ ui::Transform getTransformWithScale() const;
int32_t getSequence() const { return sequence; }
@@ -372,6 +394,11 @@
bool isHiddenByPolicy() const;
/*
+ * Returns whether this layer can receive input.
+ */
+ virtual bool canReceiveInput() const;
+
+ /*
* isProtected - true if the layer may contain protected content in the
* GRALLOC_USAGE_PROTECTED sense.
*/
@@ -406,11 +433,9 @@
virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
protected:
- /*
- * onDraw - draws the surface.
- */
- virtual void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) = 0;
+ virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ renderengine::LayerSettings& layer) = 0;
public:
virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
@@ -459,13 +484,15 @@
// If a buffer was replaced this frame, release the former buffer
virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
-
/*
- * draw - performs some global clipping optimizations
- * and calls onDraw().
+ * prepareClientLayer - populates a renderengine::LayerSettings to passed to
+ * RenderEngine::drawLayers. Returns true if the layer can be used, and
+ * false otherwise.
*/
- void draw(const RenderArea& renderArea, const Region& clip);
- void draw(const RenderArea& renderArea, bool useIdentityTransform);
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
+ renderengine::LayerSettings& layer);
+ bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+ Region& clearRegion, renderengine::LayerSettings& layer);
/*
* doTransaction - process the transaction. This is a good place to figure
@@ -503,8 +530,8 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
- const sp<Fence>& /*releaseFence*/) {
+ virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+ const sp<Fence>& /*releaseFence*/) {
return {};
}
@@ -623,7 +650,7 @@
ssize_t removeChild(const sp<Layer>& layer);
sp<Layer> getParent() const { return mCurrentParent.promote(); }
bool hasParent() const { return getParent() != nullptr; }
- Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+ Rect getScreenBounds(bool reduceTransparentRegion = true) const;
bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
bool setChildRelativeLayer(const sp<Layer>& childLayer,
const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -640,6 +667,15 @@
*/
virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+ /**
+ * Returns the source bounds. If the bounds are not defined, it is inferred from the
+ * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+ * For the root layer, this is the display viewport size.
+ */
+ virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+ return parentBounds;
+ }
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -671,22 +707,12 @@
uint32_t getEffectiveUsage(uint32_t usage) const;
- // Computes the crop applied to this layer. windowBounds is the boundary of
- // layer-stack space, so the cropping rectangle will be clipped to those
- // bounds in that space. The crop rectangle is returned in buffer space. If
- // windowBounds is invalid, then it is ignored.
- virtual FloatRect computeCrop(const Rect& windowBounds) const;
-
- // See the above method, but pulls the window boundaries from the display.
- FloatRect computeCrop(const sp<const DisplayDevice>& display) const {
- return computeCrop(display->getViewport());
- }
+ virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const;
// Compute the initial crop as specified by parent layers and the
// SurfaceControl for this layer. Does not include buffer crop from the
// IGraphicBufferProducer client, as that should not affect child clipping.
// Returns in screen space.
- Rect computeInitialCrop(const Rect& windowBounds) const;
-
+ Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
/**
* Setup rounded corners coordinates of this layer, taking into account the layer bounds and
* crop coordinates, transforming them into layer space.
@@ -806,7 +832,13 @@
FenceTimeline mReleaseTimeline;
// main thread
+ // Active buffer fields
sp<GraphicBuffer> mActiveBuffer;
+ sp<Fence> mActiveBufferFence;
+ // False if the buffer and its contents have been previously used for GPU
+ // composition, true otherwise.
+ bool mIsActiveBufferUpdatedForGpu = true;
+
ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
Rect mCurrentCrop;
uint32_t mCurrentTransform{0};
@@ -844,6 +876,8 @@
// Can only be accessed with the SF state lock held.
bool mLayerDetached{false};
+ // Can only be accessed with the SF state lock held.
+ bool mChildrenChanged{false};
private:
/**
@@ -860,14 +894,6 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
-
- /**
- * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
- * The cropped bounds must be transformed back from parent layer space to child layer space by
- * applying the inverse of the child's transformation.
- */
- FloatRect cropChildBounds(const FloatRect& childBounds) const;
-
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
* INVALID_RECT if the layer has no buffer and no crop.
@@ -875,6 +901,21 @@
* bounds are constrained by its parent bounds.
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
+
+ // Cached properties computed from drawing state
+ // Effective transform taking into account parent transforms and any parent scaling.
+ ui::Transform mEffectiveTransform;
+
+ // Bounds of the layer before any transformation is applied and before it has been cropped
+ // by its parents.
+ FloatRect mSourceBounds;
+
+ // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
+ // its parent bounds.
+ FloatRect mBounds;
+
+ // Layer bounds in screen space.
+ FloatRect mScreenBounds;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h
index 3f5134e..6270efa 100644
--- a/services/surfaceflinger/LayerBE.h
+++ b/services/surfaceflinger/LayerBE.h
@@ -19,6 +19,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <compositionengine/impl/HwcBufferCache.h>
+
#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/Texture.h>
@@ -26,7 +28,6 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWComposerBufferCache.h"
#include "SurfaceFlinger.h"
namespace android {
@@ -120,7 +121,7 @@
bool clearClientTarget;
Rect displayFrame;
FloatRect sourceCrop;
- HWComposerBufferCache bufferCache;
+ compositionengine::impl::HwcBufferCache bufferCache;
HWC2::Transform transform;
};
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index dc2b300..ce0611c 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -1,3 +1,4 @@
+akrulec@google.com
chaviw@google.com
lpy@google.com
marissaw@google.com
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 9b2a6fc..075e238 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -757,18 +757,7 @@
const uint32_t hwcLatency = 0;
// Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
- const nsecs_t nextRefresh = computeNextRefresh(hwcLatency);
-
- // The DispSync time is already adjusted for the difference between
- // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
- // we don't need to factor that in here. Pad a little to avoid
- // weird effects if apps might be requesting times right on the edge.
- nsecs_t extraPadding = 0;
- if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
- extraPadding = 1000000; // 1ms (6% of 60Hz)
- }
-
- return nextRefresh + extraPadding;
+ return computeNextRefresh(hwcLatency);
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7b25adb..5d9cfde 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -19,8 +19,11 @@
#include <pthread.h>
#include <sched.h>
#include <sys/types.h>
+
#include <chrono>
#include <cstdint>
+#include <optional>
+#include <type_traits>
#include <android-base/stringprintf.h>
@@ -35,18 +38,72 @@
#include "EventThread.h"
using namespace std::chrono_literals;
-using android::base::StringAppendF;
-
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
+using base::StringAppendF;
+using base::StringPrintf;
+
+namespace {
+
+auto vsyncPeriod(VSyncRequest request) {
+ return static_cast<std::underlying_type_t<VSyncRequest>>(request);
+}
+
+std::string toString(VSyncRequest request) {
+ switch (request) {
+ case VSyncRequest::None:
+ return "VSyncRequest::None";
+ case VSyncRequest::Single:
+ return "VSyncRequest::Single";
+ default:
+ return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
+ }
+}
+
+std::string toString(const EventThreadConnection& connection) {
+ return StringPrintf("Connection{%p, %s}", &connection,
+ toString(connection.vsyncRequest).c_str());
+}
+
+std::string toString(const DisplayEventReceiver::Event& event) {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}",
+ event.header.displayId,
+ event.hotplug.connected ? "connected" : "disconnected");
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", count=%u}",
+ event.header.displayId, event.vsync.count);
+ default:
+ return "Event{}";
+ }
+}
+
+DisplayEventReceiver::Event makeHotplug(PhysicalDisplayId displayId, nsecs_t timestamp,
+ bool connected) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+ event.hotplug.connected = connected;
+ return event;
+}
+
+DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
+ uint32_t count) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+ event.vsync.count = count;
+ return event;
+}
+
+} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
- ResyncCallback resyncCallback)
+ ResyncCallback resyncCallback,
+ ResetIdleTimerCallback resetIdleTimerCallback)
: resyncCallback(std::move(resyncCallback)),
- count(-1),
+ resetIdleTimerCallback(std::move(resetIdleTimerCallback)),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -65,8 +122,8 @@
return NO_ERROR;
}
-status_t EventThreadConnection::setVsyncRate(uint32_t count) {
- mEventThread->setVsyncRate(count, this);
+status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
+ mEventThread->setVsyncRate(rate, this);
return NO_ERROR;
}
@@ -92,33 +149,28 @@
namespace impl {
EventThread::EventThread(std::unique_ptr<VSyncSource> src,
- const InterceptVSyncsCallback& interceptVSyncsCallback,
- const ResetIdleTimerCallback& resetIdleTimerCallback,
- const char* threadName)
- : EventThread(nullptr, std::move(src), interceptVSyncsCallback, threadName) {
- mResetIdleTimer = resetIdleTimerCallback;
-}
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+ : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
const char* threadName)
- : EventThread(src, nullptr, interceptVSyncsCallback, threadName) {}
+ : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
: mVSyncSource(src),
mVSyncSourceUnique(std::move(uniqueSrc)),
- mInterceptVSyncsCallback(interceptVSyncsCallback) {
+ mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThreadName(threadName) {
if (src == nullptr) {
mVSyncSource = mVSyncSourceUnique.get();
}
- for (auto& event : mVSyncEvent) {
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- event.header.id = 0;
- event.header.timestamp = 0;
- event.vsync.count = 0;
- }
+ mVSyncSource->setCallback(this);
- mThread = std::thread(&EventThread::threadMain, this);
+ mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+ threadMain(lock);
+ });
pthread_setname_np(mThread.native_handle(), threadName);
@@ -136,9 +188,11 @@
}
EventThread::~EventThread() {
+ mVSyncSource->setCallback(nullptr);
+
{
std::lock_guard<std::mutex> lock(mMutex);
- mKeepRunning = false;
+ mState = State::Quit;
mCondition.notify_all();
}
mThread.join();
@@ -149,8 +203,10 @@
mVSyncSource->setPhaseOffset(phaseOffset);
}
-sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
+sp<EventThreadConnection> EventThread::createEventConnection(
+ ResyncCallback resyncCallback, ResetIdleTimerCallback resetIdleTimerCallback) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
+ std::move(resetIdleTimerCallback));
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -178,21 +234,24 @@
}
}
-void EventThread::setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) {
- if (int32_t(count) >= 0) { // server must protect against bad params
- std::lock_guard<std::mutex> lock(mMutex);
- const int32_t new_count = (count == 0) ? -1 : count;
- if (connection->count != new_count) {
- connection->count = new_count;
- mCondition.notify_all();
- }
+void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) {
+ if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
+ if (connection->vsyncRequest != request) {
+ connection->vsyncRequest = request;
+ mCondition.notify_all();
}
}
void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, bool reset) {
- if (mResetIdleTimer && reset) {
+ if (connection->resetIdleTimerCallback && reset) {
ATRACE_NAME("resetIdleTimer");
- mResetIdleTimer();
+ connection->resetIdleTimerCallback();
}
if (connection->resyncCallback) {
@@ -201,249 +260,220 @@
std::lock_guard<std::mutex> lock(mMutex);
- if (connection->count < 0) {
- connection->count = 0;
+ if (connection->vsyncRequest == VSyncRequest::None) {
+ connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
}
}
void EventThread::onScreenReleased() {
std::lock_guard<std::mutex> lock(mMutex);
- if (!mUseSoftwareVSync) {
- // disable reliance on h/w vsync
- mUseSoftwareVSync = true;
- mCondition.notify_all();
+ if (!mVSyncState || mVSyncState->synthetic) {
+ return;
}
+
+ mVSyncState->synthetic = true;
+ mCondition.notify_all();
}
void EventThread::onScreenAcquired() {
std::lock_guard<std::mutex> lock(mMutex);
- if (mUseSoftwareVSync) {
- // resume use of h/w vsync
- mUseSoftwareVSync = false;
- mCondition.notify_all();
+ if (!mVSyncState || !mVSyncState->synthetic) {
+ return;
}
+
+ mVSyncState->synthetic = false;
+ mCondition.notify_all();
}
void EventThread::onVSyncEvent(nsecs_t timestamp) {
std::lock_guard<std::mutex> lock(mMutex);
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = 0;
- mVSyncEvent[0].header.timestamp = timestamp;
- mVSyncEvent[0].vsync.count++;
+
+ LOG_FATAL_IF(!mVSyncState);
+ mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
mCondition.notify_all();
}
-void EventThread::onHotplugReceived(DisplayType displayType, bool connected) {
+void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) {
std::lock_guard<std::mutex> lock(mMutex);
- DisplayEventReceiver::Event event;
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
- event.header.id = displayType == DisplayType::Primary ? 0 : 1;
- event.header.timestamp = systemTime();
- event.hotplug.connected = connected;
-
- mPendingEvents.push(event);
+ mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));
mCondition.notify_all();
}
-void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
- std::unique_lock<std::mutex> lock(mMutex);
- while (mKeepRunning) {
- DisplayEventReceiver::Event event;
- std::vector<sp<EventThreadConnection>> signalConnections;
- signalConnections = waitForEventLocked(&lock, &event);
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+ DisplayEventConsumers consumers;
- // dispatch events to listeners...
- for (const sp<EventThreadConnection>& conn : signalConnections) {
- // now see if we still need to report this event
- status_t err = conn->postEvent(event);
- if (err == -EAGAIN || err == -EWOULDBLOCK) {
- // The destination doesn't accept events anymore, it's probably
- // full. For now, we just drop the events on the floor.
- // FIXME: Note that some events cannot be dropped and would have
- // to be re-sent later.
- // Right-now we don't have the ability to do this.
- ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
- conn.get());
- } else if (err < 0) {
- // handle any other error on the pipe as fatal. the only
- // reasonable thing to do is to clean-up this connection.
- // The most common error we'll get here is -EPIPE.
- removeDisplayEventConnectionLocked(conn);
- }
- }
- }
-}
+ while (mState != State::Quit) {
+ std::optional<DisplayEventReceiver::Event> event;
-// This will return when (1) a vsync event has been received, and (2) there was
-// at least one connection interested in receiving it when we started waiting.
-std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked(
- std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) {
- std::vector<sp<EventThreadConnection>> signalConnections;
+ // Determine next event to dispatch.
+ if (!mPendingEvents.empty()) {
+ event = mPendingEvents.front();
+ mPendingEvents.pop_front();
- while (signalConnections.empty() && mKeepRunning) {
- bool eventPending = false;
- bool waitForVSync = false;
+ switch (event->header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ if (event->hotplug.connected && !mVSyncState) {
+ mVSyncState.emplace(event->header.displayId);
+ } else if (!event->hotplug.connected && mVSyncState &&
+ mVSyncState->displayId == event->header.displayId) {
+ mVSyncState.reset();
+ }
+ break;
- size_t vsyncCount = 0;
- nsecs_t timestamp = 0;
- for (auto& event : mVSyncEvent) {
- timestamp = event.header.timestamp;
- if (timestamp) {
- // we have a vsync event to dispatch
- if (mInterceptVSyncsCallback) {
- mInterceptVSyncsCallback(timestamp);
- }
- *outEvent = event;
- event.header.timestamp = 0;
- vsyncCount = event.vsync.count;
- break;
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ if (mInterceptVSyncsCallback) {
+ mInterceptVSyncsCallback(event->header.timestamp);
+ }
+ break;
}
}
- if (!timestamp) {
- // no vsync event, see if there are some other event
- eventPending = !mPendingEvents.empty();
- if (eventPending) {
- // we have some other event to dispatch
- *outEvent = mPendingEvents.front();
- mPendingEvents.pop();
- }
- }
+ bool vsyncRequested = false;
- // find out connections waiting for events
+ // Find connections that should consume this event.
auto it = mDisplayEventConnections.begin();
while (it != mDisplayEventConnections.end()) {
- sp<EventThreadConnection> connection(it->promote());
- if (connection != nullptr) {
- bool added = false;
- if (connection->count >= 0) {
- // we need vsync events because at least
- // one connection is waiting for it
- waitForVSync = true;
- if (timestamp) {
- // we consume the event only if it's time
- // (ie: we received a vsync event)
- if (connection->count == 0) {
- // fired this time around
- connection->count = -1;
- signalConnections.push_back(connection);
- added = true;
- } else if (connection->count == 1 ||
- (vsyncCount % connection->count) == 0) {
- // continuous event, and time to report it
- signalConnections.push_back(connection);
- added = true;
- }
- }
+ if (const auto connection = it->promote()) {
+ vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
+ if (event && shouldConsumeEvent(*event, connection)) {
+ consumers.push_back(connection);
}
- if (eventPending && !timestamp && !added) {
- // we don't have a vsync event to process
- // (timestamp==0), but we have some pending
- // messages.
- signalConnections.push_back(connection);
- }
++it;
} else {
- // we couldn't promote this reference, the connection has
- // died, so clean-up!
it = mDisplayEventConnections.erase(it);
}
}
- // Here we figure out if we need to enable or disable vsyncs
- if (timestamp && !waitForVSync) {
- // we received a VSYNC but we have no clients
- // don't report it, and disable VSYNC events
- disableVSyncLocked();
- } else if (!timestamp && waitForVSync) {
- // we have at least one client, so we want vsync enabled
- // (TODO: this function is called right after we finish
- // notifying clients of a vsync, so this call will be made
- // at the vsync rate, e.g. 60fps. If we can accurately
- // track the current state we could avoid making this call
- // so often.)
- enableVSyncLocked();
+ if (!consumers.empty()) {
+ dispatchEvent(*event, consumers);
+ consumers.clear();
}
- // note: !timestamp implies signalConnections.isEmpty(), because we
- // don't populate signalConnections if there's no vsync pending
- if (!timestamp && !eventPending) {
- // wait for something to happen
- if (waitForVSync) {
- // This is where we spend most of our time, waiting
- // for vsync events and new client registrations.
- //
- // If the screen is off, we can't use h/w vsync, so we
- // use a 16ms timeout instead. It doesn't need to be
- // precise, we just need to keep feeding our clients.
- //
- // We don't want to stall if there's a driver bug, so we
- // use a (long) timeout when waiting for h/w vsync, and
- // generate fake events when necessary.
- bool softwareSync = mUseSoftwareVSync;
- auto timeout = softwareSync ? 16ms : 1000ms;
- if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
- if (!softwareSync) {
- ALOGW("Timed out waiting for hw vsync; faking it");
- }
- // FIXME: how do we decide which display id the fake
- // vsync came from ?
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = 0;
- mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- mVSyncEvent[0].vsync.count++;
- }
- } else {
- // Nobody is interested in vsync, so we just want to sleep.
- // h/w vsync should be disabled, so this will wait until we
- // get a new connection, or an existing connection becomes
- // interested in receiving vsync again.
- mCondition.wait(*lock);
+ State nextState;
+ if (mVSyncState && vsyncRequested) {
+ nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+ } else {
+ ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
+ nextState = State::Idle;
+ }
+
+ if (mState != nextState) {
+ if (mState == State::VSync) {
+ mVSyncSource->setVSyncEnabled(false);
+ } else if (nextState == State::VSync) {
+ mVSyncSource->setVSyncEnabled(true);
+ }
+
+ mState = nextState;
+ }
+
+ if (event) {
+ continue;
+ }
+
+ // Wait for event or client registration/request.
+ if (mState == State::Idle) {
+ mCondition.wait(lock);
+ } else {
+ // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
+ // 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");
+
+ LOG_FATAL_IF(!mVSyncState);
+ mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
+ systemTime(SYSTEM_TIME_MONOTONIC),
+ ++mVSyncState->count));
}
}
}
-
- // here we're guaranteed to have a timestamp and some connections to signal
- // (The connections might have dropped out of mDisplayEventConnections
- // while we were asleep, but we'll still have strong references to them.)
- return signalConnections;
}
-void EventThread::enableVSyncLocked() {
- if (!mUseSoftwareVSync) {
- // never enable h/w VSYNC when screen is off
- if (!mVsyncEnabled) {
- mVsyncEnabled = true;
- mVSyncSource->setCallback(this);
- mVSyncSource->setVSyncEnabled(true);
- }
+bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ return true;
+
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ switch (connection->vsyncRequest) {
+ case VSyncRequest::None:
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::None;
+ return true;
+ case VSyncRequest::Periodic:
+ return true;
+ default:
+ return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
+ }
+
+ default:
+ return false;
}
- mDebugVsyncEnabled = true;
}
-void EventThread::disableVSyncLocked() {
- if (mVsyncEnabled) {
- mVsyncEnabled = false;
- mVSyncSource->setVSyncEnabled(false);
- mDebugVsyncEnabled = false;
+void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) {
+ for (const auto& consumer : consumers) {
+ switch (consumer->postEvent(event)) {
+ case NO_ERROR:
+ break;
+
+ case -EAGAIN:
+ // TODO: Try again if pipe is full.
+ ALOGW("Failed dispatching %s for %s", toString(event).c_str(),
+ toString(*consumer).c_str());
+ break;
+
+ default:
+ // Treat EPIPE and other errors as fatal.
+ removeDisplayEventConnectionLocked(consumer);
+ }
}
}
void EventThread::dump(std::string& result) const {
std::lock_guard<std::mutex> lock(mMutex);
- StringAppendF(&result, "VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
- StringAppendF(&result, " soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
- StringAppendF(&result, " numListeners=%zu,\n events-delivered: %u\n",
- mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count);
- for (const wp<EventThreadConnection>& weak : mDisplayEventConnections) {
- sp<EventThreadConnection> connection = weak.promote();
- StringAppendF(&result, " %p: count=%d\n", connection.get(),
- connection != nullptr ? connection->count : 0);
+
+ StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
+ if (mVSyncState) {
+ StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n",
+ mVSyncState->displayId, mVSyncState->count,
+ mVSyncState->synthetic ? ", synthetic" : "");
+ } else {
+ StringAppendF(&result, "none\n");
}
- StringAppendF(&result, " other-events-pending: %zu\n", mPendingEvents.size());
+
+ StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size());
+ for (const auto& event : mPendingEvents) {
+ StringAppendF(&result, " %s\n", toString(event).c_str());
+ }
+
+ StringAppendF(&result, " connections (count=%zu):\n", mDisplayEventConnections.size());
+ for (const auto& ptr : mDisplayEventConnections) {
+ if (const auto connection = ptr.promote()) {
+ StringAppendF(&result, " %s\n", toString(*connection).c_str());
+ }
+ }
+}
+
+const char* EventThread::toCString(State state) {
+ switch (state) {
+ case State::Idle:
+ return "Idle";
+ case State::Quit:
+ return "Quit";
+ case State::SyntheticVSync:
+ return "SyntheticVSync";
+ case State::VSync:
+ return "VSync";
+ }
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a411885..b275183 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -18,11 +18,11 @@
#include <sys/types.h>
-#include <array>
#include <condition_variable>
#include <cstdint>
+#include <deque>
#include <mutex>
-#include <queue>
+#include <optional>
#include <thread>
#include <vector>
@@ -45,6 +45,14 @@
// ---------------------------------------------------------------------------
using ResyncCallback = std::function<void()>;
+using ResetIdleTimerCallback = std::function<void()>;
+
+enum class VSyncRequest {
+ None = -1,
+ Single = 0,
+ Periodic = 1,
+ // Subsequent values are periods.
+};
class VSyncSource {
public:
@@ -62,13 +70,13 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread* eventThread, ResyncCallback resyncCallback);
+ EventThreadConnection(EventThread*, ResyncCallback, ResetIdleTimerCallback);
virtual ~EventThreadConnection();
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
- status_t setVsyncRate(uint32_t count) override;
+ status_t setVsyncRate(uint32_t rate) override;
void requestNextVsync() override; // asynchronous
// Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
// in order to update the configs.
@@ -76,11 +84,9 @@
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
+ const ResetIdleTimerCallback resetIdleTimerCallback;
- // count >= 1 : continuous event. count is the vsync rate
- // count == 0 : one-shot event that has not fired
- // count ==-1 : one-shot event that fired this round / disabled
- int32_t count;
+ VSyncRequest vsyncRequest = VSyncRequest::None;
private:
virtual void onFirstRef();
@@ -90,13 +96,10 @@
class EventThread {
public:
- // TODO: Remove once stable display IDs are plumbed through SF/WM interface.
- enum class DisplayType { Primary, External };
-
virtual ~EventThread();
- virtual sp<EventThreadConnection> createEventConnection(
- ResyncCallback resyncCallback) const = 0;
+ virtual sp<EventThreadConnection> createEventConnection(ResyncCallback,
+ ResetIdleTimerCallback) const = 0;
// called before the screen is turned off from main thread
virtual void onScreenReleased() = 0;
@@ -104,8 +107,7 @@
// called after the screen is turned on from main thread
virtual void onScreenAcquired() = 0;
- // called when receiving a hotplug event
- virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0;
+ virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
virtual void dump(std::string& result) const = 0;
@@ -113,7 +115,7 @@
virtual status_t registerDisplayEventConnection(
const sp<EventThreadConnection>& connection) = 0;
- virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0;
+ virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
bool resetIdleTimer) = 0;
@@ -124,20 +126,17 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
- using ResetIdleTimerCallback = std::function<void()>;
// TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
- EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
- const char* threadName);
- EventThread(std::unique_ptr<VSyncSource> src,
- const InterceptVSyncsCallback& interceptVSyncsCallback,
- const ResetIdleTimerCallback& resetIdleTimerCallback, const char* threadName);
+ EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
+ EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
~EventThread();
- sp<EventThreadConnection> createEventConnection(ResyncCallback resyncCallback) const override;
+ sp<EventThreadConnection> createEventConnection(ResyncCallback,
+ ResetIdleTimerCallback) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
- void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override;
+ void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection,
bool resetIdleTimer) override;
@@ -147,8 +146,7 @@
// called after the screen is turned on from main thread
void onScreenAcquired() override;
- // called when receiving a hotplug event
- void onHotplugReceived(DisplayType displayType, bool connected) override;
+ void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
void dump(std::string& result) const override;
@@ -157,20 +155,21 @@
private:
friend EventThreadTest;
+ using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
+
// TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+ void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
- void threadMain();
- std::vector<sp<EventThreadConnection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
- DisplayEventReceiver::Event* event)
- REQUIRES(mMutex);
+ bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const REQUIRES(mMutex);
+ void dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) REQUIRES(mMutex);
void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
REQUIRES(mMutex);
- void enableVSyncLocked() REQUIRES(mMutex);
- void disableVSyncLocked() REQUIRES(mMutex);
// Implements VSyncSource::Callback
void onVSyncEvent(nsecs_t timestamp) override;
@@ -181,26 +180,45 @@
// TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
- // constants
+
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const char* const mThreadName;
std::thread mThread;
mutable std::mutex mMutex;
mutable std::condition_variable mCondition;
- // protected by mLock
std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
- std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
- std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex);
- bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
- bool mVsyncEnabled GUARDED_BY(mMutex) = false;
- bool mKeepRunning GUARDED_BY(mMutex) = true;
+ std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
- // for debugging
- bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
+ // VSYNC state of connected display.
+ struct VSyncState {
+ explicit VSyncState(PhysicalDisplayId displayId) : displayId(displayId) {}
- // Callback that resets the idle timer when the next vsync is received.
- ResetIdleTimerCallback mResetIdleTimer;
+ const PhysicalDisplayId displayId;
+
+ // Number of VSYNC events since display was connected.
+ uint32_t count = 0;
+
+ // True if VSYNC should be faked, e.g. when display is off.
+ bool synthetic = false;
+ };
+
+ // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
+ // and support headless mode by injecting a fake display with synthetic VSYNC.
+ std::optional<VSyncState> mVSyncState GUARDED_BY(mMutex);
+
+ // State machine for event loop.
+ enum class State {
+ Idle,
+ Quit,
+ SyntheticVSync,
+ VSync,
+ };
+
+ State mState GUARDED_BY(mMutex) = State::Idle;
+
+ static const char* toCString(State);
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
index 5a76dbc..b28b1aa 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -22,8 +22,9 @@
namespace android {
namespace scheduler {
-IdleTimer::IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback)
- : mInterval(interval), mTimeoutCallback(timeoutCallback) {}
+IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback)
+ : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
IdleTimer::~IdleTimer() {
stop();
@@ -49,23 +50,52 @@
}
void IdleTimer::loop() {
- std::lock_guard<std::mutex> lock(mMutex);
- while (mState != TimerState::STOPPED) {
- if (mState == TimerState::IDLE) {
- mCondition.wait(mMutex);
- } else if (mState == TimerState::RESET) {
- mState = TimerState::WAITING;
- if (mCondition.wait_for(mMutex, mInterval) == std::cv_status::timeout) {
- if (mTimeoutCallback) {
- mTimeoutCallback();
- }
+ while (true) {
+ bool triggerReset = false;
+ bool triggerTimeout = false;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
}
- if (mState == TimerState::WAITING) {
- mState = TimerState::IDLE;
+
+ if (mState == TimerState::IDLE) {
+ mCondition.wait(mMutex);
+ continue;
+ }
+
+ if (mState == TimerState::RESET) {
+ triggerReset = true;
}
}
+ if (triggerReset && mResetCallback) {
+ mResetCallback();
+ }
+
+ { // lock the mutex again. someone might have called stop meanwhile
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
+ }
+
+ auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+ mState = TimerState::WAITING;
+ while (mState == TimerState::WAITING) {
+ constexpr auto zero = std::chrono::steady_clock::duration::zero();
+ auto waitTime = triggerTime - std::chrono::steady_clock::now();
+ if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
+ if (mState == TimerState::WAITING &&
+ (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+ triggerTimeout = true;
+ mState = TimerState::IDLE;
+ }
+ }
+ }
+ if (triggerTimeout && mTimeoutCallback) {
+ mTimeoutCallback();
+ }
}
-}
+} // namespace scheduler
void IdleTimer::reset() {
{
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
index aee3fa3..19f1267 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -32,9 +32,11 @@
class IdleTimer {
public:
using Interval = std::chrono::milliseconds;
+ using ResetCallback = std::function<void()>;
using TimeoutCallback = std::function<void()>;
- IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback);
+ IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback);
~IdleTimer();
void start();
@@ -62,6 +64,9 @@
// Interval after which timer expires.
const Interval mInterval;
+ // Callback that happens when timer resets.
+ const ResetCallback mResetCallback;
+
// Callback that happens when timer expires.
const TimeoutCallback mTimeoutCallback;
};
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 75a410b..1f18ead 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -96,7 +96,8 @@
}
mEventThread = eventThread;
- mEvents = eventThread->createEventConnection(std::move(resyncCallback));
+ mEvents =
+ eventThread->createEventConnection(std::move(resyncCallback), ResetIdleTimerCallback());
mEvents->stealReceiveChannel(&mEventTube);
mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
this);
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
new file mode 100644
index 0000000..ab1f460
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PhaseOffsets.h"
+
+#include <cutils/properties.h>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+using namespace android::sysprop;
+
+namespace scheduler {
+
+PhaseOffsets::~PhaseOffsets() = default;
+
+namespace impl {
+PhaseOffsets::PhaseOffsets() {
+ int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
+
+ int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.early_phase_offset_ns", value, "-1");
+ const int earlySfOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
+ const int earlyGlSfOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
+ const int earlyAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
+ const int earlyGlAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
+ const int highFpsEarlySfOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
+ const int highFpsEarlyGlSfOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
+ const int highFpsEarlyAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
+ const int highFpsEarlyGlAppOffsetNs = atoi(value);
+
+ // TODO(b/122905996): Define these in device.mk.
+ property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
+ const int highFpsLateAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
+ const int highFpsLateSfOffsetNs = atoi(value);
+
+ mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
+ : sfVsyncPhaseOffsetNs,
+ earlyAppOffsetNs != -1 ? earlyAppOffsetNs
+ : vsyncPhaseOffsetNs};
+ mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
+ : sfVsyncPhaseOffsetNs,
+ earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
+ : vsyncPhaseOffsetNs};
+ mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+
+ mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+ : highFpsLateAppOffsetNs,
+ highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+ : highFpsLateSfOffsetNs};
+ mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+ : highFpsLateAppOffsetNs,
+ highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+ : highFpsLateSfOffsetNs};
+ mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
+ switch (mRefreshRateType) {
+ case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
+ return mHighRefreshRateOffsets;
+ default:
+ return mDefaultRefreshRateOffsets;
+ }
+}
+
+void PhaseOffsets::dump(std::string& result) const {
+ const auto [early, earlyGl, late] = getCurrentOffsets();
+ base::StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
+ "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
+ late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+}
+
+nsecs_t PhaseOffsets::getCurrentAppOffset() {
+ return getCurrentOffsets().late.app;
+}
+
+nsecs_t PhaseOffsets::getCurrentSfOffset() {
+ return getCurrentOffsets().late.sf;
+}
+
+} // namespace impl
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
new file mode 100644
index 0000000..cbcaade
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+
+#include "RefreshRateConfigs.h"
+#include "VSyncModulator.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class encapsulates offsets for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class PhaseOffsets {
+public:
+ struct Offsets {
+ VSyncModulator::Offsets early;
+ VSyncModulator::Offsets earlyGl;
+ VSyncModulator::Offsets late;
+ };
+
+ virtual ~PhaseOffsets();
+
+ virtual nsecs_t getCurrentAppOffset() = 0;
+ virtual nsecs_t getCurrentSfOffset() = 0;
+ virtual Offsets getCurrentOffsets() const = 0;
+ virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+class PhaseOffsets : public scheduler::PhaseOffsets {
+public:
+ PhaseOffsets();
+
+ nsecs_t getCurrentAppOffset() override;
+ nsecs_t getCurrentSfOffset() override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ Offsets getCurrentOffsets() const override;
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
+ mRefreshRateType = refreshRateType;
+ }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+private:
+ Offsets getmDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
+ Offsets getmHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
+
+ std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
+ RefreshRateConfigs::RefreshRateType::DEFAULT;
+
+ Offsets mDefaultRefreshRateOffsets;
+ Offsets mHighRefreshRateOffsets;
+};
+} // namespace impl
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
new file mode 100644
index 0000000..026f7c7
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <numeric>
+
+#include "android-base/stringprintf.h"
+
+#include "DisplayHardware/HWComposer.h"
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * This class is used to encapsulate configuration for refresh rates. It holds infomation
+ * about available refresh rates on the device, and the mapping between the numbers and human
+ * readable names.
+ */
+class RefreshRateConfigs {
+public:
+ // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
+ // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+ // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
+ enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+
+ struct RefreshRate {
+ // Type of the refresh rate.
+ RefreshRateType type;
+ // This config ID corresponds to the position of the config in the vector that is stored
+ // on the device.
+ int configId;
+ // Human readable name of the refresh rate.
+ std::string name;
+ // Refresh rate in frames per second, rounded to the nearest integer.
+ uint32_t fps = 0;
+ };
+
+ // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+ // baking them in.
+ explicit RefreshRateConfigs(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ init(configs);
+ }
+ ~RefreshRateConfigs() = default;
+
+ const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; }
+
+private:
+ void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
+ mRefreshRates.push_back(
+ RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+
+ if (configs.size() < 1) {
+ ALOGE("Device does not have valid configs. Config size is 0.");
+ return;
+ }
+
+ // Create a map between config index and vsync period. This is all the info we need
+ // from the configs.
+ std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
+ for (int i = 0; i < configs.size(); ++i) {
+ configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
+ }
+
+ std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
+ [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
+ return a.second > b.second;
+ });
+
+ // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
+ nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
+ if (vsyncPeriod != 0) {
+ const float fps = 1e9 / vsyncPeriod;
+ mRefreshRates.push_back(
+ RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first,
+ base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
+ }
+
+ if (configs.size() < 2) {
+ return;
+ }
+
+ // When the configs are ordered by the resync rate. We assume that the second one is
+ // PERFORMANCE, eg. the higher rate.
+ vsyncPeriod = configIdToVsyncPeriod[1].second;
+ if (vsyncPeriod != 0) {
+ const float fps = 1e9 / vsyncPeriod;
+ mRefreshRates.push_back(
+ RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first,
+ base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
+ }
+ }
+
+ std::vector<RefreshRate> mRefreshRates;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
new file mode 100644
index 0000000..dcb2988
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <numeric>
+
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/SchedulerUtils.h"
+#include "TimeStats/TimeStats.h"
+
+#include "android-base/stringprintf.h"
+#include "utils/Timers.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * Class to encapsulate statistics about refresh rates that the display is using. When the power
+ * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in
+ * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the
+ * hood (eg. the device is in DOZE, or screen off mode).
+ */
+class RefreshRateStats {
+ static constexpr int64_t MS_PER_S = 1000;
+ static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S;
+ static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
+ static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
+
+public:
+ explicit RefreshRateStats(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ const std::shared_ptr<TimeStats>& timeStats)
+ : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
+ mTimeStats(timeStats),
+ mPreviousRecordedTime(systemTime()) {}
+ ~RefreshRateStats() = default;
+
+ // Sets power mode. We only collect the information when the power mode is not
+ // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
+ // on config mode.
+ void setPowerMode(int mode) {
+ if (mCurrentPowerMode == mode) {
+ return;
+ }
+ // If power mode is normal, the time is going to be recorded under config modes.
+ if (mode == HWC_POWER_MODE_NORMAL) {
+ mCurrentPowerMode = mode;
+ return;
+ }
+ flushTime();
+ mCurrentPowerMode = mode;
+ }
+
+ // Sets config mode. If the mode has changed, it records how much time was spent in the previous
+ // mode.
+ void setConfigMode(int mode) {
+ if (mCurrentConfigMode == mode) {
+ return;
+ }
+ flushTime();
+ mCurrentConfigMode = mode;
+ }
+
+ // Returns a map between human readable refresh rate and number of seconds the device spent in
+ // that mode.
+ std::unordered_map<std::string, int64_t> getTotalTimes() {
+ // If the power mode is on, then we are probably switching between the config modes. If
+ // it's not then the screen is probably off. Make sure to flush times before printing
+ // them.
+ flushTime();
+
+ std::unordered_map<std::string, int64_t> totalTime;
+ for (auto config : mRefreshRateConfigs->getRefreshRates()) {
+ int64_t totalTimeForConfig = 0;
+ if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
+ totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+ }
+ totalTime[config.name] = totalTimeForConfig;
+ }
+ return totalTime;
+ }
+
+ // Traverses through the map of config modes and returns how long they've been running in easy
+ // to read format.
+ std::string doDump() {
+ std::ostringstream stream;
+ stream << "+ Refresh rate: running time in seconds\n";
+ for (auto stats : getTotalTimes()) {
+ stream << stats.first.c_str() << ": " << getDateFormatFromMs(stats.second) << "\n";
+ }
+ return stream.str();
+ }
+
+private:
+ void flushTime() {
+ // Normal power mode is counted under different config modes.
+ if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+ flushTimeForMode(mCurrentConfigMode);
+ } else {
+ flushTimeForMode(SCREEN_OFF_CONFIG_ID);
+ }
+ }
+
+ // Calculates the time that passed in ms between the last time we recorded time and the time
+ // this method was called.
+ void flushTimeForMode(int mode) {
+ nsecs_t currentTime = systemTime();
+ nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
+ int64_t timeElapsedMs = ns2ms(timeElapsed);
+ mPreviousRecordedTime = currentTime;
+
+ mConfigModesTotalTime[mode] += timeElapsedMs;
+ for (const auto& config : mRefreshRateConfigs->getRefreshRates()) {
+ if (config.configId == mode) {
+ mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+ }
+ }
+ }
+
+ // Formats the time in milliseconds into easy to read format.
+ static std::string getDateFormatFromMs(int64_t timeMs) {
+ auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+ auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
+ auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
+ auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
+ return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64
+ ".%03" PRId64,
+ days, hours, mins, sec, secRemainderMs);
+ }
+
+ // Keeps information about refresh rate configs that device has.
+ std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+
+ // Aggregate refresh rate statistics for telemetry.
+ std::shared_ptr<TimeStats> mTimeStats;
+
+ int64_t mCurrentConfigMode = 0;
+ int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+
+ std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+
+ nsecs_t mPreviousRecordedTime;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0d587dd..af439f7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,9 +28,7 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
-
#include <cutils/properties.h>
-#include <gui/ISurfaceComposer.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -42,11 +40,13 @@
#include "IdleTimer.h"
#include "InjectVSyncSource.h"
#include "SchedulerUtils.h"
+#include "SurfaceFlingerProperties.h"
namespace android {
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
#define RETURN_VALUE_IF_INVALID(value) \
if (handle == nullptr || mConnections.count(handle->id) == 0) return value
@@ -56,11 +56,8 @@
std::atomic<int64_t> Scheduler::sNextId = 0;
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
- : mHasSyncFramework(
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(true)),
- mDispSyncPresentTimeOffset(
- getInt64<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0)),
+ : mHasSyncFramework(running_without_sync_framework(true)),
+ mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false) {
// Note: We create a local temporary with the real DispSync implementation
@@ -78,46 +75,56 @@
if (mSetIdleTimerMs > 0) {
mIdleTimer =
std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+ [this] { resetTimerCallback(); },
[this] { expiredTimerCallback(); });
mIdleTimer->start();
}
}
-Scheduler::~Scheduler() = default;
+Scheduler::~Scheduler() {
+ // Ensure the IdleTimer thread is joined before we start destroying state.
+ mIdleTimer.reset();
+}
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
- const std::string& connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+ const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
const int64_t id = sNextId++;
ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
std::unique_ptr<EventThread> eventThread =
makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
- interceptCallback);
- auto connection = std::make_unique<Connection>(new ConnectionHandle(id),
- eventThread->createEventConnection(
- std::move(resyncCallback)),
- std::move(eventThread));
+ std::move(interceptCallback));
- mConnections.insert(std::make_pair(id, std::move(connection)));
+ auto eventThreadConnection =
+ createConnectionInternal(eventThread.get(), std::move(resyncCallback));
+ mConnections.emplace(id,
+ std::make_unique<Connection>(new ConnectionHandle(id),
+ eventThreadConnection,
+ std::move(eventThread)));
return mConnections[id]->handle;
}
std::unique_ptr<EventThread> Scheduler::makeEventThread(
- const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- const std::string sourceName = connectionName + "Source";
std::unique_ptr<VSyncSource> eventThreadSource =
- std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, sourceName.c_str());
- const std::string threadName = connectionName + "Thread";
- return std::make_unique<impl::EventThread>(std::move(eventThreadSource), interceptCallback,
- [this] { resetIdleTimer(); }, threadName.c_str());
+ std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+ return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
+ std::move(interceptCallback), connectionName);
+}
+
+sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
+ ResyncCallback&& resyncCallback) {
+ return eventThread->createEventConnection(std::move(resyncCallback),
+ [this] { resetIdleTimer(); });
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
RETURN_VALUE_IF_INVALID(nullptr);
- return mConnections[handle->id]->thread->createEventConnection(std::move(resyncCallback));
+ return createConnectionInternal(mConnections[handle->id]->thread.get(),
+ std::move(resyncCallback));
}
EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -131,9 +138,9 @@
}
void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
- EventThread::DisplayType displayType, bool connected) {
+ PhysicalDisplayId displayId, bool connected) {
RETURN_IF_INVALID();
- mConnections[handle->id]->thread->onHotplugReceived(displayType, connected);
+ mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
}
void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -183,9 +190,15 @@
}
void Scheduler::setVsyncPeriod(const nsecs_t period) {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
mPrimaryDispSync->reset();
mPrimaryDispSync->setPeriod(period);
- enableHardwareVsync();
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync->beginResync();
+ mEventControlThread->setVsyncEnabled(true);
+ mPrimaryHWVsyncEnabled = true;
+ }
}
void Scheduler::addResyncSample(const nsecs_t timestamp) {
@@ -221,6 +234,19 @@
mHWVsyncAvailable = makeAvailable;
}
+bool Scheduler::getHWSyncAvailable() {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ return mHWVsyncAvailable;
+}
+
+nsecs_t Scheduler::expectedPresentTime() {
+ return mPrimaryDispSync->expectedPresentTime();
+}
+
+void Scheduler::dumpPrimaryDispSync(std::string& result) const {
+ mPrimaryDispSync->dump(result);
+}
+
void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
const std::string layerName) {
// This is V1 logic. It calculates the average FPS based on the timestamp frequency
@@ -335,12 +361,14 @@
void Scheduler::resetIdleTimer() {
if (mIdleTimer) {
mIdleTimer->reset();
- ATRACE_INT("ExpiredIdleTimer", 0);
}
+}
+void Scheduler::resetTimerCallback() {
std::lock_guard<std::mutex> lock(mCallbackLock);
if (mResetTimerCallback) {
mResetTimerCallback();
+ ATRACE_INT("ExpiredIdleTimer", 0);
}
}
@@ -352,4 +380,10 @@
}
}
+std::string Scheduler::doDump() {
+ std::ostringstream stream;
+ stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
+ return stream.str();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ba18d21..d628e40 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -19,8 +19,8 @@
#include <cstdint>
#include <memory>
-#include <gui/ISurfaceComposer.h>
#include <ui/DisplayStatInfo.h>
+#include <ui/GraphicTypes.h>
#include "DispSync.h"
#include "EventControlThread.h"
@@ -72,12 +72,12 @@
virtual ~Scheduler();
/** Creates an EventThread connection. */
- sp<ConnectionHandle> createConnection(
- const std::string& connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
- impl::EventThread::InterceptVSyncsCallback interceptCallback);
+ sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
+ ResyncCallback,
+ impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
- ResyncCallback resyncCallback);
+ ResyncCallback);
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -85,7 +85,7 @@
sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
// Should be called when receiving a hotplug event.
- void hotplugReceived(const sp<ConnectionHandle>& handle, EventThread::DisplayType displayType,
+ void hotplugReceived(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
bool connected);
// Should be called after the screen is turned on.
@@ -109,6 +109,9 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
void makeHWSyncAvailable(bool makeAvailable);
+ // returns HWSyncAvailable flag to SF would enable HW vsync based on this
+ bool getHWSyncAvailable();
+ nsecs_t expectedPresentTime();
// Adds the present time for given layer to the history of present times.
void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
const std::string layerName);
@@ -118,13 +121,21 @@
void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
// Callback that gets invoked once the idle timer is reset.
void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
+ // Returns relevant information about Scheduler for dumpsys purposes.
+ std::string doDump();
+
+ // calls DispSync::dump() on primary disp sync
+ void dumpPrimaryDispSync(std::string& result) const;
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
- const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
impl::EventThread::InterceptVSyncsCallback interceptCallback);
private:
+ // Creates a connection on the given EventThread and forwards the given callbacks.
+ sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
+
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
// Collects the statistical mean (average) and median between timestamp
@@ -135,19 +146,17 @@
void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
// Function that resets the idle timer.
void resetIdleTimer();
+ // Function that is called when the timer resets.
+ void resetTimerCallback();
// Function that is called when the timer expires.
void expiredTimerCallback();
- // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
- // should make request to Scheduler to compute next refresh.
- friend class BufferQueueLayer;
-
// If fences from sync Framework are supported.
const bool mHasSyncFramework;
// The offset in nanoseconds to use, when DispSync timestamps present fence
// signaling time.
- const nsecs_t mDispSyncPresentTimeOffset;
+ nsecs_t mDispSyncPresentTimeOffset;
// Each connection has it's own ID. This variable keeps track of the count.
static std::atomic<int64_t> sNextId;
@@ -182,7 +191,7 @@
std::mutex mCallbackLock;
ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
- ResetIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
+ ExpiredIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 17c57db..edd23de 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -26,6 +26,11 @@
// about layers.
static constexpr size_t ARRAY_SIZE = 30;
+// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
+// the config is not visible to SF, and is completely maintained by HWC. However, we would
+// still like to keep track of time when the device is in this config.
+static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
template <typename T>
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index dde0c57..0bf3ceb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -18,9 +18,10 @@
#include <utils/Errors.h>
+#include <cinttypes>
#include <mutex>
-using namespace android::surfaceflinger;
+#include "Scheduler.h"
namespace android {
@@ -123,7 +124,7 @@
changed = true;
}
if (desired.app != current.app) {
- if (mSfConnectionHandle != nullptr) {
+ if (mAppConnectionHandle != nullptr) {
mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
} else {
mAppEventThread->setPhaseOffset(desired.app);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5df2876..85411b6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <sys/types.h>
@@ -28,6 +28,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <unordered_map>
#include <cutils/properties.h>
#include <log/log.h>
@@ -39,6 +40,8 @@
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <dvr/vr_flinger.h>
@@ -75,7 +78,6 @@
#include "ColorLayer.h"
#include "Colorizer.h"
#include "ContainerLayer.h"
-#include "DdmConnection.h"
#include "DisplayDevice.h"
#include "Layer.h"
#include "LayerVector.h"
@@ -111,17 +113,23 @@
#include <configstore/Utils.h>
#include <layerproto/LayerProtoParser.h>
+#include "SurfaceFlingerProperties.h"
namespace android {
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
+
using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
+using ui::DisplayPrimaries;
using ui::Hdr;
using ui::RenderIntent;
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
namespace {
#pragma clang diagnostic push
@@ -193,9 +201,23 @@
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
+constexpr float kSrgbRedX = 0.4123f;
+constexpr float kSrgbRedY = 0.2126f;
+constexpr float kSrgbRedZ = 0.0193f;
+constexpr float kSrgbGreenX = 0.3576f;
+constexpr float kSrgbGreenY = 0.7152f;
+constexpr float kSrgbGreenZ = 0.1192f;
+constexpr float kSrgbBlueX = 0.1805f;
+constexpr float kSrgbBlueY = 0.0722f;
+constexpr float kSrgbBlueZ = 0.9506f;
+constexpr float kSrgbWhiteX = 0.9505f;
+constexpr float kSrgbWhiteY = 1.0000f;
+constexpr float kSrgbWhiteZ = 1.0891f;
+
+constexpr float kDefaultRefreshRate = 60.f;
+constexpr float kPerformanceRefreshRate = 90.f;
+
// ---------------------------------------------------------------------------
-int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
-int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
uint64_t SurfaceFlinger::maxVirtualDisplaySize;
@@ -256,12 +278,12 @@
mLayersRemoved(false),
mLayersAdded(false),
mBootTime(systemTime()),
+ mPhaseOffsets{getFactory().createPhaseOffsets()},
mVisibleRegionsDirty(false),
mGeometryInvalid(false),
mAnimCompositionPending(false),
mBootStage(BootStage::BOOTLOADER),
mDebugRegion(0),
- mDebugDDMS(0),
mDebugDisableHWC(0),
mDebugDisableTransformHint(0),
mDebugInTransaction(0),
@@ -281,67 +303,46 @@
: SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
- vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+ hasSyncFramework = running_without_sync_framework(true);
- sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+ dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
- hasSyncFramework = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+ useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
- dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
-
- useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
-
- maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+ maxVirtualDisplaySize = max_virtual_display_dimension(0);
// Vr flinger is only enabled on Daydream ready devices.
- useVrFlinger = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::useVrFlinger>(false);
+ useVrFlinger = use_vr_flinger(false);
- maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+ maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
- hasWideColorDisplay =
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
- useColorManagement =
- getBool<V1_2::ISurfaceFlingerConfigs,
- &V1_2::ISurfaceFlingerConfigs::useColorManagement>(false);
+ hasWideColorDisplay = has_wide_color_display(false);
- auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
- if (surfaceFlingerConfigsServiceV1_2) {
- surfaceFlingerConfigsServiceV1_2->getCompositionPreference(
- [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
- auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
- defaultCompositionDataspace = tmpDefaultDataspace;
- defaultCompositionPixelFormat = tmpDefaultPixelFormat;
- wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
- wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
- });
- }
- mDefaultCompositionDataspace = defaultCompositionDataspace;
- mWideColorGamutCompositionDataspace = wideColorGamutCompositionDataspace;
+ useColorManagement = use_color_management(false);
- useContextPriority = getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::useContextPriority>(true);
+ mDefaultCompositionDataspace =
+ static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
+ mWideColorGamutCompositionDataspace =
+ static_cast<ui::Dataspace>(wcg_composition_dataspace(Dataspace::V0_SRGB));
+ defaultCompositionDataspace = mDefaultCompositionDataspace;
+ wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
+ defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
+ default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+ wideColorGamutCompositionPixelFormat =
+ static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
- V1_1::DisplayOrientation primaryDisplayOrientation =
- getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
- &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
- V1_1::DisplayOrientation::ORIENTATION_0);
+ useContextPriority = use_context_priority(true);
- switch (primaryDisplayOrientation) {
- case V1_1::DisplayOrientation::ORIENTATION_90:
+ auto tmpPrimaryDisplayOrientation = primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0);
+ switch (tmpPrimaryDisplayOrientation) {
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
break;
- case V1_1::DisplayOrientation::ORIENTATION_180:
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
break;
- case V1_1::DisplayOrientation::ORIENTATION_270:
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
break;
default:
@@ -350,9 +351,15 @@
}
ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
- mPrimaryDispSync =
- getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
- SurfaceFlinger::dispSyncPresentTimeOffset);
+ auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+ if (surfaceFlingerConfigsServiceV1_2) {
+ surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries(
+ [&](auto tmpPrimaries) {
+ memcpy(&mInternalDisplayPrimaries, &tmpPrimaries, sizeof(ui::DisplayPrimaries));
+ });
+ } else {
+ initDefaultDisplayNativePrimaries();
+ }
// debugging stuff...
char value[PROPERTY_VALUE_MAX];
@@ -363,16 +370,12 @@
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
- property_get("debug.sf.ddms", value, "0");
- mDebugDDMS = atoi(value);
- if (mDebugDDMS) {
- if (!startDdmConnection()) {
- // start failed, and DDMS debugging not enabled
- mDebugDDMS = 0;
- }
- }
ALOGI_IF(mDebugRegion, "showupdates enabled");
- ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
+
+ // DDMS debugging deprecated (b/120782499)
+ property_get("debug.sf.ddms", value, "0");
+ int debugDdms = atoi(value);
+ ALOGI_IF(debugDdms, "DDMS debugging not supported");
property_get("debug.sf.disable_backpressure", value, "0");
mPropagateBackpressure = !atoi(value);
@@ -390,29 +393,17 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
- property_get("debug.sf.early_phase_offset_ns", value, "-1");
- const int earlySfOffsetNs = atoi(value);
-
- property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
- const int earlyGlSfOffsetNs = atoi(value);
-
- property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
- const int earlyAppOffsetNs = atoi(value);
-
- property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
- const int earlyGlAppOffsetNs = atoi(value);
-
property_get("debug.sf.use_scheduler", value, "0");
mUseScheduler = atoi(value);
- const VSyncModulator::Offsets earlyOffsets =
- {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
- earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
- const VSyncModulator::Offsets earlyGlOffsets =
- {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
- earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
- mVsyncModulator.setPhaseOffsets(earlyOffsets, earlyGlOffsets,
- {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs});
+ if (!mUseScheduler) {
+ mPrimaryDispSync =
+ getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
+ SurfaceFlinger::dispSyncPresentTimeOffset);
+ }
+
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
@@ -509,21 +500,30 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
- std::optional<DisplayId> displayId;
+std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const {
+ Mutex::Autolock lock(mStateLock);
- if (id == HWC_DISPLAY_PRIMARY) {
- displayId = getInternalDisplayId();
- } else if (id == HWC_DISPLAY_EXTERNAL) {
- displayId = getExternalDisplayId();
+ const auto internalDisplayId = getInternalDisplayIdLocked();
+ if (!internalDisplayId) {
+ return {};
}
- if (!displayId) {
- ALOGE("%s: Invalid display %d", __FUNCTION__, id);
- return nullptr;
+ std::vector<PhysicalDisplayId> displayIds;
+ displayIds.reserve(mPhysicalDisplayTokens.size());
+ displayIds.push_back(internalDisplayId->value);
+
+ for (const auto& [id, token] : mPhysicalDisplayTokens) {
+ if (id != *internalDisplayId) {
+ displayIds.push_back(id.value);
+ }
}
- return getPhysicalDisplayToken(*displayId);
+ return displayIds;
+}
+
+sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
+ Mutex::Autolock lock(mStateLock);
+ return getPhysicalDisplayTokenLocked(DisplayId{displayId});
}
status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
@@ -619,7 +619,7 @@
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs);
+ ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
Mutex::Autolock _l(mStateLock);
@@ -627,16 +627,20 @@
// start the EventThread
if (mUseScheduler) {
- mScheduler = getFactory().createScheduler([this](bool enabled) {
- setVsyncEnabled(EventThread::DisplayType::Primary, enabled);
- });
+ mScheduler = getFactory().createScheduler(
+ [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+
+ // TODO(b/113612090): Currently we assume that if scheduler is turned on, then the refresh
+ // rate is 90. Once b/122905403 is completed, this should be updated accordingly.
+ mPhaseOffsets->setRefreshRateType(
+ scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
mAppConnectionHandle =
- mScheduler->createConnection("appConnection", SurfaceFlinger::vsyncPhaseOffsetNs,
+ mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
resyncCallback,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sfConnection", SurfaceFlinger::sfVsyncPhaseOffsetNs,
+ mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
resyncCallback, [this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
@@ -647,14 +651,14 @@
} else {
mEventThreadSource =
std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
- SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
+ mPhaseOffsets->getCurrentAppOffset(), true, "app");
mEventThread =
std::make_unique<impl::EventThread>(mEventThreadSource.get(),
impl::EventThread::InterceptVSyncsCallback(),
"appEventThread");
mSfEventThreadSource =
std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
- SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+ mPhaseOffsets->getCurrentSfOffset(), true, "sf");
mSFEventThread =
std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
@@ -713,8 +717,10 @@
}
}
- mEventControlThread = getFactory().createEventControlThread(
- [this](bool enabled) { setVsyncEnabled(EventThread::DisplayType::Primary, enabled); });
+ if (!mUseScheduler) {
+ mEventControlThread = getFactory().createEventControlThread(
+ [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+ }
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -735,8 +741,19 @@
}
if (mUseScheduler) {
- mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); });
- mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); });
+ mScheduler->setExpiredIdleTimerCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(RefreshRateType::DEFAULT);
+ });
+ mScheduler->setResetIdleTimerCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(RefreshRateType::PERFORMANCE);
+ });
+
+ mRefreshRateStats =
+ std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
+ *display->getId()),
+ mTimeStats);
}
ALOGV("Done initializing");
@@ -809,13 +826,13 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) {
+status_t SurfaceFlinger::getDisplayConfigsLocked(const sp<IBinder>& displayToken,
+ Vector<DisplayInfo>* configs) {
if (!displayToken || !configs) {
return BAD_VALUE;
}
- const auto displayId = getPhysicalDisplayId(displayToken);
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
}
@@ -839,8 +856,6 @@
configs->clear();
- ConditionalLock _l(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
DisplayInfo info = DisplayInfo();
@@ -853,7 +868,7 @@
info.viewportW = info.w;
info.viewportH = info.h;
- if (displayId == getInternalDisplayId()) {
+ if (displayId == getInternalDisplayIdLocked()) {
// The density of the device is provided by a build property
float density = Density::getBuildDensity() / 160.0f;
if (density == 0) {
@@ -889,7 +904,7 @@
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- info.appVsyncOffset = vsyncPhaseOffsetNs;
+ info.appVsyncOffset = mPhaseOffsets->getCurrentAppOffset();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -903,13 +918,13 @@
//
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
- info.presentationDeadline = hwConfig->getVsyncPeriod() -
- sfVsyncPhaseOffsetNs + 1000000;
+ info.presentationDeadline =
+ hwConfig->getVsyncPeriod() - mPhaseOffsets->getCurrentSfOffset() + 1000000;
// All non-virtual displays are currently considered secure.
info.secure = true;
- if (displayId == getInternalDisplayId() &&
+ if (displayId == getInternalDisplayIdLocked() &&
primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
std::swap(info.w, info.h);
}
@@ -944,29 +959,19 @@
return display->getActiveConfig();
}
-status_t SurfaceFlinger::setActiveConfigAsync(const sp<IBinder>& displayToken, int mode) {
- ATRACE_NAME("setActiveConfigAsync");
- postMessageAsync(new LambdaMessage(
- [=]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
- return NO_ERROR;
-}
+void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
- ATRACE_NAME("setActiveConfigSync");
- postMessageSync(new LambdaMessage(
- [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
- return NO_ERROR;
-}
-
-void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
Vector<DisplayInfo> configs;
- getDisplayConfigs(displayToken, &configs);
+ // Lock is acquired by setRefreshRateTo.
+ getDisplayConfigsLocked(displayToken, &configs);
if (mode < 0 || mode >= static_cast<int>(configs.size())) {
ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
return;
}
- const auto display = getDisplayDevice(displayToken);
+ // Lock is acquired by setRefreshRateTo.
+ const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set active config %d for invalid display token %p", mode,
displayToken.get());
@@ -982,20 +987,95 @@
return;
}
- int currentMode = display->getActiveConfig();
- if (mode == currentMode) {
- // Don't update config if we are already running in the desired mode.
+ // Don't check against the current mode yet. Worst case we set the desired
+ // config twice.
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+ }
+ // This will trigger HWC refresh without resetting the idle timer.
+ repaintEverythingForHWC();
+}
+
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
+ postMessageSync(new LambdaMessage(
+ [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); }));
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setActiveConfigInHWC() {
+ ATRACE_CALL();
+
+ const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
+ if (!display) {
return;
}
-
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
- display->setActiveConfig(mode);
- getHwComposer().setActiveConfig(*displayId, mode);
+ ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+ getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+ mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+}
- ATRACE_INT("ActiveConfigMode", mode);
+void SurfaceFlinger::setActiveConfigInternal() {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mUseScheduler) {
+ mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
+ }
+
+ const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
+ display->setActiveConfig(mUpcomingActiveConfig.configId);
+
+ mSetActiveConfigState = SetActiveConfigState::NONE;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+
resyncToHardwareVsync(true, getVsyncPeriod());
+ ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+}
+
+bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
+ // Store the local variable to release the lock.
+ ActiveConfigInfo desiredActiveConfig;
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ desiredActiveConfig = mDesiredActiveConfig;
+ }
+
+ const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
+ if (display) {
+ if (mSetActiveConfigState == SetActiveConfigState::NONE &&
+ display->getActiveConfig() != desiredActiveConfig.configId) {
+ // Step 1) Desired active config was set, it is different than the
+ // config currently in use. Notify HWC.
+ mUpcomingActiveConfig = desiredActiveConfig;
+ setActiveConfigInHWC();
+ } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
+ // Step 2) HWC was notified and we received refresh from it.
+ mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+ repaintEverythingForHWC();
+ // We do not want to give another frame to HWC while we are transitioning.
+ return false;
+ } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
+ !(mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
+ // Step 3) We received the present fence from the HWC, so we assume it
+ // successfully updated the config, hence we update SF.
+ setActiveConfigInternal();
+ // If the config changed again while we were transitioning, restart the
+ // process.
+ if (desiredActiveConfig != mUpcomingActiveConfig) {
+ mUpcomingActiveConfig = desiredActiveConfig;
+ setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
+ }
+ }
+ }
+ return true;
}
status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1004,15 +1084,15 @@
return BAD_VALUE;
}
- const auto displayId = getPhysicalDisplayId(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
-
std::vector<ColorMode> modes;
{
- ConditionalLock _l(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ return NAME_NOT_FOUND;
+ }
+
modes = getHwComposer().getColorModes(*displayId);
}
outColorModes->clear();
@@ -1021,6 +1101,21 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ // Currently we only support this API for a single internal display.
+ if (getInternalDisplayToken() != displayToken) {
+ return BAD_VALUE;
+ }
+
+ memcpy(&primaries, &mInternalDisplayPrimaries, sizeof(ui::DisplayPrimaries));
+ return NO_ERROR;
+}
+
ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
if (const auto display = getDisplayDevice(displayToken)) {
return display->getCompositionDisplay()->getState().colorMode;
@@ -1138,17 +1233,17 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
- int32_t* outBufferId) {
- if (!outBufferId) {
+status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken,
+ bool* outIsWideColorDisplay) const {
+ if (!displayToken || !outIsWideColorDisplay) {
return BAD_VALUE;
}
- *outBufferId = mBufferStateLayerCache.add(token, buffer);
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
- mBufferStateLayerCache.release(token, bufferId);
+ Mutex::Autolock _l(mStateLock);
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return BAD_VALUE;
+ }
+ *outIsWideColorDisplay = display->hasWideColorGamut();
return NO_ERROR;
}
@@ -1229,6 +1324,17 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addRegionSamplingListener(
+ const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/,
+ const sp<IRegionSamplingListener>& /*listener*/) {
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& /*listener*/) {
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1239,16 +1345,15 @@
});
if (mUseScheduler) {
- if (vsyncSource == eVsyncSourceSurfaceFlinger) {
- return mScheduler->createDisplayEventConnection(mSfConnectionHandle, resyncCallback);
- } else {
- return mScheduler->createDisplayEventConnection(mAppConnectionHandle, resyncCallback);
- }
+ const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle
+ : mAppConnectionHandle;
+
+ return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
} else {
if (vsyncSource == eVsyncSourceSurfaceFlinger) {
- return mSFEventThread->createEventConnection(resyncCallback);
+ return mSFEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback());
} else {
- return mEventThread->createEventConnection(resyncCallback);
+ return mEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback());
}
}
}
@@ -1293,7 +1398,7 @@
}
nsecs_t SurfaceFlinger::getVsyncPeriod() const {
- const auto displayId = getInternalDisplayId();
+ const auto displayId = getInternalDisplayIdLocked();
if (!displayId || !getHwComposer().isConnected(*displayId)) {
return 0;
}
@@ -1303,6 +1408,7 @@
}
void SurfaceFlinger::enableHardwareVsync() {
+ assert(!mUseScheduler);
Mutex::Autolock _l(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
mPrimaryDispSync->beginResync();
@@ -1313,17 +1419,23 @@
void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
Mutex::Autolock _l(mHWVsyncLock);
-
- if (makeAvailable) {
- mHWVsyncAvailable = true;
- // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
- if (mUseScheduler) {
+ // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
+ if (mUseScheduler) {
+ if (makeAvailable) {
mScheduler->makeHWSyncAvailable(true);
+ } else if (!mScheduler->getHWSyncAvailable()) {
+ // Hardware vsync is not currently available, so abort the resync
+ // attempt for now
+ return;
}
- } else if (!mHWVsyncAvailable) {
- // Hardware vsync is not currently available, so abort the resync
- // attempt for now
- return;
+ } else {
+ if (makeAvailable) {
+ mHWVsyncAvailable = true;
+ } else if (!mHWVsyncAvailable) {
+ // Hardware vsync is not currently available, so abort the resync
+ // attempt for now
+ return;
+ }
}
if (period <= 0) {
@@ -1345,6 +1457,7 @@
}
void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
+ assert(!mUseScheduler);
Mutex::Autolock _l(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
mEventControlThread->setVsyncEnabled(false);
@@ -1410,29 +1523,39 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::setRefreshRateTo(float newFps) {
- const auto displayId = getInternalDisplayId();
- if (!displayId || mBootStage != BootStage::FINISHED) {
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
+ mPhaseOffsets->setRefreshRateType(refreshRate);
+
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
+
+ if (mBootStage != BootStage::FINISHED) {
return;
}
+
// TODO(b/113612090): There should be a message queue flush here. Because this esentially
// runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
// manner, once the setActiveConfig is synchronous, and is executed at a known time in a
// refresh cycle.
// Don't do any updating if the current fps is the same as the new one.
- const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
- const nsecs_t currentVsyncPeriod = activeConfig->getVsyncPeriod();
+ const nsecs_t currentVsyncPeriod = getVsyncPeriod();
if (currentVsyncPeriod == 0) {
return;
}
+
// TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
// floating numbers.
const float currentFps = 1e9 / currentVsyncPeriod;
+ const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate
+ : kDefaultRefreshRate;
if (std::abs(currentFps - newFps) <= 1) {
return;
}
+ const auto displayId = getInternalDisplayIdLocked();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
auto configs = getHwComposer().getConfigs(*displayId);
for (int i = 0; i < configs.size(); i++) {
const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
@@ -1443,12 +1566,7 @@
// TODO(b/113612090): There should be a better way at determining which config
// has the right refresh rate.
if (std::abs(fps - newFps) <= 1) {
- const auto display = getBuiltInDisplay(HWC_DISPLAY_PRIMARY);
- if (!display) return;
- // This is posted in async function to avoid deadlock when getDisplayDevice
- // requires mStateLock.
- setActiveConfigAsync(display, i);
- ATRACE_INT("FPS", newFps);
+ setDesiredActiveConfig(getInternalDisplayTokenLocked(), i);
}
}
}
@@ -1487,10 +1605,10 @@
repaintEverythingForHWC();
}
-void SurfaceFlinger::setVsyncEnabled(EventThread::DisplayType /*displayType*/, bool enabled) {
+void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
Mutex::Autolock lock(mStateLock);
- if (const auto displayId = getInternalDisplayId()) {
+ if (const auto displayId = getInternalDisplayIdLocked()) {
getHwComposer().setVsyncEnabled(*displayId,
enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
}
@@ -1592,14 +1710,16 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ if (!updateSetActiveConfigStateMachine()) {
+ break;
+ }
+
if (mUseScheduler) {
// This call is made each time SF wakes up and creates a new frame.
mScheduler->incrementFrameCounter();
}
- bool frameMissed = !mHadClientComposition &&
- mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getSignalTime() ==
- Fence::SIGNAL_TIME_PENDING);
+ bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
mFrameMissedCount += frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
if (frameMissed) {
@@ -1617,6 +1737,10 @@
bool refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
+
+ updateCursorAsync();
+ updateInputFlinger();
+
refreshNeeded |= mRepaintEverything;
if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
// Signal a refresh if a transaction modified the window state,
@@ -1711,7 +1835,19 @@
bool SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL();
- return handlePageFlip();
+ bool refreshNeeded = handlePageFlip();
+
+ if (mVisibleRegionsDirty) {
+ computeLayerBounds();
+ }
+
+ for (auto& layer : mLayersPendingRefresh) {
+ Region visibleReg;
+ visibleReg.set(layer->getScreenBounds());
+ invalidateLayerStack(layer, visibleReg);
+ }
+ mLayersPendingRefresh.clear();
+ return refreshNeeded;
}
void SurfaceFlinger::calculateWorkingSet() {
@@ -1832,16 +1968,13 @@
if (displayState.isEnabled) {
// transform the dirty region into this screen's coordinate space
- const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
+ const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
if (!dirtyRegion.isEmpty()) {
+ base::unique_fd readyFence;
// redraw the whole screen
- doComposeSurfaces(displayDevice);
+ doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
- // and draw the dirty region
- auto& engine(getRenderEngine());
- engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
-
- display->getRenderSurface()->queueBuffer();
+ display->getRenderSurface()->queueBuffer(std::move(readyFence));
}
}
@@ -1925,11 +2058,11 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0)
- ? (stats.vsyncPeriod - (sfVsyncPhaseOffsetNs % stats.vsyncPeriod))
- : ((-sfVsyncPhaseOffsetNs) % stats.vsyncPeriod);
+ nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
+ ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
+ : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
- // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+ // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -1938,7 +2071,7 @@
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+ // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
// with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
@@ -2189,6 +2322,21 @@
}
#pragma clang optimize on // b/119477596
+void SurfaceFlinger::computeLayerBounds() {
+ for (const auto& pair : mDisplays) {
+ const auto& displayDevice = pair.second;
+ const auto display = displayDevice->getCompositionDisplay();
+ for (const auto& layer : mDrawingState.layersSortedByZ) {
+ // only consider the layers on the given layer stack
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+ continue;
+ }
+
+ layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+ }
+ }
+}
+
void SurfaceFlinger::rebuildLayerStacks() {
ATRACE_CALL();
ALOGV("rebuildLayerStacks");
@@ -2216,7 +2364,8 @@
const auto& displayState = display->getState();
Region opaqueRegion;
Region dirtyRegion;
- Vector<sp<Layer>> layersSortedByZ;
+ compositionengine::Output::OutputLayers layersSortedByZ;
+ Vector<sp<Layer>> deprecated_layersSortedByZ;
Vector<sp<Layer>> layersNeedingFences;
const ui::Transform& tr = displayState.transform;
const Rect bounds = displayState.bounds;
@@ -2224,14 +2373,27 @@
computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto compositionLayer = layer->getCompositionLayer();
+ if (compositionLayer == nullptr) {
+ return;
+ }
+
bool hwcLayerDestroyed = false;
const auto displayId = displayDevice->getId();
- if (display->belongsInOutput(layer->getLayerStack())) {
+ sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
+ LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr);
+
+ if (display->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
drawRegion.andSelf(bounds);
if (!drawRegion.isEmpty()) {
- layersSortedByZ.add(layer);
+ layersSortedByZ.emplace_back(
+ display->getOrCreateOutputLayer(layer->getCompositionLayer(),
+ layerFE));
+
+ deprecated_layersSortedByZ.add(layer);
} else {
// Clear out the HWC layer if this layer was
// previously visible, but no longer is
@@ -2257,7 +2419,10 @@
}
});
}
- displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
+
+ display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
+
+ displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ);
displayDevice->setLayersNeedingFences(layersNeedingFences);
Region undefinedRegion{bounds};
@@ -2298,10 +2463,12 @@
break;
case Dataspace::BT2020_PQ:
case Dataspace::BT2020_ITU_PQ:
+ bestDataSpace = Dataspace::DISPLAY_P3;
*outHdrDataSpace = Dataspace::BT2020_PQ;
break;
case Dataspace::BT2020_HLG:
case Dataspace::BT2020_ITU_HLG:
+ bestDataSpace = Dataspace::DISPLAY_P3;
// When there's mixed PQ content and HLG content, we set the HDR
// data space to be BT2020_PQ and convert HLG to PQ.
if (*outHdrDataSpace == Dataspace::UNKNOWN) {
@@ -2359,7 +2526,7 @@
auto display = displayDevice->getCompositionDisplay();
const auto& displayState = display->getState();
- bool dirty = !display->getPhysicalSpaceDirtyRegion(false).isEmpty();
+ bool dirty = !display->getDirtyRegion(false).isEmpty();
bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
@@ -2410,7 +2577,7 @@
if (displayState.isEnabled) {
// transform the dirty region into this screen's coordinate space
- const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
+ const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
// repaint the framebuffer (if needed)
doDisplayComposition(displayDevice, dirtyRegion);
@@ -2558,6 +2725,16 @@
mPendingHotplugEvents.clear();
}
+void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+ if (mUseScheduler) {
+ mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected);
+ mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected);
+ } else {
+ mEventThread->onHotplugReceived(displayId, connected);
+ mSFEventThread->onHotplugReceived(displayId, connected);
+ }
+}
+
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
@@ -2570,7 +2747,7 @@
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
- const bool isInternalDisplay = displayId && displayId == getInternalDisplayId();
+ const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
creationArgs.isPrimary = isInternalDisplay;
if (useColorManagement && displayId) {
@@ -2653,29 +2830,18 @@
for (size_t i = 0; i < dc;) {
const ssize_t j = curr.indexOfKey(draw.keyAt(i));
if (j < 0) {
- // Save display IDs before disconnecting.
- const auto internalDisplayId = getInternalDisplayId();
- const auto externalDisplayId = getExternalDisplayId();
-
// 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 (internalDisplayId && internalDisplayId == draw[i].displayId) {
- if (mUseScheduler) {
- mScheduler->hotplugReceived(mAppConnectionHandle,
- EventThread::DisplayType::Primary, false);
- } else {
- mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
- }
- } else if (externalDisplayId && externalDisplayId == draw[i].displayId) {
- if (mUseScheduler) {
- mScheduler->hotplugReceived(mAppConnectionHandle,
- EventThread::DisplayType::External, false);
- } else {
- mEventThread->onHotplugReceived(EventThread::DisplayType::External, false);
+
+ if (!display->isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, false);
}
}
+
mDisplays.erase(draw.keyAt(i));
} else {
// this display is in both lists. see if something changed.
@@ -2778,26 +2944,7 @@
dispSurface, producer));
if (!state.isVirtual()) {
LOG_ALWAYS_FATAL_IF(!displayId);
-
- if (displayId == getInternalDisplayId()) {
- if (mUseScheduler) {
- mScheduler->hotplugReceived(mAppConnectionHandle,
- EventThread::DisplayType::Primary,
- true);
- } else {
- mEventThread->onHotplugReceived(EventThread::DisplayType::Primary,
- true);
- }
- } else if (displayId == getExternalDisplayId()) {
- if (mUseScheduler) {
- mScheduler->hotplugReceived(mAppConnectionHandle,
- EventThread::DisplayType::External,
- true);
- } else {
- mEventThread->onHotplugReceived(EventThread::DisplayType::External,
- true);
- }
- }
+ dispatchDisplayHotplugEvent(displayId->value, true);
}
}
}
@@ -2819,7 +2966,6 @@
* (perform the transaction for each of them if needed)
*/
- bool inputChanged = false;
if (transactionFlags & eTraversalNeeded) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
@@ -2830,7 +2976,7 @@
mVisibleRegionsDirty = true;
if (flags & Layer::eInputInfoChanged) {
- inputChanged = true;
+ mInputInfoChanged = true;
}
});
}
@@ -2879,7 +3025,9 @@
// if not, pick the only display it's on.
hintDisplay = nullptr;
for (const auto& [token, display] : mDisplays) {
- if (display->getCompositionDisplay()->belongsInOutput(layer->getLayerStack())) {
+ if (display->getCompositionDisplay()
+ ->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
if (hintDisplay) {
hintDisplay = nullptr;
break;
@@ -2929,33 +3077,31 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (mLayersPendingRemoval.indexOf(layer) >= 0) {
// this layer is not visible anymore
- // TODO: we could traverse the tree from front to back and
- // compute the actual visible region
- // TODO: we could cache the transformed region
Region visibleReg;
- visibleReg.set(layer->computeScreenBounds());
+ visibleReg.set(layer->getScreenBounds());
invalidateLayerStack(layer, visibleReg);
}
});
}
commitTransaction();
-
- if (inputChanged || mVisibleRegionsDirty) {
- updateInputWindows();
- }
- executeInputWindowCommands();
-
- updateCursorAsync();
}
-void SurfaceFlinger::updateInputWindows() {
+void SurfaceFlinger::updateInputFlinger() {
ATRACE_CALL();
-
- if (mInputFlinger == nullptr) {
+ if (!mInputFlinger) {
return;
}
+ if (mVisibleRegionsDirty || mInputInfoChanged) {
+ mInputInfoChanged = false;
+ updateInputWindowInfo();
+ }
+
+ executeInputWindowCommands();
+}
+
+void SurfaceFlinger::updateInputWindowInfo() {
Vector<InputWindowInfo> inputHandles;
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
@@ -2969,10 +3115,6 @@
}
void SurfaceFlinger::executeInputWindowCommands() {
- if (!mInputFlinger) {
- return;
- }
-
for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) {
if (transferTouchFocusCommand.fromToken != nullptr &&
transferTouchFocusCommand.toToken != nullptr &&
@@ -3000,7 +3142,12 @@
void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
if (layer->hasReadyFrame()) {
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
if (layer->shouldPresentNow(expectedPresentTime)) {
bool ignored = false;
layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
@@ -3063,7 +3210,7 @@
const Layer::State& s(layer->getDrawingState());
// only consider the layers on the given layer stack
- if (!display->belongsInOutput(layer->getLayerStack())) {
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
return;
}
@@ -3100,7 +3247,7 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
- Rect bounds(layer->computeScreenBounds());
+ Rect bounds(layer->getScreenBounds());
visibleRegion.set(bounds);
ui::Transform tr = layer->getTransform();
@@ -3189,12 +3336,27 @@
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
- if (display->belongsInOutput(layer->getLayerStack())) {
+ if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
display->editState().dirtyRegion.orSelf(dirty);
}
}
}
+void SurfaceFlinger::initDefaultDisplayNativePrimaries() {
+ mInternalDisplayPrimaries.red.X = kSrgbRedX;
+ mInternalDisplayPrimaries.red.Y = kSrgbRedY;
+ mInternalDisplayPrimaries.red.Z = kSrgbRedZ;
+ mInternalDisplayPrimaries.green.X = kSrgbGreenX;
+ mInternalDisplayPrimaries.green.Y = kSrgbGreenY;
+ mInternalDisplayPrimaries.green.Z = kSrgbGreenZ;
+ mInternalDisplayPrimaries.blue.X = kSrgbBlueX;
+ mInternalDisplayPrimaries.blue.Y = kSrgbBlueY;
+ mInternalDisplayPrimaries.blue.Z = kSrgbBlueZ;
+ mInternalDisplayPrimaries.white.X = kSrgbWhiteX;
+ mInternalDisplayPrimaries.white.Y = kSrgbWhiteY;
+ mInternalDisplayPrimaries.white.Z = kSrgbWhiteZ;
+}
+
bool SurfaceFlinger::handlePageFlip()
{
ALOGV("handlePageFlip");
@@ -3217,7 +3379,12 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
@@ -3234,9 +3401,10 @@
Mutex::Autolock lock(mStateLock);
for (auto& layer : mLayersWithQueuedFrames) {
- const Region dirty(layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence));
+ if (layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence)) {
+ mLayersPendingRefresh.push_back(layer);
+ }
layer->useSurfaceDamage();
- invalidateLayerStack(layer, dirty);
if (layer->isBufferLatched()) {
newDataLatched = true;
}
@@ -3250,11 +3418,6 @@
mVisibleRegionsDirty |= visibleRegions;
- if (visibleRegions) {
- // Update input window info if the layer receives its first buffer.
- updateInputWindows();
- }
-
// If we will need to wake up at some time in the future to deal with a
// queued frame that shouldn't be displayed during this vsync period, wake
// up during the next vsync period to check again.
@@ -3280,7 +3443,6 @@
void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
const Region& inDirtyRegion) {
auto display = displayDevice->getCompositionDisplay();
-
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
@@ -3291,13 +3453,16 @@
}
ALOGV("doDisplayComposition");
- if (!doComposeSurfaces(displayDevice)) return;
+ base::unique_fd readyFence;
+ if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
// swap buffers (presentation)
- display->getRenderSurface()->queueBuffer();
+ display->getRenderSurface()->queueBuffer(std::move(readyFence));
}
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
+ const Region& debugRegion, base::unique_fd* readyFence) {
+ ATRACE_CALL();
ALOGV("doComposeSurfaces");
auto display = displayDevice->getCompositionDisplay();
@@ -3312,13 +3477,14 @@
mat4 colorMatrix;
bool applyColorMatrix = false;
- // Framebuffer will live in this scope for GPU composition.
- std::unique_ptr<renderengine::BindNativeBufferAsFramebuffer> fbo;
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<renderengine::LayerSettings> clientCompositionLayers;
+ sp<GraphicBuffer> buf;
if (hasClientComposition) {
ALOGV("hasClientComposition");
- sp<GraphicBuffer> buf = display->getRenderSurface()->dequeueBuffer();
+ buf = display->getRenderSurface()->dequeueBuffer();
if (buf == nullptr) {
ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
@@ -3327,24 +3493,30 @@
return false;
}
- // Bind the framebuffer in this scope.
- fbo = std::make_unique<renderengine::BindNativeBufferAsFramebuffer>(getRenderEngine(),
- buf->getNativeBuffer());
+ clientCompositionDisplay.physicalDisplay = displayState.scissor;
+ clientCompositionDisplay.clip = displayState.scissor;
+ const ui::Transform& displayTransform = displayState.transform;
+ mat4 m;
+ m[0][0] = displayTransform[0][0];
+ m[0][1] = displayTransform[0][1];
+ m[0][3] = displayTransform[0][2];
+ m[1][0] = displayTransform[1][0];
+ m[1][1] = displayTransform[1][1];
+ m[1][3] = displayTransform[1][2];
+ m[3][0] = displayTransform[2][0];
+ m[3][1] = displayTransform[2][1];
+ m[3][3] = displayTransform[2][2];
- if (fbo->getStatus() != NO_ERROR) {
- ALOGW("Binding buffer for display [%s] failed with status: %d",
- displayDevice->getDisplayName().c_str(), fbo->getStatus());
- return false;
- }
+ clientCompositionDisplay.globalTransform = m;
const auto* profile = display->getDisplayColorProfile();
Dataspace outputDataspace = Dataspace::UNKNOWN;
if (profile->hasWideColorGamut()) {
outputDataspace = displayState.dataspace;
}
- getRenderEngine().setOutputDataSpace(outputDataspace);
- getRenderEngine().setDisplayMaxLuminance(
- profile->getHdrCapabilities().getDesiredMaxLuminance());
+ clientCompositionDisplay.outputDataspace = outputDataspace;
+ clientCompositionDisplay.maxLuminance =
+ profile->getHdrCapabilities().getDesiredMaxLuminance();
const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
const bool skipClientColorTransform =
@@ -3355,44 +3527,7 @@
// Compute the global color transform matrix.
applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
if (applyColorMatrix) {
- colorMatrix = mDrawingState.colorMatrix;
- }
-
- display->getRenderSurface()->setViewportAndProjection();
-
- // Never touch the framebuffer if we don't have any framebuffer layers
- if (hasDeviceComposition) {
- // when using overlays, we assume a fully transparent framebuffer
- // NOTE: we could reduce how much we need to clear, for instance
- // remove where there are opaque FB layers. however, on some
- // GPUs doing a "clean slate" clear might be more efficient.
- // We'll revisit later if needed.
- getRenderEngine().clearWithColor(0, 0, 0, 0);
- } else {
- // we start with the whole screen area and remove the scissor part
- // we're left with the letterbox region
- // (common case is that letterbox ends-up being empty)
- const Region letterbox = bounds.subtract(displayState.scissor);
-
- // compute the area to clear
- const Region region = displayState.undefinedRegion.merge(letterbox);
-
- // screen is already cleared here
- if (!region.isEmpty()) {
- // can happen with SurfaceView
- drawWormhole(region);
- }
- }
-
- const Rect& bounds = displayState.bounds;
- const Rect& scissor = displayState.scissor;
- if (scissor != bounds) {
- // scissor doesn't match the screen's dimensions, so we
- // need to clear everything outside of it and enable
- // the GL scissor so we don't draw anything where we shouldn't
-
- // enable scissor for this frame
- getRenderEngine().setScissor(scissor);
+ clientCompositionDisplay.colorTransform = colorMatrix;
}
}
@@ -3401,11 +3536,11 @@
*/
ALOGV("Rendering client layers");
- const ui::Transform& displayTransform = displayState.transform;
bool firstLayer = true;
+ Region clearRegion = Region::INVALID_REGION;
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- const Region clip(bounds.intersect(
- displayTransform.transform(layer->visibleRegion)));
+ const Region viewportRegion(displayState.viewport);
+ const Region clip(viewportRegion.intersect(layer->visibleRegion));
ALOGV("Layer: %s", layer->getName().string());
ALOGV(" Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str());
if (!clip.isEmpty()) {
@@ -3421,22 +3556,28 @@
layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(renderArea);
+ renderengine::LayerSettings layerSettings;
+ Region dummyRegion;
+ bool prepared = layer->prepareClientLayer(renderArea, clip, dummyRegion,
+ layerSettings);
+
+ if (prepared) {
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+ layerSettings.alpha = half(0.0);
+ layerSettings.disableBlending = true;
+ clientCompositionLayers.push_back(layerSettings);
+ }
}
break;
}
case HWC2::Composition::Client: {
- if (layer->hasColorTransform()) {
- mat4 tmpMatrix;
- if (applyColorMatrix) {
- tmpMatrix = mDrawingState.colorMatrix;
- }
- tmpMatrix *= layer->getColorTransform();
- getRenderEngine().setColorTransform(tmpMatrix);
- } else {
- getRenderEngine().setColorTransform(colorMatrix);
+ renderengine::LayerSettings layerSettings;
+ bool prepared =
+ layer->prepareClientLayer(renderArea, clip, clearRegion, layerSettings);
+ if (prepared) {
+ clientCompositionLayers.push_back(layerSettings);
}
- layer->draw(renderArea, clip);
break;
}
default:
@@ -3450,12 +3591,22 @@
// Perform some cleanup steps if we used client composition.
if (hasClientComposition) {
- getRenderEngine().setColorTransform(mat4());
- getRenderEngine().disableScissor();
- display->getRenderSurface()->finishBuffer();
- // Clear out error flags here so that we don't wait until next
- // composition to log.
- getRenderEngine().checkErrors();
+ clientCompositionDisplay.clearRegion = clearRegion;
+ if (!debugRegion.isEmpty()) {
+ Region::const_iterator it = debugRegion.begin();
+ Region::const_iterator end = debugRegion.end();
+ while (it != end) {
+ const Rect& rect = *it++;
+ renderengine::LayerSettings layerSettings;
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+ layerSettings.geometry.boundaries = rect.toFloatRect();
+ layerSettings.alpha = half(1.0);
+ clientCompositionLayers.push_back(layerSettings);
+ }
+ }
+ getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers,
+ buf->getNativeBuffer(), readyFence);
}
return true;
}
@@ -3574,7 +3725,12 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states) {
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -3833,6 +3989,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eBackgroundColorChanged) {
+ if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eMatrixChanged) {
// TODO: b/109894387
//
@@ -3939,9 +4100,6 @@
if (what & layer_state_t::eFrameChanged) {
if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBufferChanged) {
- if (layer->setBuffer(s.buffer)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eAcquireFenceChanged) {
if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
}
@@ -3961,8 +4119,15 @@
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eInputInfoChanged) {
- layer->setInputInfo(s.inputInfo);
- flags |= eTraversalNeeded;
+ if (callingThreadHasUnscopedSurfaceFlingerAccess()) {
+ layer->setInputInfo(s.inputInfo);
+ flags |= eTraversalNeeded;
+ } else {
+ ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
+ }
+ }
+ if (what & layer_state_t::eMetadataChanged) {
+ if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
std::vector<sp<CallbackHandle>> callbackHandles;
if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
@@ -3971,9 +4136,16 @@
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
+
+ if (what & layer_state_t::eBufferChanged) {
+ // Add the new buffer to the cache. This should always come before eCachedBufferChanged.
+ BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.bufferId,
+ s.buffer);
+ }
if (what & layer_state_t::eCachedBufferChanged) {
sp<GraphicBuffer> buffer =
- mBufferStateLayerCache.get(s.cachedBuffer.token, s.cachedBuffer.bufferId);
+ BufferStateLayerCache::getInstance().get(s.cachedBuffer.token,
+ s.cachedBuffer.bufferId);
if (layer->setBuffer(buffer)) flags |= eTraversalNeeded;
}
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
@@ -3992,13 +4164,10 @@
return flags;
}
-status_t SurfaceFlinger::createLayer(
- const String8& name,
- const sp<Client>& client,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
-{
+status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
+ uint32_t h, PixelFormat format, uint32_t flags,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -4054,12 +4223,15 @@
// window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
// TODO b/64227542
- if (windowType == 441731) {
- windowType = 2024; // TYPE_NAVIGATION_BAR_PANEL
- layer->setPrimaryDisplayOnly();
+ if (metadata.has(METADATA_WINDOW_TYPE)) {
+ int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ if (windowType == 441731) {
+ metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL
+ layer->setPrimaryDisplayOnly();
+ }
}
- layer->setInfo(windowType, ownerUid);
+ layer->setMetadata(std::move(metadata));
bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
result = addClientLayer(client, *handle, *gbp, layer, *parent,
@@ -4323,6 +4495,10 @@
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
+ if (mUseScheduler && mRefreshRateStats) {
+ // Update refresh rate stats.
+ mRefreshRateStats->setPowerMode(mode);
+ }
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4344,8 +4520,8 @@
// ---------------------------------------------------------------------------
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto)
- NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
+ bool asProto) NO_THREAD_SAFETY_ANALYSIS {
std::string result;
IPCThreadState* ipc = IPCThreadState::self();
@@ -4369,114 +4545,42 @@
strerror(-err), err);
}
- bool dumpAll = true;
- size_t index = 0;
- size_t numArgs = args.size();
+ using namespace std::string_literals;
- if (numArgs) {
- if ((index < numArgs) &&
- (args[index] == String16("--list"))) {
- index++;
- listLayersLocked(args, index, result);
- dumpAll = false;
- }
+ static const std::unordered_map<std::string, Dumper> dumpers = {
+ {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
+ {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
+ {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+ {"--dispsync"s, dumper([this](std::string& s) {
+ if (mUseScheduler) {
+ mScheduler->dumpPrimaryDispSync(s);
+ } else {
+ mPrimaryDispSync->dump(s);
+ }
+ })},
+ {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
+ {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+ {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
+ {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
+ {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+ {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+ {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+ {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+ {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ };
- if ((index < numArgs) &&
- (args[index] == String16("--latency"))) {
- index++;
- dumpStatsLocked(args, index, result);
- dumpAll = false;
- }
+ const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
- if ((index < numArgs) &&
- (args[index] == String16("--latency-clear"))) {
- index++;
- clearStatsLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dispsync"))) {
- index++;
- mPrimaryDispSync->dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--static-screen"))) {
- index++;
- dumpStaticScreenStats(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--frame-events"))) {
- index++;
- dumpFrameEventsLocked(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
- index++;
- dumpWideColorInfo(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--enable-layer-stats"))) {
- index++;
- mLayerStats.enable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--disable-layer-stats"))) {
- index++;
- mLayerStats.disable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--clear-layer-stats"))) {
- index++;
- mLayerStats.clear();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dump-layer-stats"))) {
- index++;
- mLayerStats.dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--frame-composition"))) {
- index++;
- dumpFrameCompositionInfo(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--display-identification"))) {
- index++;
- dumpDisplayIdentificationData(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--timestats"))) {
- index++;
- mTimeStats->parseArgs(asProto, args, index, result);
- dumpAll = false;
- }
- }
-
- if (dumpAll) {
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ } else {
if (asProto) {
LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
} else {
- dumpAllLocked(args, index, result);
+ dumpAllLocked(args, result);
}
}
@@ -4488,43 +4592,29 @@
return NO_ERROR;
}
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */, size_t& /* index */,
- std::string& result) const {
+void SurfaceFlinger::listLayersLocked(std::string& result) const {
mCurrentState.traverseInZOrder(
[&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
}
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
- std::string& result) const {
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
+void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
- if (name.isEmpty()) {
- mAnimFrameTracker.dumpStats(result);
- } else {
+ if (args.size() > 1) {
+ const auto name = String8(args[1]);
mCurrentState.traverseInZOrder([&](Layer* layer) {
if (name == layer->getName()) {
layer->dumpFrameStats(result);
}
});
+ } else {
+ mAnimFrameTracker.dumpStats(result);
}
}
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
- std::string& /* result */) {
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
+void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
- if (name.isEmpty() || (name == layer->getName())) {
+ if (args.size() < 2 || String8(args[1]) == layer->getName()) {
layer->clearFrameStats();
}
});
@@ -4532,6 +4622,10 @@
mAnimFrameTracker.clearStats();
}
+void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
+ mTimeStats->parseArgs(asProto, args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4557,6 +4651,21 @@
result.append("]");
}
+void SurfaceFlinger::dumpVSync(std::string& result) const {
+ mPhaseOffsets->dump(result);
+ StringAppendF(&result,
+ " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
+ dispSyncPresentTimeOffset, getVsyncPeriod());
+
+ StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled");
+
+ if (mUseScheduler) {
+ mScheduler->dump(mAppConnectionHandle, result);
+ } else {
+ mEventThread->dump(result);
+ }
+}
+
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -4760,15 +4869,8 @@
return layersProto;
}
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
- std::string& result) const {
- bool colorize = false;
- if (index < args.size()
- && (args[index] == String16("--color"))) {
- colorize = true;
- index++;
- }
-
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+ const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
// figure out if we're stuck somewhere
@@ -4798,27 +4900,14 @@
result.append("Sync configuration: ");
colorizer.reset(result);
result.append(SyncFeatures::getInstance().toString());
- result.append("\n");
+ result.append("\n\n");
colorizer.bold(result);
- result.append("DispSync configuration:\n");
+ result.append("VSYNC configuration:\n");
colorizer.reset(result);
-
- const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
- const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
- StringAppendF(&result,
- "app phase %" PRId64 " ns, "
- "sf phase %" PRId64 " ns, "
- "early app phase %" PRId64 " ns, "
- "early sf phase %" PRId64 " ns, "
- "early app gl phase %" PRId64 " ns, "
- "early sf gl phase %" PRId64 " ns, "
- "present offset %" PRId64 " ns (refresh %" PRId64 " ns)\n",
- vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset,
- appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod());
-
- // Dump static screen stats
+ dumpVSync(result);
result.append("\n");
+
dumpStaticScreenStats(result);
result.append("\n");
@@ -4879,7 +4968,7 @@
" gpu_to_cpu_unsupported : %d\n",
mTransactionFlags.load(), !mGpuToCpuSupported);
- if (const auto displayId = getInternalDisplayId();
+ if (const auto displayId = getInternalDisplayIdLocked();
displayId && getHwComposer().isConnected(*displayId)) {
const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
StringAppendF(&result,
@@ -4892,17 +4981,6 @@
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
- StringAppendF(&result, " use Scheduler: %s\n", mUseScheduler ? "true" : "false");
- /*
- * VSYNC state
- */
- if (mUseScheduler) {
- mScheduler->dump(mAppConnectionHandle, result);
- } else {
- mEventThread->dump(result);
- }
- result.append("\n");
-
/*
* Tracing state
*/
@@ -4948,6 +5026,15 @@
result.append(mVrFlinger->Dump());
result.append("\n");
}
+
+ /**
+ * Scheduler dump state.
+ */
+ if (mUseScheduler) {
+ result.append("\nScheduler state:\n");
+ result.append(mScheduler->doDump() + "\n");
+ result.append(mRefreshRateStats->doDump() + "\n");
+ }
}
const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
@@ -4963,24 +5050,6 @@
return empty;
}
-bool SurfaceFlinger::startDdmConnection()
-{
- void* libddmconnection_dso =
- dlopen("libsurfaceflinger_ddmconnection.so", RTLD_NOW);
- if (!libddmconnection_dso) {
- return false;
- }
- void (*DdmConnection_start)(const char* name);
- DdmConnection_start =
- (decltype(DdmConnection_start))dlsym(libddmconnection_dso, "DdmConnection_start");
- if (!DdmConnection_start) {
- dlclose(libddmconnection_dso);
- return false;
- }
- (*DdmConnection_start)(getServiceName());
- return true;
-}
-
void SurfaceFlinger::updateColorMatrixLocked() {
mat4 colorMatrix;
if (mGlobalSaturationFactor != 1.0f) {
@@ -5051,8 +5120,10 @@
// information, so it is OK to pass them.
case AUTHENTICATE_SURFACE:
case GET_ACTIVE_CONFIG:
- case GET_BUILT_IN_DISPLAY:
+ case GET_PHYSICAL_DISPLAY_IDS:
+ case GET_PHYSICAL_DISPLAY_TOKEN:
case GET_DISPLAY_COLOR_MODES:
+ case GET_DISPLAY_NATIVE_PRIMARIES:
case GET_DISPLAY_CONFIGS:
case GET_DISPLAY_STATS:
case GET_SUPPORTED_FRAME_TIMESTAMPS:
@@ -5063,12 +5134,13 @@
case GET_COLOR_MANAGEMENT:
case GET_COMPOSITION_PREFERENCE:
case GET_PROTECTED_CONTENT_SUPPORT:
- case CACHE_BUFFER:
- case UNCACHE_BUFFER: {
+ case IS_WIDE_COLOR_DISPLAY: {
return OK;
}
case CAPTURE_LAYERS:
- case CAPTURE_SCREEN: {
+ case CAPTURE_SCREEN:
+ case ADD_REGION_SAMPLING_LISTENER:
+ case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -5442,8 +5514,8 @@
DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
renderAreaRotation);
- auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
- display, std::placeholders::_1);
+ auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
+ std::placeholders::_1);
return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
useIdentityTransform);
}
@@ -5490,11 +5562,14 @@
const sp<Layer>& oldParent;
const sp<Layer>& newParent;
- ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent)
+ ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+ const Rect& drawingBounds)
: oldParent(oldParent), newParent(newParent) {
+ // Compute and cache the bounds for the new parent layer.
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
oldParent->setChildrenDrawingParent(newParent);
}
- ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+ ~ReparentForDrawing() { newParent->setChildrenDrawingParent(oldParent); }
};
void render(std::function<void()> drawLayers) override {
@@ -5512,7 +5587,7 @@
LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
bounds.getWidth(), bounds.getHeight(), 0));
- ReparentForDrawing reparent(mLayer, screenshotParentLayer);
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
drawLayers();
}
}
@@ -5662,35 +5737,109 @@
void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- bool useIdentityTransform) {
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ int* outSyncFd) {
ATRACE_CALL();
- auto& engine(getRenderEngine());
-
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
const auto sourceCrop = renderArea.getSourceCrop();
const auto rotation = renderArea.getRotationFlags();
- engine.setOutputDataSpace(renderArea.getReqDataSpace());
- engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<renderengine::LayerSettings> clientCompositionLayers;
- // make sure to clear all GL error flags
- engine.checkErrors();
+ // assume that bounds are never offset, and that they are the same as the
+ // buffer bounds.
+ clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
+ ui::Transform transform = renderArea.getTransform();
+ mat4 m;
+ m[0][0] = transform[0][0];
+ m[0][1] = transform[0][1];
+ m[0][3] = transform[0][2];
+ m[1][0] = transform[1][0];
+ m[1][1] = transform[1][1];
+ m[1][3] = transform[1][2];
+ m[3][0] = transform[2][0];
+ m[3][1] = transform[2][1];
+ m[3][3] = transform[2][2];
- // set-up our viewport
- engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, rotation);
- engine.disableTexturing();
+ clientCompositionDisplay.globalTransform = m;
+ mat4 rotMatrix;
+ // Displacement for repositioning the clipping rectangle after rotating it
+ // with the rotation hint.
+ int displacementX = 0;
+ int displacementY = 0;
+ float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+ displacementX = reqWidth;
+ break;
+ case ui::Transform::ROT_180:
+ rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+ displacementX = reqWidth;
+ displacementY = reqHeight;
+ break;
+ case ui::Transform::ROT_270:
+ rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+ displacementY = reqHeight;
+ break;
+ default:
+ break;
+ }
+ // We need to transform the clipping window into the right spot.
+ // First, rotate the clipping rectangle by the rotation hint to get the
+ // right orientation
+ const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
+ const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
+ const vec4 rotClipTL = rotMatrix * clipTL;
+ const vec4 rotClipBR = rotMatrix * clipBR;
+ const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
+ const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
+ const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
+ const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
+
+ // Now reposition the clipping rectangle with the displacement vector
+ // computed above.
+ const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
+
+ clientCompositionDisplay.clip =
+ Rect(newClipLeft + displacementX, newClipTop + displacementY,
+ newClipRight + displacementX, newClipBottom + displacementY);
+
+ // We need to perform the same transformation in layer space, so propagate
+ // it to the global transform.
+ mat4 clipTransform = displacementMat * rotMatrix;
+ clientCompositionDisplay.globalTransform *= clipTransform;
+ clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
+ clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
- // redraw the screen entirely...
- engine.clearWithColor(0, 0, 0, alpha);
+ renderengine::LayerSettings fillLayer;
+ fillLayer.source.buffer.buffer = nullptr;
+ fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
+ fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+ fillLayer.alpha = half(alpha);
+ clientCompositionLayers.push_back(fillLayer);
+
+ Region clearRegion = Region::INVALID_REGION;
traverseLayers([&](Layer* layer) {
- engine.setColorTransform(layer->getColorTransform());
- layer->draw(renderArea, useIdentityTransform);
- engine.setColorTransform(mat4());
+ renderengine::LayerSettings layerSettings;
+ bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
+ layerSettings);
+ if (prepared) {
+ clientCompositionLayers.push_back(layerSettings);
+ }
});
+
+ clientCompositionDisplay.clearRegion = clearRegion;
+ base::unique_fd drawFence;
+ getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+ &drawFence);
+
+ *outSyncFd = drawFence.release();
}
status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5714,28 +5863,7 @@
ALOGW("FB is protected: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
- auto& engine(getRenderEngine());
-
- // this binds the given EGLImage as a framebuffer for the
- // duration of this scope.
- renderengine::BindNativeBufferAsFramebuffer bufferBond(engine, buffer);
- if (bufferBond.getStatus() != NO_ERROR) {
- ALOGE("got ANWB binding error while taking screenshot");
- return INVALID_OPERATION;
- }
-
- // this will in fact render into our dequeued buffer
- // via an FBO, which means we didn't have to create
- // an EGLSurface and therefore we're not
- // dependent on the context's EGLConfig.
- renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform);
-
- base::unique_fd syncFd = engine.flush();
- if (syncFd < 0) {
- engine.finish();
- }
- *outSyncFd = syncFd.release();
-
+ renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 499ebcd..669d3a1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -59,6 +59,9 @@
#include "Scheduler/DispSync.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncModulator.h"
#include "SurfaceFlingerFactory.h"
@@ -68,6 +71,7 @@
#include <atomic>
#include <cstdint>
+#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -75,6 +79,7 @@
#include <set>
#include <string>
#include <thread>
+#include <type_traits>
#include <unordered_map>
#include <utility>
@@ -138,7 +143,6 @@
ENHANCED = 2,
};
-
class SurfaceFlingerBE
{
public:
@@ -292,10 +296,6 @@
// starts SurfaceFlinger main loop in the current thread
void run() ANDROID_API;
- enum {
- EVENT_VSYNC = HWC_EVENT_VSYNC
- };
-
// post an asynchronous message to the main thread
status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
@@ -314,7 +314,7 @@
compositionengine::CompositionEngine& getCompositionEngine() const;
// returns the default Display
- sp<const DisplayDevice> getDefaultDisplayDevice() const {
+ sp<const DisplayDevice> getDefaultDisplayDevice() {
Mutex::Autolock _l(mStateLock);
return getDefaultDisplayDeviceLocked();
}
@@ -328,7 +328,7 @@
// enable/disable h/w composer event
// TODO: this should be made accessible only to EventThread
- void setVsyncEnabled(EventThread::DisplayType displayType, bool enabled);
+ void setPrimaryVsyncEnabled(bool enabled);
// called on the main thread by MessageQueue when an internal message
// is received
@@ -415,7 +415,8 @@
sp<ISurfaceComposerClient> createConnection() override;
sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
void destroyDisplay(const sp<IBinder>& displayToken) override;
- sp<IBinder> getBuiltInDisplay(int32_t id) override;
+ std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
+ sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
void setTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
@@ -436,10 +437,17 @@
const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) override;
+ Vector<DisplayInfo>* configs) override {
+ Mutex::Autolock _l(mStateLock);
+ return getDisplayConfigsLocked(displayToken, configs);
+ }
+ status_t getDisplayConfigsLocked(const sp<IBinder>& displayToken, Vector<DisplayInfo>* configs)
+ REQUIRES(mStateLock);
int getActiveConfig(const sp<IBinder>& displayToken) override;
status_t getDisplayColorModes(const sp<IBinder>& displayToken,
Vector<ui::ColorMode>* configs) override;
+ status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries);
ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
@@ -466,10 +474,11 @@
uint64_t timestamp,
DisplayedFrameStats* outStats) const override;
status_t getProtectedContentSupport(bool* outSupported) const override;
- status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
- int32_t* outBufferId) override;
- status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) override;
-
+ status_t isWideColorDisplay(const sp<IBinder>& displayToken,
+ bool* outIsWideColorDisplay) const override;
+ status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) override;
+ status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -501,10 +510,18 @@
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
- // setActiveConfigInternal() posted on a main thread for async execution
- status_t setActiveConfigAsync(const sp<IBinder>& displayToken, int mode);
- // called on the main thread in response to setActiveConfig()
- void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+ // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
+ void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+ // Calls setActiveConfig in HWC.
+ void setActiveConfigInHWC();
+ // Once HWC has returned the present fence, this sets the active config and a new refresh
+ // rate in SF. It also triggers HWC vsync.
+ void setActiveConfigInternal() REQUIRES(mStateLock);
+ // Active config is updated on INVALIDATE call in a state machine-like manner. When the
+ // desired config was set, HWC needs to update the pannel on the next refresh, and when
+ // we receive the fence back, we know that the process was complete. It returns whether
+ // the invalidate process should continue.
+ bool updateSetActiveConfigStateMachine();
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
@@ -519,7 +536,8 @@
void handleTransaction(uint32_t transactionFlags);
void handleTransactionLocked(uint32_t transactionFlags);
- void updateInputWindows();
+ void updateInputFlinger();
+ void updateInputWindowInfo();
void executeInputWindowCommands();
void updateCursorAsync();
@@ -553,10 +571,9 @@
/* ------------------------------------------------------------------------
* Layer management
*/
- status_t createLayer(const String8& name, const sp<Client>& client,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
+ status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags, LayerMetadata metadata,
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
uint32_t h, uint32_t flags, PixelFormat& format,
@@ -591,6 +608,9 @@
const sp<Layer>& parent,
bool addToCurrentState);
+ // Traverse through all the layers and compute and cache its bounds.
+ void computeLayerBounds();
+
/* ------------------------------------------------------------------------
* Boot animation, on/off animations and screen capture
*/
@@ -598,7 +618,8 @@
void startBootAnim();
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- bool useIdentityTransform);
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ int* outSyncFd);
status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform);
@@ -654,7 +675,7 @@
}
sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
- if (const auto token = getInternalDisplayToken()) {
+ if (const auto token = getInternalDisplayTokenLocked()) {
return getDisplayDeviceLocked(token);
}
return nullptr;
@@ -664,6 +685,10 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
+ // Initialize structures containing information about the internal
+ // display's native color coordinates using default data
+ void initDefaultDisplayNativePrimaries();
+
/* ------------------------------------------------------------------------
* H/W composer
*/
@@ -733,8 +758,11 @@
void logLayerStats();
void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
- // This fails if using GL and the surface has been destroyed.
- bool doComposeSurfaces(const sp<DisplayDevice>& display);
+ // This fails if using GL and the surface has been destroyed. readyFence
+ // will be populated if using GL and native fence sync is supported, to
+ // signal when drawing has completed.
+ bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
+ base::unique_fd* readyFence);
void postFramebuffer(const sp<DisplayDevice>& display);
void postFrame();
@@ -751,6 +779,8 @@
void processDisplayChangesLocked();
void processDisplayHotplugEventsLocked();
+ void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
+
/* ------------------------------------------------------------------------
* VSync
*/
@@ -759,9 +789,9 @@
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
void disableHardwareVsync(bool makeUnavailable);
- // Sets the refresh rate to newFps by switching active configs, if they are available for
+ // Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void setRefreshRateTo(float newFps);
+ void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType) REQUIRES(mStateLock);
using GetVsyncPeriod = std::function<nsecs_t()>;
@@ -786,25 +816,15 @@
};
}
- /* ------------------------------------------------------------------------
- * Debugging & dumpsys
+ /*
+ * Display identification
*/
-public:
- status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
- return doDump(fd, Vector<String16>(), asProto);
- }
-
- status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
- return doDump(fd, args, asProto);
- }
-
-private:
- sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const {
+ sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const {
const auto it = mPhysicalDisplayTokens.find(displayId);
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
}
- std::optional<DisplayId> getPhysicalDisplayId(const sp<IBinder>& displayToken) const {
+ std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const {
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (token == displayToken) {
return id;
@@ -814,33 +834,56 @@
}
// TODO(b/74619554): Remove special cases for primary display.
- sp<IBinder> getInternalDisplayToken() const {
- const auto displayId = getInternalDisplayId();
- return displayId ? getPhysicalDisplayToken(*displayId) : nullptr;
+ sp<IBinder> getInternalDisplayTokenLocked() const {
+ const auto displayId = getInternalDisplayIdLocked();
+ return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
}
- std::optional<DisplayId> getInternalDisplayId() const {
+ std::optional<DisplayId> getInternalDisplayIdLocked() const {
const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
- // TODO(b/74619554): Remove special cases for external display.
- std::optional<DisplayId> getExternalDisplayId() const {
- const auto hwcDisplayId = getHwComposer().getExternalHwcDisplayId();
- return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+ /*
+ * Debugging & dumpsys
+ */
+ using DumpArgs = Vector<String16>;
+ using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
+ template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+ static Dumper dumper(F&& dump) {
+ using namespace std::placeholders;
+ return std::bind(std::forward<F>(dump), _3);
}
- void listLayersLocked(const Vector<String16>& args, size_t& index, std::string& result) const;
- void dumpStatsLocked(const Vector<String16>& args, size_t& index, std::string& result) const
- REQUIRES(mStateLock);
- void clearStatsLocked(const Vector<String16>& args, size_t& index, std::string& result);
- void dumpAllLocked(const Vector<String16>& args, size_t& index, std::string& result) const
- REQUIRES(mStateLock);
- bool startDdmConnection();
- void appendSfConfigString(std::string& result) const;
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper dumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _3);
+ }
+ template <typename F>
+ Dumper argsDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _3);
+ }
+
+ template <typename F>
+ Dumper protoDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _2, _3);
+ }
+
+ void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+
+ void appendSfConfigString(std::string& result) const;
+ void listLayersLocked(std::string& result) const;
+ void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+ void clearStatsLocked(const DumpArgs& args, std::string& result);
+ void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void logFrameStats();
+ void dumpVSync(std::string& result) const REQUIRES(mStateLock);
void dumpStaticScreenStats(std::string& result) const;
// Not const because each Layer needs to query Fences and cache timestamps.
void dumpFrameEventsLocked(std::string& result);
@@ -857,7 +900,16 @@
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
}
- status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+ status_t doDump(int fd, const DumpArgs& args, bool asProto);
+
+ status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
+ return doDump(fd, DumpArgs(), asProto);
+ }
+
+ status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
+ return doDump(fd, args, asProto);
+ }
/* ------------------------------------------------------------------------
* VrFlinger
@@ -909,17 +961,23 @@
std::unique_ptr<VSyncSource> mSfEventThreadSource;
std::unique_ptr<InjectVSyncSource> mVSyncInjector;
std::unique_ptr<EventControlThread> mEventControlThread;
- std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
+ // Calculates correct offsets.
VSyncModulator mVsyncModulator;
+ // Keeps track of all available phase offsets for different refresh types.
+ std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
// Can only accessed from the main thread, these members
// don't need synchronization
State mDrawingState{LayerVector::StateSet::Drawing};
bool mVisibleRegionsDirty;
+ // Set during transaction commit stage to track if the input info for a layer has changed.
+ bool mInputInfoChanged{false};
bool mGeometryInvalid;
bool mAnimCompositionPending;
std::vector<sp<Layer>> mLayersWithQueuedFrames;
+ // Tracks layers that need to update a display's dirty region.
+ std::vector<sp<Layer>> mLayersPendingRefresh;
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
bool mHadClientComposition = false;
@@ -940,10 +998,10 @@
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
+ std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
// don't use a lock for these, we don't care
int mDebugRegion;
- int mDebugDDMS;
int mDebugDisableHWC;
int mDebugDisableTransformHint;
volatile nsecs_t mDebugInSwapBuffers;
@@ -955,7 +1013,7 @@
std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
SurfaceTracing mTracing;
LayerStats mLayerStats;
- std::unique_ptr<TimeStats> mTimeStats;
+ std::shared_ptr<TimeStats> mTimeStats;
bool mUseHwcVirtualDisplays = false;
std::atomic<uint32_t> mFrameMissedCount{0};
@@ -1037,16 +1095,49 @@
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+ /* ------------------------------------------------------------------------
+ * Scheduler
+ */
bool mUseScheduler = false;
std::unique_ptr<Scheduler> mScheduler;
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+ std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+ // The following structs are used for configuring active config state at a desired time,
+ // which is once per vsync at invalidate time.
+ enum SetActiveConfigState {
+ NONE, // not in progress
+ NOTIFIED_HWC, // call to HWC has been made
+ REFRESH_RECEIVED // onRefresh was received from HWC
+ };
+ std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE;
+
+ struct ActiveConfigInfo {
+ int configId;
+ sp<IBinder> displayToken;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ if (configId != other.configId) {
+ return true;
+ }
+ return (displayToken != other.displayToken);
+ }
+ };
+ std::mutex mActiveConfigLock;
+ // This bit is set once we start setting the config. We read from this bit during the
+ // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
+ // the process.
+ ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
+ // This bit can be set at any point in time when the system wants the new config.
+ ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+
+ /* ------------------------------------------------------------------------ */
sp<IInputFlinger> mInputFlinger;
InputWindowCommands mInputWindowCommands;
- BufferStateLayerCache mBufferStateLayerCache;
+ ui::DisplayPrimaries mInternalDisplayPrimaries;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 77679e3..26d2c21 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -33,6 +33,7 @@
#include "Scheduler/DispSync.h"
#include "Scheduler/EventControlThread.h"
#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
#include "TimeStats/TimeStats.h"
@@ -68,6 +69,10 @@
return std::make_unique<android::impl::MessageQueue>();
}
+ std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ return std::make_unique<scheduler::impl::PhaseOffsets>();
+ }
+
std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override {
return std::make_unique<Scheduler>(callback);
}
@@ -123,8 +128,8 @@
return new ColorLayer(args);
}
- std::unique_ptr<TimeStats> createTimeStats() override {
- return std::make_unique<TimeStats>();
+ std::shared_ptr<TimeStats> createTimeStats() override {
+ return std::make_shared<android::impl::TimeStats>();
}
};
static Factory factory;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index f747684..fc1d0f8 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -52,6 +52,10 @@
namespace compositionengine {
class CompositionEngine;
} // namespace compositionengine
+
+namespace scheduler {
+class PhaseOffsets;
+} // namespace scheduler
namespace surfaceflinger {
class NativeWindowSurface;
@@ -66,6 +70,7 @@
std::function<void(bool)> setVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
+ virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0;
virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
@@ -88,7 +93,7 @@
virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
- virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+ virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
protected:
~Factory() = default;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
new file mode 100644
index 0000000..b654ba7
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -0,0 +1,246 @@
+
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
+#include <tuple>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+namespace sysprop {
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using ::android::hardware::graphics::common::V1_2::Dataspace;
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(
+ defaultValue);
+}
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::vsync_sf_event_phase_offset_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(
+ defaultValue);
+}
+
+bool use_context_priority(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_context_priority();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useContextPriority>(
+ defaultValue);
+}
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_frame_buffer_acquired_buffers();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(
+ defaultValue);
+}
+
+bool has_wide_color_display(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::has_wide_color_display();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
+ defaultValue);
+}
+
+bool running_without_sync_framework(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::running_without_sync_framework();
+ if (temp.has_value()) {
+ return !(*temp);
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(defaultValue);
+}
+
+bool has_HDR_display(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::has_HDR_display();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(defaultValue);
+}
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::present_time_offset_from_vsync_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(
+ defaultValue);
+}
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::force_hwc_copy_for_virtual_displays();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(
+ defaultValue);
+}
+
+int64_t max_virtual_display_dimension(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_virtual_display_dimension();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getUInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(
+ defaultValue);
+}
+
+bool use_vr_flinger(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_vr_flinger();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(defaultValue);
+}
+
+bool start_graphics_allocator_service(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::start_graphics_allocator_service();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(
+ defaultValue);
+}
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values defaultValue) {
+ auto temp = SurfaceFlingerProperties::primary_display_orientation();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configDefault = DisplayOrientation::ORIENTATION_0;
+ switch (defaultValue) {
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
+ configDefault = DisplayOrientation::ORIENTATION_90;
+ break;
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
+ configDefault = DisplayOrientation::ORIENTATION_180;
+ break;
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
+ configDefault = DisplayOrientation::ORIENTATION_270;
+ break;
+ default:
+ configDefault = DisplayOrientation::ORIENTATION_0;
+ break;
+ }
+ DisplayOrientation result =
+ getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
+ &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
+ configDefault);
+ switch (result) {
+ case DisplayOrientation::ORIENTATION_90:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90;
+ case DisplayOrientation::ORIENTATION_180:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180;
+ case DisplayOrientation::ORIENTATION_270:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270;
+ default:
+ break;
+ }
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
+}
+
+bool use_color_management(bool defaultValue) {
+ auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
+ auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
+ auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
+ if (tmpuseColorManagement.has_value() && tmpHasHDRDisplay.has_value() &&
+ tmpHasWideColorDisplay.has_value()) {
+ return *tmpuseColorManagement || *tmpHasHDRDisplay || *tmpHasWideColorDisplay;
+ }
+ auto surfaceFlingerConfigsServiceV1_2 = ISurfaceFlingerConfigs::getService();
+ if (surfaceFlingerConfigsServiceV1_2) {
+ return getBool<V1_2::ISurfaceFlingerConfigs,
+ &V1_2::ISurfaceFlingerConfigs::useColorManagement>(defaultValue);
+ }
+ return defaultValue;
+}
+
+auto getCompositionPreference(sp<V1_2::ISurfaceFlingerConfigs> configsServiceV1_2) {
+ Dataspace defaultCompositionDataspace = Dataspace::V0_SRGB;
+ PixelFormat defaultCompositionPixelFormat = PixelFormat::RGBA_8888;
+ Dataspace wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
+ PixelFormat wideColorGamutCompositionPixelFormat = PixelFormat::RGBA_8888;
+ configsServiceV1_2->getCompositionPreference(
+ [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
+ auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
+ defaultCompositionDataspace = tmpDefaultDataspace;
+ defaultCompositionPixelFormat = tmpDefaultPixelFormat;
+ wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
+ wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
+ });
+ return std::tuple(defaultCompositionDataspace, defaultCompositionPixelFormat,
+ wideColorGamutCompositionDataspace, wideColorGamutCompositionPixelFormat);
+}
+
+int64_t default_composition_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::default_composition_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+ if (configsServiceV1_2) {
+ return static_cast<int64_t>(get<0>(getCompositionPreference(configsServiceV1_2)));
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
+int32_t default_composition_pixel_format(PixelFormat defaultValue) {
+ auto temp = SurfaceFlingerProperties::default_composition_pixel_format();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+ if (configsServiceV1_2) {
+ return static_cast<int32_t>(get<1>(getCompositionPreference(configsServiceV1_2)));
+ }
+ return static_cast<int32_t>(defaultValue);
+}
+
+int64_t wcg_composition_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::wcg_composition_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+ if (configsServiceV1_2) {
+ return static_cast<int64_t>(get<2>(getCompositionPreference(configsServiceV1_2)));
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
+int32_t wcg_composition_pixel_format(PixelFormat defaultValue) {
+ auto temp = SurfaceFlingerProperties::wcg_composition_pixel_format();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+ if (configsServiceV1_2) {
+ return static_cast<int32_t>(get<3>(getCompositionPreference(configsServiceV1_2)));
+ }
+ return static_cast<int32_t>(defaultValue);
+}
+
+} // namespace sysprop
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
new file mode 100644
index 0000000..9b26883
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -0,0 +1,58 @@
+
+#ifndef SURFACEFLINGERPROPERTIES_H_
+#define SURFACEFLINGERPROPERTIES_H_
+
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+namespace android {
+namespace sysprop {
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue);
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue);
+
+bool use_context_priority(bool defaultValue);
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
+
+bool has_wide_color_display(bool defaultValue);
+
+bool running_without_sync_framework(bool defaultValue);
+
+bool has_HDR_display(bool defaultValue);
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue);
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue);
+
+int64_t max_virtual_display_dimension(int64_t defaultValue);
+
+bool use_vr_flinger(bool defaultValue);
+
+bool start_graphics_allocator_service(bool defaultValue);
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
+
+bool use_color_management(bool defaultValue);
+
+int64_t default_composition_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t default_composition_pixel_format(
+ android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+
+int64_t wcg_composition_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t wcg_composition_pixel_format(
+ android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+} // namespace sysprop
+} // namespace android
+#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/TimeStats/OWNERS b/services/surfaceflinger/TimeStats/OWNERS
new file mode 100644
index 0000000..ac02d12
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/OWNERS
@@ -0,0 +1 @@
+zzyiwei@google.com
\ No newline at end of file
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 6a5488a..78c6e74 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,19 +32,14 @@
namespace android {
-void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
- std::string& result) {
+namespace impl {
+
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
ATRACE_CALL();
- if (args.size() > index + 10) {
- ALOGD("Invalid args count");
- return;
- }
-
std::unordered_map<std::string, int32_t> argsMap;
- while (index < args.size()) {
+ for (size_t index = 0; index < args.size(); ++index) {
argsMap[std::string(String8(args[index]).c_str())] = index;
- ++index;
}
if (argsMap.count("-disable")) {
@@ -457,6 +452,15 @@
mPowerTime.powerMode = powerMode;
}
+void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mTimeStats.refreshRateStats.count(fps)) {
+ mTimeStats.refreshRateStats[fps] += duration;
+ } else {
+ mTimeStats.refreshRateStats.insert({fps, duration});
+ }
+}
+
void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
ATRACE_CALL();
@@ -554,6 +558,7 @@
mTimeStats.clientCompositionFrames = 0;
mTimeStats.displayOnTime = 0;
mTimeStats.presentToPresent.hist.clear();
+ mTimeStats.refreshRateStats.clear();
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
@@ -587,4 +592,6 @@
}
}
+} // namespace impl
+
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71c3ed7..d8c0786 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -36,6 +36,40 @@
namespace android {
class TimeStats {
+public:
+ virtual ~TimeStats() = default;
+
+ virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
+ virtual bool isEnabled() = 0;
+
+ virtual void incrementTotalFrames() = 0;
+ virtual void incrementMissedFrames() = 0;
+ virtual void incrementClientCompositionFrames() = 0;
+
+ virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ nsecs_t postTime) = 0;
+ virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
+ virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+ virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+ virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& acquireFence) = 0;
+ virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
+ virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& presentFence) = 0;
+ // Clean up the layer record
+ virtual void onDestroy(int32_t layerID) = 0;
+ // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
+ virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+
+ virtual void setPowerMode(int32_t powerMode) = 0;
+ // Source of truth is RefrehRateStats.
+ virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
+ virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
+};
+
+namespace impl {
+
+class TimeStats : public android::TimeStats {
struct FrameTime {
uint64_t frameNumber = 0;
nsecs_t postTime = 0;
@@ -75,32 +109,33 @@
public:
TimeStats() = default;
- ~TimeStats() = default;
- void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, std::string& result);
- bool isEnabled();
+ void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
+ bool isEnabled() override;
- void incrementTotalFrames();
- void incrementMissedFrames();
- void incrementClientCompositionFrames();
+ void incrementTotalFrames() override;
+ void incrementMissedFrames() override;
+ void incrementClientCompositionFrames() override;
void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime);
- void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime);
- void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime);
- void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime);
+ nsecs_t postTime) override;
+ void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
+ void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
+ void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
void setAcquireFence(int32_t layerID, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& acquireFence);
- void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
+ const std::shared_ptr<FenceTime>& acquireFence) override;
+ void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
void setPresentFence(int32_t layerID, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence);
+ const std::shared_ptr<FenceTime>& presentFence) override;
// Clean up the layer record
- void onDestroy(int32_t layerID);
+ void onDestroy(int32_t layerID) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
- void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
+ void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
- void setPowerMode(int32_t powerMode);
- void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
+ void setPowerMode(int32_t powerMode) override;
+ // Source of truth is RefrehRateStats.
+ void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
+ void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;
// TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
// static const size_t MAX_NUM_LAYER_RECORDS = 200;
@@ -126,4 +161,6 @@
GlobalRecord mGlobalRecord;
};
+} // namespace impl
+
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 75ce4be..16d2da0 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -103,6 +103,11 @@
StringAppendF(&result, "missedFrames = %d\n", missedFrames);
StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ StringAppendF(&result, "displayConfigStats is as below:\n");
+ for (const auto& [fps, duration] : refreshRateStats) {
+ StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+ }
+ result.back() = '\n';
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
result.append(presentToPresent.toString());
@@ -141,6 +146,13 @@
globalProto.set_missed_frames(missedFrames);
globalProto.set_client_composition_frames(clientCompositionFrames);
globalProto.set_display_on_time(displayOnTime);
+ for (const auto& ele : refreshRateStats) {
+ SFTimeStatsDisplayConfigBucketProto* configBucketProto =
+ globalProto.add_display_config_stats();
+ SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config();
+ configProto->set_fps(ele.first);
+ configBucketProto->set_duration_millis(ns2ms(ele.second));
+ }
for (const auto& histEle : presentToPresent.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
histProto->set_time_millis(histEle.first);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5f40a1a..f2ac7ff 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -16,6 +16,7 @@
#pragma once
#include <timestatsproto/TimeStatsProtoHeader.h>
+#include <utils/Timers.h>
#include <optional>
#include <string>
@@ -61,6 +62,7 @@
int64_t displayOnTime = 0;
Histogram presentToPresent;
std::unordered_map<std::string, TimeStatsLayer> stats;
+ std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 377612a..0dacbeb 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
// changes to these messages, and keep google3 side proto messages in sync if
// the end to end pipeline needs to be updated.
-// Next tag: 9
+// Next tag: 10
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -39,6 +39,8 @@
optional int32 client_composition_frames = 5;
// Primary display on time in milliseconds.
optional int64 display_on_time = 7;
+ // Stats per display configuration.
+ repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
// Present to present histogram.
repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
// Stats per layer. Apps could have multiple layers.
@@ -80,3 +82,18 @@
// Number of frames in the bucket.
optional int32 frame_count = 2;
}
+
+// Next tag: 3
+message SFTimeStatsDisplayConfigBucketProto {
+ // Metadata desribing a display config.
+ optional SFTimeStatsDisplayConfigProto config = 1;
+ // Duration in milliseconds for how long the display was in this
+ // configuration.
+ optional int64 duration_millis = 2;
+}
+
+// Next tag: 2
+message SFTimeStatsDisplayConfigProto {
+ // Frames per second, rounded to the nearest integer.
+ optional int32 fps = 1;
+}
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index ac147fe..cb368b0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,6 +1,5 @@
cc_library_shared {
name: "liblayers_proto",
- vendor_available: true,
export_include_dirs: ["include"],
srcs: [
@@ -11,6 +10,7 @@
shared_libs: [
"android.hardware.graphics.common@1.1",
+ "libgui",
"libui",
"libprotobuf-cpp-lite",
"libbase",
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index d020a39..5c72fea 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -117,11 +117,15 @@
layer.hwcFrame = generateRect(layerProto.hwc_frame());
layer.hwcCrop = generateFloatRect(layerProto.hwc_crop());
layer.hwcTransform = layerProto.hwc_transform();
- layer.windowType = layerProto.window_type();
- layer.appId = layerProto.app_id();
layer.hwcCompositionType = layerProto.hwc_composition_type();
layer.isProtected = layerProto.is_protected();
layer.cornerRadius = layerProto.corner_radius();
+ for (const auto& entry : layerProto.metadata()) {
+ const std::string& dataStr = entry.second;
+ std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
+ outData.resize(dataStr.size());
+ memcpy(outData.data(), dataStr.data(), dataStr.size());
+ }
return layer;
}
@@ -310,7 +314,14 @@
StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str());
StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending);
- StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId);
+ StringAppendF(&result, " metadata={");
+ bool first = true;
+ for (const auto& entry : metadata.mMap) {
+ if (!first) result.append(", ");
+ first = false;
+ result.append(metadata.itemToString(entry.first, ":"));
+ }
+ result.append("}");
return result;
}
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index a794ca5..d1b2b1f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -17,6 +17,7 @@
#include <layerproto/LayerProtoHeader.h>
+#include <gui/LayerMetadata.h>
#include <math/vec4.h>
#include <memory>
@@ -110,11 +111,10 @@
LayerProtoParser::Rect hwcFrame;
LayerProtoParser::FloatRect hwcCrop;
int32_t hwcTransform;
- int32_t windowType;
- int32_t appId;
int32_t hwcCompositionType;
bool isProtected;
float cornerRadius;
+ LayerMetadata metadata;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index b100438..72cbfac 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -74,8 +74,8 @@
optional FloatRectProto hwc_crop = 31;
// The layer's composer backend transform
optional int32 hwc_transform = 32;
- optional int32 window_type = 33;
- optional int32 app_id = 34;
+ optional int32 window_type = 33 [deprecated=true];
+ optional int32 app_id = 34 [deprecated=true];
// The layer's composition type
optional int32 hwc_composition_type = 35;
// If it's a buffer layer, indicate if the content is protected
@@ -89,6 +89,13 @@
optional int32 effective_scaling_mode = 40;
// Layer's corner radius.
optional float corner_radius = 41;
+ // Metadata map. May be empty.
+ map<int32, bytes> metadata = 42;
+
+ optional TransformProto effective_transform = 43;
+ optional FloatRectProto source_bounds = 44;
+ optional FloatRectProto bounds = 45;
+ optional FloatRectProto screen_bounds = 46;
}
message PositionProto {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 3ad6ec3..e7986d3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -31,14 +31,14 @@
#include <processgroup/sched_policy.h>
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerProperties.h"
using namespace android;
static status_t startGraphicsAllocatorService() {
using android::hardware::configstore::getBool;
using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
- if (!getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+ if (!android::sysprop::start_graphics_allocator_service(false)) {
return OK;
}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
new file mode 100644
index 0000000..cc7b280
--- /dev/null
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -0,0 +1,252 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the License);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: "android.sysprop.SurfaceFlingerProperties"
+owner: Platform
+
+# The following two propertiess define (respectively):
+#
+# - The phase offset between hardware vsync and when apps are woken up by the
+# Choreographer callback
+# - The phase offset between hardware vsync and when SurfaceFlinger wakes up
+# to consume input
+# Their values may be tuned to trade off between display pipeline latency (both
+# overall latency and the lengths of the app --> SF and SF --> display phases)
+# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
+# while interacting with the device). The default values must produce a
+# relatively low amount of jitter at the expense of roughly two frames of
+# app --> display latency, and unless significant testing is performed to avoid
+# increased display jitter (both manual investigation using systrace [1] and
+# automated testing using dumpsys gfxinfo [2] are recommended), they should not
+# be modified.
+#
+# [1] https://developer.android.com/studio/profile/systrace.html
+# [2] https://developer.android.com/training/testing/performance.html
+prop {
+ api_name: "vsync_event_phase_offset_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns"
+}
+
+prop {
+ api_name: "vsync_sf_event_phase_offset_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns"
+}
+
+# Instruct the Render Engine to use EGL_IMG_context_priority hint if available.
+prop {
+ api_name: "use_context_priority"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_context_priority"
+}
+
+# Controls the number of buffers SurfaceFlinger will allocate for use in FramebufferSurface.
+prop {
+ api_name: "max_frame_buffer_acquired_buffers"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
+}
+
+# hasWideColorDisplay indicates that the device has
+# or can support a wide-color display, e.g. color space
+# greater than sRGB. Typical display may have same
+# color primaries as DCI-P3.
+# Indicate support for this feature by setting
+# TARGET_HAS_WIDE_COLOR_DISPLAY to true in BoardConfig.mk
+# This also means that the device is color managed.
+# A color managed device will use the appropriate
+# display mode depending on the content on the screen.
+# Default is sRGB.
+prop {
+ api_name: "has_wide_color_display"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.has_wide_color_display"
+}
+
+# Indicates if Sync framework is available. Sync framework provides fence
+# mechanism which significantly reduces buffer processing latency.
+prop {
+ api_name: "running_without_sync_framework"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.running_without_sync_framework"
+}
+
+# hwHDRDisplay indicates that the device has an High Dynamic Range display.
+# A display is considered High Dynamic Range if it
+#
+# 1. is a wide color gamut display, typically DCI-P3 or lager
+# 2. has high luminance capability, typically 540 nits or higher at 10% OPR
+#
+# Indicate support for this feature by setting
+# ro.surface_flinger.has_HDR_display to true in device.mk
+# ro.surface_flinger.has_wide_color_display must be set to true when
+# ro.surface_flinger.has_HDR_display is true.
+prop {
+ api_name: "has_HDR_display"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.has_HDR_display"
+}
+
+# Specify the offset in nanoseconds to add to vsync time when timestamping present fences.
+prop {
+ api_name: "present_time_offset_from_vsync_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
+}
+
+# Some hardware can do RGB->YUV conversion more efficiently in hardware
+# controlled by HWC than in hardware controlled by the video encoder.
+# This instruct VirtualDisplaySurface to use HWC for such conversion on
+# GL composition.
+prop {
+ api_name: "force_hwc_copy_for_virtual_displays"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
+}
+
+# Maximum dimension supported by HWC for virtual display.
+# Must be equals to min(max_width, max_height).
+prop {
+ api_name: "max_virtual_display_dimension"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_virtual_display_dimension"
+}
+
+# Return true if surface flinger should use vr flinger for compatible vr
+# apps, false otherwise. Devices that will never be running vr apps should
+# return false to avoid extra resource usage. Daydream ready devices must
+# return true for full vr support.
+prop {
+ api_name: "use_vr_flinger"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_vr_flinger"
+}
+
+# Returns true if surface flinger should start
+# hardware.graphics.allocator@2.0::IAllocator service.
+prop {
+ api_name: "start_graphics_allocator_service"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.start_graphics_allocator_service"
+}
+
+# Returns the orientation of the primary display device.
+prop {
+ api_name: "primary_display_orientation"
+ type: Enum
+ enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.primary_display_orientation"
+}
+
+# useColorManagement indicates whether SurfaceFlinger should manage color
+# by switching to appropriate color mode automatically depending on the
+# Dataspace of the surfaces on screen.
+prop {
+ api_name: "use_color_management"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_color_management"
+}
+
+# The following four propertiess define:
+# Returns the default data space and pixel format that SurfaceFlinger
+# expects to receive and output as well as the wide color gamut data space
+# and pixel format for wide color gamut surfaces.
+# To determine the data space and pixel format, there are a few things
+# we recommend to consider:
+#
+# 1. Hardware composer's capability to composite contents with the chosen
+# data space and pixel format efficiently;
+# 2. Hardware composer's ability to composite contents when sRGB contents
+# and the chosen wide color gamut data space contents coexist;
+# 3. For better blending, consider using pixel format where the alpha
+# channel has as many bits as the RGB color channel.
+# 4. Memory consumption and efficient buffer compression when considering
+# more bits in pixel format.
+
+# dataspace is the default data space that SurfaceFlinger expects.
+# The data space must not be Dataspace::UNKNOWN, if unspecified,
+# the default data space is Dataspace::V0_SRGB;
+prop {
+ api_name: "default_composition_dataspace"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.default_composition_dataspace"
+}
+
+# pixelFormat is the default pixel format that SurfaceFlinger
+# expects. If unspecified, the default pixel format is
+# PixelFormat::RGBA_8888.
+prop {
+ api_name: "default_composition_pixel_format"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.default_composition_pixel_format"
+}
+
+# wcgDataspace is the data space that SurfaceFlinger expects for
+# wide color gamut surfaces.
+# When hasWideColorDisplay returns true, this API must return a
+# valid wide color gamut data space.
+# The data space must not be UNKNOWN, if unspecified, the data space
+# is V0_SRGB by default, which essentially indicates there's no wide
+# color gamut, meaning hasWideColorDisplay returns false.
+prop {
+ api_name: "wcg_composition_dataspace"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.wcg_composition_dataspace"
+}
+
+# wcgPixelFormat is the pixel format that SurfaceFlinger expects for
+# wide color gamut surfaces. If unspecified, the pixel format is
+# PixelFormat::RGBA_8888 by default.
+prop {
+ api_name: "wcg_composition_pixel_format"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
+}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 8560f5e..61d09da 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -19,6 +19,7 @@
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
+using ui::ColorMode;
namespace {
const String8 DISPLAY_NAME("Credentials Display Test");
@@ -61,9 +62,11 @@
}
void setupBackgroundSurface() {
- mDisplay = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+ mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(mDisplay == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(mDisplay, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
const ssize_t displayWidth = info.w;
const ssize_t displayHeight = info.h;
@@ -169,10 +172,8 @@
}
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
- std::function<bool()> condition = [=]() {
- sp<IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- return (display != nullptr);
+ std::function<bool()> condition = [] {
+ return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
};
// Anyone can access display information.
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
@@ -182,7 +183,7 @@
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
setBinUID();
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_TRUE(display != nullptr);
DisplayInfo info;
@@ -198,7 +199,7 @@
}
TEST_F(CredentialsTest, GetDisplayColorModesTest) {
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
Vector<ui::ColorMode> outColorModes;
return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes);
@@ -206,8 +207,17 @@
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
+TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ ui::DisplayPrimaries primaries;
+ return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
TEST_F(CredentialsTest, SetActiveConfigTest) {
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setActiveConfig(display, 0);
};
@@ -215,7 +225,7 @@
}
TEST_F(CredentialsTest, SetActiveColorModeTest) {
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
};
@@ -248,7 +258,7 @@
}
TEST_F(CredentialsTest, CaptureTest) {
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
@@ -312,4 +322,38 @@
seteuid(AID_BIN);
ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
}
+
+TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+ bool result = false;
+ status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
+ ASSERT_EQ(NO_ERROR, error);
+ bool hasWideColorMode = false;
+ Vector<ColorMode> colorModes;
+ SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
+ for (ColorMode colorMode : colorModes) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DCI_P3:
+ hasWideColorMode = true;
+ break;
+ default:
+ break;
+ }
+ }
+ ASSERT_EQ(hasWideColorMode, result);
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+ std::function<status_t()> condition = [=]() {
+ bool result = false;
+ return SurfaceComposerClient::isWideColorDisplay(display, &result);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index 89c26f4..ee857b0 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -34,7 +34,7 @@
auto surf = client->createSurface(String8("t"), 100, 100,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(surf != nullptr);
- surf->clear();
+ surf.clear();
}
};
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 8ec3e15..5cc946a 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -240,10 +240,12 @@
}
void SurfaceInterceptorTest::setupBackgroundSurface() {
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 216532a..7b21dbc 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -26,6 +26,8 @@
#include <android/native_window.h>
+#include <binder/ProcessState.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -193,13 +195,12 @@
class ScreenCapture : public RefBase {
public:
static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto sf = ComposerService::getComposerService();
+ const auto token = sf->getInternalDisplayToken();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, Rect(), 0, 0, false));
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(token, &outBuffer, Rect(), 0, 0, false));
*sc = std::make_unique<ScreenCapture>(outBuffer);
}
@@ -322,7 +323,6 @@
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> binder = sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
}
@@ -495,21 +495,17 @@
// leave room for ~256 layers
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
- void setRelativeZBasicHelper(uint32_t layerType);
- void setRelativeZGroupHelper(uint32_t layerType);
- void setAlphaBasicHelper(uint32_t layerType);
-
sp<SurfaceControl> mBlackBgSurface;
bool mColorManagementUsed;
private:
void SetUpDisplay() {
- mDisplay = mClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
- ASSERT_NE(nullptr, mDisplay.get()) << "failed to get built-in display";
+ mDisplay = mClient->getInternalDisplayToken();
+ ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
// get display width/height
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(mDisplay, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
mDisplayWidth = info.w;
mDisplayHeight = info.h;
mDisplayRect =
@@ -544,15 +540,71 @@
}
int32_t mBufferPostDelay;
+
+ friend class LayerRenderPathTestHarness;
+};
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+class LayerRenderPathTestHarness {
+public:
+ LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+ : mDelegate(delegate), mRenderPath(renderPath) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ switch (mRenderPath) {
+ case RenderPath::SCREENSHOT:
+ return mDelegate->screenshot();
+ case RenderPath::VIRTUAL_DISPLAY:
+
+ const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ DisplayInfo mainDisplayInfo;
+ SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+
+ sp<IBinder> vDisplay;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ sp<BufferItemConsumer> itemConsumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+
+ itemConsumer = new BufferItemConsumer(consumer,
+ // Sample usage bits from screenrecord
+ GRALLOC_USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_SW_READ_OFTEN);
+
+ vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+ false /*secure*/);
+
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(vDisplay, producer);
+ t.setDisplayLayerStack(vDisplay, 0);
+ t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
+ Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
+ Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+ t.apply();
+ SurfaceComposerClient::Transaction().apply(true);
+ BufferItem item;
+ itemConsumer->acquireBuffer(&item, 0, true);
+ auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+ itemConsumer->releaseBuffer(item);
+ SurfaceComposerClient::destroyDisplay(vDisplay);
+ return sc;
+ }
+ }
+
+protected:
+ LayerTransactionTest* mDelegate;
+ RenderPath mRenderPath;
};
-class LayerTypeTransactionTest : public LayerTransactionTest,
- public ::testing::WithParamInterface<uint32_t> {
+class LayerTypeTransactionHarness : public LayerTransactionTest {
public:
- LayerTypeTransactionTest() { mLayerType = GetParam(); }
+ LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0, SurfaceControl* parent = nullptr) override {
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
// if the flags already have a layer type specified, return an error
if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
return nullptr;
@@ -579,12 +631,71 @@
uint32_t mLayerType;
};
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<uint32_t> {
+public:
+ LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+class LayerTypeAndRenderTypeTransactionTest
+ : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+ LayerTypeAndRenderTypeTransactionTest()
+ : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+ mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ return mRenderPathHarness.getScreenCapture();
+ }
+
+protected:
+ LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+ void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+ public ::testing::WithParamInterface<RenderPath> {
+public:
+ LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+ void setRelativeZBasicHelper(uint32_t layerType);
+ void setRelativeZGroupHelper(uint32_t layerType);
+ void setAlphaBasicHelper(uint32_t layerType);
+ void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
+ Color finalColor);
+
+protected:
+ LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(
+ LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+ ::testing::Combine(
+ ::testing::Values(
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
INSTANTIATE_TEST_CASE_P(
LayerTypeTransactionTests, LayerTypeTransactionTest,
::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
-TEST_F(LayerTransactionTest, SetPositionBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -592,7 +703,7 @@
{
SCOPED_TRACE("default position");
const Rect rect(0, 0, 32, 32);
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
}
@@ -601,13 +712,13 @@
{
SCOPED_TRACE("new position");
const Rect rect(5, 10, 37, 42);
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetPositionRounding_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -619,17 +730,17 @@
Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
{
SCOPED_TRACE("rounding down");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
{
SCOPED_TRACE("rounding up");
- screenshot()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+ getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -637,17 +748,17 @@
Transaction().setPosition(layer, -32, -32).apply();
{
SCOPED_TRACE("negative coordinates");
- screenshot()->expectColor(mDisplayRect, Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
{
SCOPED_TRACE("positive coordinates");
- screenshot()->expectColor(mDisplayRect, Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -656,19 +767,19 @@
Transaction().setPosition(layer, -30, -30).apply();
{
SCOPED_TRACE("negative coordinates");
- screenshot()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
}
Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
{
SCOPED_TRACE("positive coordinates");
- screenshot()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
- mDisplayHeight),
- Color::RED);
+ getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+ mDisplayHeight),
+ Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -678,7 +789,7 @@
Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
const Rect rect(5, 10, 37, 42);
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
@@ -687,11 +798,11 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("resize applied");
- screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+ getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithNextResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -700,30 +811,30 @@
Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply();
{
SCOPED_TRACE("new position pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setPosition(layer, 15, 20).apply();
{
SCOPED_TRACE("pending new position modified");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
// finally resize and latch the buffer
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("new position applied");
- screenshot()->expectColor(Rect(15, 20, 79, 84), Color::RED);
+ getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -737,17 +848,17 @@
.apply();
{
SCOPED_TRACE("new position pending");
- screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("new position applied");
- screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+ getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetSizeBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -755,7 +866,7 @@
Transaction().setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
const Rect rect(0, 0, 32, 32);
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
@@ -764,18 +875,18 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("resize applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
const Rect rect(0, 0, 64, 64);
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
}
}
-TEST_P(LayerTypeTransactionTest, SetSizeInvalid) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
// cannot test robustness against invalid sizes (zero or really huge)
}
-TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -785,10 +896,10 @@
.setSize(layer, 64, 64)
.setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
.apply();
- screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
-TEST_P(LayerTypeTransactionTest, SetZBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -799,17 +910,17 @@
Transaction().setLayer(layerR, mLayerZBase + 1).apply();
{
SCOPED_TRACE("layerR");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setLayer(layerG, mLayerZBase + 2).apply();
{
SCOPED_TRACE("layerG");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
}
}
-TEST_P(LayerTypeTransactionTest, SetZNegative) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
sp<SurfaceControl> parent =
LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
@@ -828,19 +939,19 @@
Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
{
SCOPED_TRACE("layerR");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setLayer(layerR, -3).apply();
{
SCOPED_TRACE("layerG");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
}
}
-void LayerTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
@@ -867,7 +978,7 @@
}
{
SCOPED_TRACE("layerG above");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
}
@@ -875,17 +986,17 @@
Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
{
SCOPED_TRACE("layerG below");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
}
}
-TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
}
-TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
}
@@ -918,7 +1029,7 @@
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
-void LayerTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
@@ -954,7 +1065,7 @@
{
SCOPED_TRACE("(layerR < layerG) < layerB");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
@@ -964,7 +1075,7 @@
Transaction().setLayer(layerR, mLayerZBase + 4).apply();
{
SCOPED_TRACE("layerB < (layerR < layerG)");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -974,7 +1085,7 @@
Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
{
SCOPED_TRACE("layerB < (layerG < layerR)");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -985,7 +1096,7 @@
Transaction().setLayer(layerG, mLayerZBase).apply();
{
SCOPED_TRACE("layerG < layerB < layerR");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
}
@@ -995,21 +1106,21 @@
Transaction().setLayer(layerR, mLayerZBase + 1).apply();
{
SCOPED_TRACE("layerG < layerR < layerB");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
}
}
-TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
}
-TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
}
-TEST_P(LayerTypeTransactionTest, SetRelativeZBug64572777) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
@@ -1023,12 +1134,12 @@
.setRelativeLayer(layerG, layerR->getHandle(), 1)
.apply();
- layerG->clear();
+ layerG.clear();
// layerG should have been removed
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_P(LayerTypeTransactionTest, SetFlagsHidden) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
@@ -1036,17 +1147,17 @@
Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
{
SCOPED_TRACE("layer hidden");
- screenshot()->expectColor(mDisplayRect, Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
{
SCOPED_TRACE("layer shown");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_P(LayerTypeTransactionTest, SetFlagsOpaque) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
const Color translucentRed = {100, 0, 0, 100};
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
@@ -1061,14 +1172,14 @@
.apply();
{
SCOPED_TRACE("layerR opaque");
- screenshot()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
}
Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
{
SCOPED_TRACE("layerR translucent");
const uint8_t g = uint8_t(255 - translucentRed.a);
- screenshot()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
}
}
@@ -1090,7 +1201,7 @@
composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
const Rect top(0, 0, 32, 16);
const Rect bottom(0, 16, 32, 32);
sp<SurfaceControl> layer;
@@ -1105,7 +1216,7 @@
ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
{
SCOPED_TRACE("top transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::RED);
}
@@ -1113,7 +1224,7 @@
Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
{
SCOPED_TRACE("transparent region hint pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::RED);
}
@@ -1124,13 +1235,13 @@
ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
{
SCOPED_TRACE("bottom transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::RED);
shot->expectColor(bottom, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
const Rect top(0, 0, 32, 16);
const Rect bottom(0, 16, 32, 32);
sp<SurfaceControl> layer;
@@ -1152,7 +1263,7 @@
.apply();
{
SCOPED_TRACE("top transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::RED);
}
@@ -1160,7 +1271,7 @@
Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
{
SCOPED_TRACE("transparent region hint intermediate");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::BLACK);
}
@@ -1175,13 +1286,13 @@
Transaction().setBuffer(layer, buffer).apply();
{
SCOPED_TRACE("bottom transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::RED);
shot->expectColor(bottom, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
sp<SurfaceControl> layerTransparent;
sp<SurfaceControl> layerR;
ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -1196,10 +1307,10 @@
ASSERT_NO_FATAL_FAILURE(
fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
- screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
sp<SurfaceControl> layerTransparent;
sp<SurfaceControl> layerR;
ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -1215,10 +1326,10 @@
ASSERT_NO_FATAL_FAILURE(
fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
- screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
}
-void LayerTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
sp<SurfaceControl> layer1;
sp<SurfaceControl> layer2;
ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
@@ -1248,7 +1359,7 @@
ASSERT_FALSE(true) << "Unsupported layer type";
}
{
- auto shot = screenshot();
+ auto shot = getScreenCapture();
uint8_t r = 16; // 64 * 0.25f
uint8_t g = 48; // 64 * 0.75f
shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
@@ -1259,15 +1370,15 @@
}
}
-TEST_F(LayerTransactionTest, SetAlphaBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
}
-TEST_F(LayerTransactionTest, SetAlphaBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
}
-TEST_P(LayerTypeTransactionTest, SetAlphaClamped) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
const Color color = {64, 0, 0, 255};
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
@@ -1276,17 +1387,17 @@
Transaction().setAlpha(layer, 2.0f).apply();
{
SCOPED_TRACE("clamped to 1.0f");
- screenshot()->expectColor(Rect(0, 0, 32, 32), color);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
}
Transaction().setAlpha(layer, -1.0f).apply();
{
SCOPED_TRACE("clamped to 0.0f");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
}
-TEST_P(LayerTypeTransactionTest, SetCornerRadius) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
sp<SurfaceControl> layer;
const uint8_t size = 64;
const uint8_t testArea = 4;
@@ -1300,7 +1411,7 @@
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
- auto shot = screenshot();
+ auto shot = getScreenCapture();
// Transparent corners
shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
@@ -1309,7 +1420,7 @@
}
}
-TEST_P(LayerTypeTransactionTest, SetCornerRadiusChildCrop) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
sp<SurfaceControl> parent;
sp<SurfaceControl> child;
const uint8_t size = 64;
@@ -1328,7 +1439,7 @@
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
- auto shot = screenshot();
+ auto shot = getScreenCapture();
// Top edge of child should not have rounded corners because it's translated in the parent
shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
Color::GREEN);
@@ -1338,7 +1449,7 @@
}
}
-TEST_F(LayerTransactionTest, SetColorBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
@@ -1354,7 +1465,7 @@
{
SCOPED_TRACE("default color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
@@ -1365,11 +1476,196 @@
Transaction().setColor(colorLayer, color).apply();
{
SCOPED_TRACE("new color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
}
}
-TEST_F(LayerTransactionTest, SetColorClamped) {
+// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// BLUE: prior background color
+// GREEN: final background color
+// BLACK: no color or fill
+void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
+ bool bufferFill, float alpha,
+ Color finalColor) {
+ sp<SurfaceControl> layer;
+ int32_t width = 500;
+ int32_t height = 500;
+
+ Color fillColor = Color::RED;
+ Color priorBgColor = Color::BLUE;
+ Color expectedColor = Color::BLACK;
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceColor:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
+ Transaction()
+ .setCrop_legacy(layer, Rect(0, 0, width, height))
+ .setColor(layer, half3(1.0f, 0, 0))
+ .apply();
+ expectedColor = fillColor;
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+ break;
+ default:
+ GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
+ return;
+ }
+
+ if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
+ .apply();
+ if (!bufferFill) {
+ expectedColor = priorBgColor;
+ }
+ }
+
+ {
+ SCOPED_TRACE("default before setting background color layer");
+ screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
+ }
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
+ .apply();
+
+ {
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, width, height), finalColor);
+ shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(colorLayer =
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
@@ -1379,10 +1675,10 @@
.setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
.apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(LayerTransactionTest, SetColorWithAlpha) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
@@ -1403,11 +1699,11 @@
.setAlpha(colorLayer, alpha)
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
}
-TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> parentLayer;
sp<SurfaceControl> colorLayer;
@@ -1430,21 +1726,21 @@
.setAlpha(parentLayer, alpha)
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
}
-TEST_P(LayerTypeTransactionTest, SetColorWithBuffer) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
// color is ignored
Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_P(LayerTypeTransactionTest, SetLayerStackBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
@@ -1452,17 +1748,17 @@
Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
{
SCOPED_TRACE("non-existing layer stack");
- screenshot()->expectColor(mDisplayRect, Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
{
SCOPED_TRACE("original layer stack");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetMatrixBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1471,40 +1767,40 @@
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("IDENTITY");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
{
SCOPED_TRACE("FLIP_H");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
- Color::BLUE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+ Color::WHITE, Color::BLUE);
}
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
{
SCOPED_TRACE("FLIP_V");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
- Color::GREEN);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+ Color::RED, Color::GREEN);
}
Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
{
SCOPED_TRACE("ROT_90");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
- Color::GREEN);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+ Color::WHITE, Color::GREEN);
}
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("SCALE");
- screenshot()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
}
}
-TEST_F(LayerTransactionTest, SetMatrixBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1517,40 +1813,40 @@
.apply();
{
SCOPED_TRACE("IDENTITY");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
{
SCOPED_TRACE("FLIP_H");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
{
SCOPED_TRACE("FLIP_V");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
{
SCOPED_TRACE("ROT_90");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
{
SCOPED_TRACE("SCALE");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
}
-TEST_F(LayerTransactionTest, SetMatrixRot45_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1560,7 +1856,7 @@
const float trans = M_SQRT2 * 16.0f;
Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
// check a 8x8 region inside each color
auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
const int32_t halfL = 4;
@@ -1573,7 +1869,7 @@
shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
}
-TEST_F(LayerTransactionTest, SetMatrixWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1582,7 +1878,7 @@
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
const Rect rect(0, 0, 32, 32);
shot->expectColor(rect, Color::RED);
shot->expectBorder(rect, Color::BLACK);
@@ -1592,11 +1888,11 @@
{
SCOPED_TRACE("resize applied");
const Rect rect(0, 0, 128, 128);
- screenshot()->expectColor(rect, Color::RED);
+ getScreenCapture()->expectColor(rect, Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1607,10 +1903,10 @@
.setSize(layer, 64, 64)
.setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
.apply();
- screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
}
-TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
@@ -1625,8 +1921,8 @@
.apply();
{
SCOPED_TRACE("SCALE_TO_WINDOW");
- screenshot()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
}
}
@@ -1643,19 +1939,19 @@
ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
}
-TEST_F(LayerTransactionTest, SetCropBasic_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
const Rect crop(8, 8, 24, 24);
Transaction().setCrop_legacy(layer, crop).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(crop, Color::RED);
shot->expectBorder(crop, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1663,12 +1959,12 @@
const Rect crop(8, 8, 24, 24);
Transaction().setCrop(layer, crop).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropEmpty_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1676,17 +1972,17 @@
{
SCOPED_TRACE("empty rect");
Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetCropEmpty_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1695,28 +1991,28 @@
{
SCOPED_TRACE("empty rect");
Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2,
ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1735,7 +2031,7 @@
Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply();
{
SCOPED_TRACE("out of bounds, negative (upper left) direction");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE);
shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
}
@@ -1746,7 +2042,7 @@
.apply();
{
SCOPED_TRACE("out of bounds, positive (lower right) direction");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED);
shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
}
@@ -1755,14 +2051,14 @@
Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
{
SCOPED_TRACE("Fully out of bounds");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE);
shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2),
Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1770,12 +2066,12 @@
const Point position(32, 32);
const Rect crop(8, 8, 24, 24);
Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(crop + position, Color::RED);
shot->expectBorder(crop + position, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1784,12 +2080,12 @@
const Rect frame(32, 32, 64, 64);
const Rect crop(8, 8, 24, 24);
Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(frame, Color::RED);
shot->expectBorder(frame, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithScale_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1799,12 +2095,12 @@
.setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
.setCrop_legacy(layer, Rect(8, 8, 24, 24))
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1813,7 +2109,7 @@
Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
}
@@ -1821,13 +2117,13 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
{
SCOPED_TRACE("resize applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetCropWithNextResize_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1839,32 +2135,32 @@
.apply();
{
SCOPED_TRACE("waiting for next resize");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
{
SCOPED_TRACE("pending crop modified");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
// finally resize
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
{
SCOPED_TRACE("new crop applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
@@ -1878,7 +2174,7 @@
.apply();
{
SCOPED_TRACE("new crop pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
}
@@ -1889,13 +2185,13 @@
Transaction().setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("new crop applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetFrameBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1903,12 +2199,12 @@
const Rect frame(8, 8, 24, 24);
Transaction().setFrame(layer, frame).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(frame, Color::RED);
shot->expectBorder(frame, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFrameEmpty_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1917,29 +2213,29 @@
{
SCOPED_TRACE("empty rect");
Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
{
SCOPED_TRACE("negative rect");
Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetFrameDefaultParentless_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
// A parentless layer will default to a frame with the same size as the buffer
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFrameDefaultBSParent_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
sp<SurfaceControl> parent, child;
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1953,12 +2249,12 @@
Transaction().reparent(child, parent->getHandle()).apply();
// A layer will default to the frame of its parent
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFrameDefaultBQParent_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
sp<SurfaceControl> parent, child;
ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
@@ -1970,12 +2266,12 @@
Transaction().reparent(child, parent->getHandle()).apply();
// A layer will default to the frame of its parent
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFrameUpdate_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -1986,12 +2282,12 @@
Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFrameOutsideBounds_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
sp<SurfaceControl> parent, child;
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2005,25 +2301,25 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetBufferBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetBufferMultipleBuffers_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2032,7 +2328,7 @@
{
SCOPED_TRACE("set buffer 1");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -2041,7 +2337,7 @@
{
SCOPED_TRACE("set buffer 2");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -2050,13 +2346,13 @@
{
SCOPED_TRACE("set buffer 3");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetBufferMultipleLayers_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
sp<SurfaceControl> layer1;
ASSERT_NO_FATAL_FAILURE(
layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2070,7 +2366,7 @@
Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
{
SCOPED_TRACE("set layer 1 buffer red");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
@@ -2079,7 +2375,7 @@
Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
{
SCOPED_TRACE("set layer 2 buffer blue");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
@@ -2088,7 +2384,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
{
SCOPED_TRACE("set layer 1 buffer green");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
@@ -2098,14 +2394,125 @@
{
SCOPED_TRACE("set layer 2 buffer white");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
}
}
-TEST_F(LayerTransactionTest, SetTransformRotate90_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 10> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 70> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 65> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+ if (idx == 0) {
+ buffers[0].clear();
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2118,11 +2525,11 @@
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
.apply();
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
- Color::GREEN, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+ Color::GREEN, true /* filtered */);
}
-TEST_F(LayerTransactionTest, SetTransformFlipH_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2135,11 +2542,11 @@
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
.apply();
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
- Color::BLUE, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+ Color::BLUE, true /* filtered */);
}
-TEST_F(LayerTransactionTest, SetTransformFlipV_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2152,8 +2559,8 @@
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
.apply();
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
- Color::GREEN, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+ Color::GREEN, true /* filtered */);
}
TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
@@ -2168,7 +2575,7 @@
Transaction().setTransformToDisplayInverse(layer, true).apply();
}
-TEST_F(LayerTransactionTest, SetFenceBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
sp<SurfaceControl> layer;
Transaction transaction;
ASSERT_NO_FATAL_FAILURE(
@@ -2193,12 +2600,12 @@
ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
std::this_thread::sleep_for(200ms);
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFenceNull_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2217,12 +2624,12 @@
.setAcquireFence(layer, fence)
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetDataspaceBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2239,12 +2646,12 @@
.setDataspace(layer, ui::Dataspace::UNKNOWN)
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetHdrMetadataBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2263,12 +2670,12 @@
.setHdrMetadata(layer, hdrMetadata)
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2287,12 +2694,12 @@
.setSurfaceDamageRegion(layer, region)
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetApiBasic_BufferState) {
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2309,7 +2716,7 @@
.setApi(layer, NATIVE_WINDOW_API_CPU)
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -2323,141 +2730,21 @@
Transaction().setSidebandStream(layer, nullptr).apply();
}
-TEST_F(LayerTransactionTest, CacheBuffer_BufferState) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- int32_t bufferId = -1;
- ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
- ASSERT_GE(bufferId, 0);
-
- ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
-}
-
-TEST_F(LayerTransactionTest, CacheBuffers_BufferState) {
- std::vector<int32_t> bufferIds;
- int32_t bufferCount = 20;
-
- for (int i = 0; i < bufferCount; i++) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- int32_t bufferId = -1;
- ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
- if (bufferId < 0) {
- EXPECT_GE(bufferId, 0);
- break;
- }
- bufferIds.push_back(bufferId);
- }
-
- for (int32_t bufferId : bufferIds) {
- ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
- }
-}
-
-TEST_F(LayerTransactionTest, CacheBufferInvalid_BufferState) {
- sp<GraphicBuffer> buffer = nullptr;
-
- int32_t bufferId = -1;
- ASSERT_NE(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
- ASSERT_LT(bufferId, 0);
-}
-
-TEST_F(LayerTransactionTest, UncacheBufferTwice_BufferState) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- int32_t bufferId = -1;
- ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
- ASSERT_GE(bufferId, 0);
-
- ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
- mClient->uncacheBuffer(bufferId);
-}
-
-TEST_F(LayerTransactionTest, UncacheBufferInvalidId_BufferState) {
- mClient->uncacheBuffer(-1);
- mClient->uncacheBuffer(0);
- mClient->uncacheBuffer(1);
- mClient->uncacheBuffer(5);
- mClient->uncacheBuffer(1000);
-}
-
-TEST_F(LayerTransactionTest, SetCachedBuffer_BufferState) {
+TEST_F(LayerTransactionTest, ReparentToSelf) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().reparent(layer, layer->getHandle()).apply();
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
-
- fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- int32_t bufferId = -1;
- ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
- ASSERT_GE(bufferId, 0);
-
- Transaction().setCachedBuffer(layer, bufferId).apply();
-
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-
- ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
-}
-
-TEST_F(LayerTransactionTest, SetCachedBufferDelayed_BufferState) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
- sp<GraphicBuffer> cachedBuffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- int32_t bufferId = -1;
- ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(cachedBuffer, &bufferId));
- ASSERT_GE(bufferId, 0);
-
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY,
- "test");
- fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::BLUE);
- Transaction().setBuffer(layer, buffer).apply();
{
- SCOPED_TRACE("Uncached buffer");
-
+ // We expect the transaction to be silently dropped, but for SurfaceFlinger
+ // to still be functioning.
+ SCOPED_TRACE("after reparent to self");
+ const Rect rect(0, 0, 32, 32);
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
-
- fillGraphicBufferColor(cachedBuffer, Rect(0, 0, 32, 32), Color::RED);
- Transaction().setCachedBuffer(layer, bufferId).apply();
- {
- SCOPED_TRACE("Cached buffer");
-
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
}
class ColorTransformHelper {
@@ -2501,7 +2788,7 @@
}
};
-TEST_F(LayerTransactionTest, SetColorTransformBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(colorLayer =
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
@@ -2512,7 +2799,7 @@
.apply();
{
SCOPED_TRACE("default color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2544,11 +2831,11 @@
.setColorTransform(colorLayer, matrix, vec3()).apply();
{
SCOPED_TRACE("new color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
}
}
-TEST_F(LayerTransactionTest, SetColorTransformOnParent) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
sp<SurfaceControl> parentLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
@@ -2565,7 +2852,7 @@
.apply();
{
SCOPED_TRACE("default color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2599,11 +2886,11 @@
.apply();
{
SCOPED_TRACE("new color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
}
}
-TEST_F(LayerTransactionTest, SetColorTransformOnChildAndParent) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
sp<SurfaceControl> parentLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
@@ -2620,7 +2907,7 @@
.apply();
{
SCOPED_TRACE("default color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
@@ -2660,10 +2947,21 @@
.apply();
{
SCOPED_TRACE("new color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
}
}
+struct CallbackData {
+ CallbackData() = default;
+ CallbackData(nsecs_t time, const sp<Fence>& fence,
+ const std::vector<SurfaceControlStats>& stats)
+ : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+ nsecs_t latchTime;
+ sp<Fence> presentFence;
+ std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
class ExpectedResult {
public:
enum Transaction {
@@ -2691,8 +2989,7 @@
ExpectedResult::Buffer bufferResult = ACQUIRED,
ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
mTransactionResult = transactionResult;
- mExpectedSurfaceResults.emplace(std::piecewise_construct,
- std::forward_as_tuple(layer->getHandle()),
+ mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
std::forward_as_tuple(bufferResult, previousBufferResult));
}
@@ -2709,8 +3006,8 @@
mExpectedPresentTime = expectedPresentTime;
}
- void verifyTransactionStats(const TransactionStats& transactionStats) const {
- const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
+ void verifyCallbackData(const CallbackData& callbackData) const {
+ const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
ASSERT_GE(latchTime, 0) << "bad latch time";
ASSERT_NE(presentFence, nullptr);
@@ -2727,14 +3024,16 @@
ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
}
- ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size())
+ ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
<< "wrong number of surfaces";
- for (const auto& stats : surfaceStats) {
+ for (const auto& stats : surfaceControlStats) {
+ ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
<< "unexpected surface control";
- expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime);
+ expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
}
}
@@ -2745,8 +3044,9 @@
ExpectedResult::PreviousBuffer previousBufferResult)
: mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
- void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const {
- const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceStats;
+ void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+ nsecs_t latchTime) const {
+ const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
<< "bad acquire time";
@@ -2766,39 +3066,40 @@
ExpectedResult::PreviousBuffer mPreviousBufferResult;
};
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& strongPointer) const {
- return std::hash<IBinder*>{}(strongPointer.get());
+ struct SCHash {
+ std::size_t operator()(const sp<SurfaceControl>& sc) const {
+ return std::hash<IBinder*>{}(sc->getHandle().get());
}
};
ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
nsecs_t mExpectedPresentTime = -1;
- std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
+ std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
};
class CallbackHelper {
public:
- static void function(void* callbackContext, const TransactionStats& transactionStats) {
+ static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
if (!callbackContext) {
ALOGE("failed to get callback context");
}
CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
std::lock_guard lock(helper->mMutex);
- helper->mTransactionStatsQueue.push(transactionStats);
+ helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
helper->mConditionVariable.notify_all();
}
- void getTransactionStats(TransactionStats* outStats) {
+ void getCallbackData(CallbackData* outData) {
std::unique_lock lock(mMutex);
- if (mTransactionStatsQueue.empty()) {
+ if (mCallbackDataQueue.empty()) {
ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
std::cv_status::timeout)
<< "did not receive callback";
}
- *outStats = std::move(mTransactionStatsQueue.front());
- mTransactionStatsQueue.pop();
+ *outData = std::move(mCallbackDataQueue.front());
+ mCallbackDataQueue.pop();
}
void verifyFinalState() {
@@ -2806,15 +3107,15 @@
std::this_thread::sleep_for(500ms);
std::lock_guard lock(mMutex);
- EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received";
- mTransactionStatsQueue = {};
+ EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ mCallbackDataQueue = {};
}
void* getContext() { return static_cast<void*>(this); }
std::mutex mMutex;
std::condition_variable mConditionVariable;
- std::queue<TransactionStats> mTransactionStatsQueue;
+ std::queue<CallbackData> mCallbackDataQueue;
};
class LayerCallbackTest : public LayerTransactionTest {
@@ -2824,16 +3125,25 @@
}
static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
- const sp<SurfaceControl>& layer = nullptr) {
+ const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
+ bool setBackgroundColor = false) {
if (layer) {
sp<GraphicBuffer> buffer;
sp<Fence> fence;
- int err = getBuffer(&buffer, &fence);
- if (err != NO_ERROR) {
- return err;
+ if (setBuffer) {
+ int err = getBuffer(&buffer, &fence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transaction.setBuffer(layer, buffer);
+ transaction.setAcquireFence(layer, fence);
}
- transaction.setBuffer(layer, buffer).setAcquireFence(layer, fence);
+ if (setBackgroundColor) {
+ transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
+ ui::Dataspace::UNKNOWN);
+ }
}
transaction.addTransactionCompletedCallback(callbackHelper->function,
@@ -2843,9 +3153,9 @@
static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
bool finalState = false) {
- TransactionStats transactionStats;
- ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats));
- EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats));
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+ EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
if (finalState) {
ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
@@ -2864,13 +3174,13 @@
}
};
-TEST_F(LayerCallbackTest, Basic) {
+TEST_F(LayerCallbackTest, BufferColor) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
Transaction transaction;
CallbackHelper callback;
- int err = fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer, true, true);
if (err) {
GTEST_SUCCEED() << "test not supported";
return;
@@ -2883,13 +3193,13 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
-TEST_F(LayerCallbackTest, NoBuffer) {
+TEST_F(LayerCallbackTest, NoBufferNoColor) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
Transaction transaction;
CallbackHelper callback;
- int err = fillTransaction(transaction, &callback);
+ int err = fillTransaction(transaction, &callback, layer, false, false);
if (err) {
GTEST_SUCCEED() << "test not supported";
return;
@@ -2903,6 +3213,45 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+TEST_F(LayerCallbackTest, BufferNoColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true, false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
TEST_F(LayerCallbackTest, NoStateChange) {
Transaction transaction;
CallbackHelper callback;
@@ -2937,7 +3286,7 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
-TEST_F(LayerCallbackTest, Merge) {
+TEST_F(LayerCallbackTest, MergeBufferNoColor) {
sp<SurfaceControl> layer1, layer2;
ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
@@ -2964,6 +3313,62 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
}
+TEST_F(LayerCallbackTest, MergeNoBufferColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
TEST_F(LayerCallbackTest, Merge_SameCallback) {
sp<SurfaceControl> layer1, layer2;
ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
@@ -3565,10 +3970,11 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- sp<IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
@@ -4029,7 +4435,7 @@
mCapture->checkPixel(64, 64, 111, 111, 111);
}
- mChild->clear();
+ mChild.clear();
{
SCOPED_TRACE("After destroying child");
@@ -4901,7 +5307,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
auto redLayerHandle = redLayer->getHandle();
- redLayer->clear();
+ redLayer.clear();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index a2c0611..68cf61e 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -31,6 +31,7 @@
"libutils",
],
static_libs: [
+ "libcompositionengine",
"libgmock",
"librenderengine",
"libtrace_proto",
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 16e0891..f9e0b64 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -145,7 +145,7 @@
void TearDown() override;
void waitForDisplayTransaction();
- bool waitForHotplugEvent(uint32_t id, bool connected);
+ bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected);
sp<IComposer> mFakeService;
sp<SurfaceComposerClient> mComposerClient;
@@ -242,7 +242,7 @@
mMockComposer->runVSyncAndWait();
}
-bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
+bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
@@ -250,11 +250,12 @@
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: id %d, connected %d\t", event.header.id,
- event.hotplug.connected);
+ "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", connected %d\t",
+ event.header.displayId, event.hotplug.connected);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
- event.header.id == id && event.hotplug.connected == connected) {
+ event.header.displayId == displayId && event.hotplug.connected == connected) {
return true;
}
}
@@ -294,13 +295,14 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
@@ -328,14 +330,15 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
@@ -364,11 +367,12 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_NE(NO_ERROR, result);
@@ -402,11 +406,12 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_EQ(NO_ERROR, result);
@@ -473,10 +478,11 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ALOGI("TransactionTest::SetUp - display");
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
mDisplayWidth = info.w;
mDisplayHeight = info.h;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index ad31a40..b1d45f39 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,10 +42,13 @@
"EventThreadTest.cpp",
"IdleTimerTest.cpp",
"LayerHistoryTest.cpp",
+ "LayerMetadataTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "RefreshRateStatsTest.cpp",
"TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
+ "mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/gui/MockGraphicBufferConsumer.cpp",
"mock/gui/MockGraphicBufferProducer.cpp",
@@ -55,6 +58,7 @@
"mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
+ "mock/MockTimeStats.cpp",
"mock/system/window/MockNativeWindow.cpp",
],
static_libs: [
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
index 2245ee1..8bed766 100644
--- a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
+++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
@@ -75,14 +75,16 @@
template <typename... Args>
class AsyncCallRecorder<void (*)(Args...)> {
public:
- // For the tests, we expect the wait for an expected change to be signaled
- // to be much shorter than this.
- static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
+ // This wait value needs to be large enough to avoid flakes caused by delays
+ // scheduling threads, but small enough that tests don't take forever if
+ // something really is wrong. Based on some empirical evidence, 100ms should
+ // be enough to avoid the former.
+ static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{100};
- // The wait here is tricky. We don't expect a change, but we don't want to
- // wait forever (or for longer than the typical test function runtime). As
- // even the simplest Google Test can take 1ms (1000us) to run, we wait for
- // half that time.
+ // The wait here is tricky. It's for when We don't expect to record a call,
+ // but we don't want to wait forever (or for longer than the typical test
+ // function runtime). As even the simplest Google Test can take 1ms (1000us)
+ // to run, we wait for half that time.
static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e972785..3fb8708 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,8 +45,10 @@
using testing::_;
using testing::AtLeast;
+using testing::Between;
using testing::ByMove;
using testing::DoAll;
+using testing::Field;
using testing::Invoke;
using testing::IsNull;
using testing::Mock;
@@ -95,6 +97,7 @@
EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
EXPECT_CALL(*mPrimaryDispSync, getPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
@@ -158,7 +161,6 @@
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::DispSync* mPrimaryDispSync = new mock::DispSync();
- renderengine::mock::Image* mReImage = new renderengine::mock::Image();
renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -238,16 +240,23 @@
static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
static void setupPreconditions(CompositionTest* test) {
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
true /* isPrimary */)
.setCapabilities(&test->mDefaultCapabilities)
.inject(&test->mFlinger, test->mComposer);
+ Mock::VerifyAndClear(test->mComposer);
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
false /* isVirtual */, true /* isPrimary */)
.setDisplaySurface(test->mDisplaySurface)
@@ -255,6 +264,7 @@
.setSecure(Derived::IS_SECURE)
.setPowerMode(Derived::INIT_POWER_MODE)
.inject();
+ Mock::VerifyAndClear(test->mNativeWindow);
test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
}
@@ -270,11 +280,10 @@
EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
- EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
- EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
-
+ // TODO: remove once we verify that we can just grab the fence from the
+ // FramebufferSurface.
EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
- return base::unique_fd(0);
+ return base::unique_fd();
}));
EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
@@ -286,36 +295,18 @@
template <typename Case>
static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
- // Called once with a non-null value to set a framebuffer, and then
- // again with nullptr to clear it.
- EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
- .WillOnce(Return(true));
- EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
- .WillOnce(Return(true));
-
- EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
- EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
- .WillOnce(Return(
- ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
- EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, clearWithColor(0, 0, 0, 1)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd())));
- EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true));
-
- EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
- .Times(1);
- // This expectation retires on saturation as setViewportAndProjection is
- // called an extra time for the code path this setup is for.
- // TODO: Investigate this extra call
- EXPECT_CALL(*test->mRenderEngine,
- setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
- Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Transform::ROT_0))
- .Times(1)
- .RetiresOnSaturation();
- EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1);
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillRepeatedly(
+ [](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+ ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ return NO_ERROR;
+ });
}
static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -339,31 +330,23 @@
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
- EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
- .Times(1);
- EXPECT_CALL(*test->mRenderEngine, setColorTransform(_)).Times(2);
- // These expectations retire on saturation as the code path these
- // expectations are for appears to make an extra call to them.
- // TODO: Investigate this extra call
- EXPECT_CALL(*test->mRenderEngine,
- setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
- Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Transform::ROT_0))
- .Times(1);
- EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
- .WillOnce(Return(true));
- EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
- .WillOnce(Return(true));
- EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
- .WillOnce(Return(
- ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
- EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
.WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
Return(0)));
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillRepeatedly(
+ [](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+ ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+ return NO_ERROR;
+ });
}
template <typename Case>
@@ -374,8 +357,6 @@
template <typename Case>
static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
Case::Layer::setupREScreenshotCompositionCallExpectations(test);
-
- EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
}
};
@@ -387,16 +368,11 @@
template <typename Case>
static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
Case::Layer::setupInsecureRECompositionCallExpectations(test);
-
- // TODO: Investigate this extra call
- EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
}
template <typename Case>
static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test);
-
- EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
}
};
@@ -405,6 +381,8 @@
template <typename Case>
static void setupCommonCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mComposer,
setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
@@ -417,6 +395,8 @@
static void setupHwcCompositionCallExpectations(CompositionTest*) {}
static void setupRECompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -503,7 +483,6 @@
bool ignoredRecomputeVisibleRegions;
layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE);
Mock::VerifyAndClear(test->mRenderEngine);
- Mock::VerifyAndClear(test->mReImage);
}
static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
@@ -575,7 +554,6 @@
setLayerColor(HWC_DISPLAY, HWC_LAYER,
IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
.Times(1);
-
}
static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
@@ -586,33 +564,35 @@
}
static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine,
- setupLayerBlending(true, false, false,
- half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
- LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
- 0.0f))
- .Times(1);
-
- EXPECT_CALL(*test->mRenderEngine, createImage())
- .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
- EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
- // This call retires on saturation as the code that renders a texture disables the state,
- // along with a top-level disable to ensure it is disabled for non-buffer layers.
- EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so gtet the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
+ EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
+ EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE,
+ layer.source.buffer.cacheHint);
+ EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
+ EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
+ EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+ EXPECT_EQ(false, layer.source.buffer.isOpaque);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return NO_ERROR;
+ });
}
static void setupREBufferCompositionCallExpectations(CompositionTest* test) {
LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
-
- // TODO - Investigate and eliminate these differences between display
- // composition and screenshot composition.
- EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
}
static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -627,20 +607,28 @@
LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
}
- static void setupREColorCompositionCommonCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
- }
-
static void setupREColorCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
- EXPECT_CALL(*test->mRenderEngine,
- setupLayerBlending(true, false, true,
- half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
- LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
- 0.0f))
- .Times(1);
- EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so get the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+ EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+ LayerProperties::COLOR[2]),
+ layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return NO_ERROR;
+ });
}
static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) {
@@ -680,10 +668,7 @@
EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
}
- static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, setupFillWithColor(0, 0, 0, 1)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
- }
+ static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
};
struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
@@ -692,24 +677,25 @@
static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, createImage())
- .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
- EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1);
-
- EXPECT_CALL(*test->mRenderEngine,
- setupLayerBlending(true, false, false,
- half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2],
- Base::COLOR[3]), 0.0f))
- .Times(1);
- EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
- EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
- // This call retires on saturation as the code that renders a texture disables the state,
- // along with a top-level disable to ensure it is disabled for non-buffer layers.
- EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so get the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(1.0f, layer.alpha);
+ return NO_ERROR;
+ });
}
static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -760,7 +746,7 @@
layerDrawingState.active.h = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-
+ layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
return layer;
@@ -813,7 +799,6 @@
}
static void setupRECompositionCallExpectations(CompositionTest* test) {
- LayerProperties::setupREColorCompositionCommonCallExpectations(test);
LayerProperties::setupREColorCompositionCallExpectations(test);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 8201704..6659d4a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -45,9 +45,9 @@
namespace {
using testing::_;
-using testing::ByMove;
using testing::DoAll;
using testing::Mock;
+using testing::ResultOf;
using testing::Return;
using testing::SetArgPointee;
@@ -118,6 +118,7 @@
TestableSurfaceFlinger mFlinger;
mock::EventThread* mEventThread = new mock::EventThread();
+ mock::EventThread* mSFEventThread = new mock::EventThread();
mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -161,6 +162,7 @@
mFlinger.mutableEventControlThread().reset(mEventControlThread);
mFlinger.mutableEventThread().reset(mEventThread);
+ mFlinger.mutableSFEventThread().reset(mSFEventThread);
mFlinger.mutableEventQueue().reset(mMessageQueue);
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
@@ -328,11 +330,17 @@
injector.setNativeWindow(test->mNativeWindow);
// Creating a DisplayDevice requires getting default dimensions from the
- // native window.
+ // native window along with some other initial setup.
EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
return injector;
}
@@ -345,6 +353,12 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
}
static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -382,7 +396,7 @@
}
// Called by tests to inject a HWC display setup
- static void injectHwcDisplay(DisplayTransactionTest* test) {
+ static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
@@ -394,6 +408,14 @@
.inject(&test->mFlinger, test->mComposer);
}
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplay(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+ injectHwcDisplayWithNoDefaultCapabilities(test);
+ }
+
static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>(
@@ -423,6 +445,9 @@
getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
IComposerClient::Attribute::DPI_Y, _))
.WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -475,6 +500,8 @@
struct TertiaryDisplay {
static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 253;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
};
// A primary display is a physical display that is critical
@@ -570,6 +597,8 @@
}
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
+
EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
Return(Error::NONE)));
@@ -1149,9 +1178,9 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(9)).Times(1);
- EXPECT_CALL(*mNativeWindow, perform(13)).Times(1);
- EXPECT_CALL(*mNativeWindow, perform(30)).Times(1);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
auto displayDevice = mInjector.inject();
displayDevice->getCompositionDisplay()
@@ -1219,6 +1248,116 @@
}
/* ------------------------------------------------------------------------
+ * SurfaceFlinger::getDisplayNativePrimaries
+ */
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+ GetDisplayNativePrimaries();
+ void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+ void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+ static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+ SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+ ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ primaries.red.X = startingVal++;
+ primaries.red.Y = startingVal++;
+ primaries.red.Z = startingVal++;
+ primaries.green.X = startingVal++;
+ primaries.green.Y = startingVal++;
+ primaries.green.Z = startingVal++;
+ primaries.blue.X = startingVal++;
+ primaries.blue.Y = startingVal++;
+ primaries.blue.Z = startingVal++;
+ primaries.white.X = startingVal++;
+ primaries.white.Y = startingVal++;
+ primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+ const ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ EXPECT_EQ(primaries.red.X, startingVal++);
+ EXPECT_EQ(primaries.red.Y, startingVal++);
+ EXPECT_EQ(primaries.red.Z, startingVal++);
+ EXPECT_EQ(primaries.green.X, startingVal++);
+ EXPECT_EQ(primaries.green.Y, startingVal++);
+ EXPECT_EQ(primaries.green.Z, startingVal++);
+ EXPECT_EQ(primaries.blue.X, startingVal++);
+ EXPECT_EQ(primaries.blue.Y, startingVal++);
+ EXPECT_EQ(primaries.blue.Z, startingVal++);
+ EXPECT_EQ(primaries.white.X, startingVal++);
+ EXPECT_EQ(primaries.white.Y, startingVal++);
+ EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithDefaultPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+ // A nullptr would trigger a different execution path than what's being tested here
+ EXPECT_NE(nullptr, internalDisplayToken.get());
+
+ mFlinger.initDefaultDisplayNativePrimaries();
+
+ ui::DisplayPrimaries primaries;
+ // Expecting sRGB primaries
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+ EXPECT_EQ(primaries.red.X, 0.4123f);
+ EXPECT_EQ(primaries.red.Y, 0.2126f);
+ EXPECT_EQ(primaries.red.Z, 0.0193f);
+ EXPECT_EQ(primaries.green.X, 0.3576f);
+ EXPECT_EQ(primaries.green.Y, 0.7152f);
+ EXPECT_EQ(primaries.green.Z, 0.1192f);
+ EXPECT_EQ(primaries.blue.X, 0.1805f);
+ EXPECT_EQ(primaries.blue.Y, 0.0722f);
+ EXPECT_EQ(primaries.blue.Z, 0.9506f);
+ EXPECT_EQ(primaries.white.X, 0.9505f);
+ EXPECT_EQ(primaries.white.Y, 1.0000f);
+ EXPECT_EQ(primaries.white.Z, 1.0891f);
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+
+ ui::DisplayPrimaries expectedPrimaries;
+ populateDummyDisplayNativePrimaries(expectedPrimaries);
+ mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+ sp<BBinder> notInternalDisplayToken = new BBinder();
+
+ ui::DisplayPrimaries primaries;
+ populateDummyDisplayNativePrimaries(primaries);
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+ // Check primaries argument wasn't modified in case of failure
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+/* ------------------------------------------------------------------------
* SurfaceFlinger::setupNewDisplayDeviceInternal
*/
@@ -1353,6 +1492,9 @@
template <typename Case>
void setupCommonPreconditions();
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
template <typename Case>
void setupCommonCallExpectationsForConnectProcessing();
@@ -1390,6 +1532,17 @@
injectFakeNativeWindowSurfaceFactory();
}
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+ const auto convert = [](auto physicalDisplayId) {
+ return std::make_optional(DisplayId{physicalDisplayId});
+ };
+
+ EXPECT_CALL(*eventThread,
+ onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+ .Times(1);
+}
+
template <typename Case>
void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
Case::Display::setupHwcHotplugCallExpectations(this);
@@ -1404,23 +1557,17 @@
Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
- EXPECT_CALL(*mEventThread,
- onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
- ? EventThread::DisplayType::Primary
- : EventThread::DisplayType::External,
- true))
- .Times(1);
+
+ expectHotplugReceived<Case, true>(mEventThread);
+ expectHotplugReceived<Case, true>(mSFEventThread);
}
template <typename Case>
void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
- EXPECT_CALL(*mEventThread,
- onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
- ? EventThread::DisplayType::Primary
- : EventThread::DisplayType::External,
- false))
- .Times(1);
+
+ expectHotplugReceived<Case, false>(mEventThread);
+ expectHotplugReceived<Case, false>(mSFEventThread);
}
template <typename Case>
@@ -1594,6 +1741,13 @@
PrimaryDisplayVariant::injectHwcDisplay(this);
ExternalDisplayVariant::injectHwcDisplay(this);
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+ SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
@@ -1741,6 +1895,10 @@
Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ EXPECT_CALL(*mComposer, getDisplayCapabilities(Case::Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+
EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
@@ -1753,6 +1911,7 @@
EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
+ EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
@@ -1985,9 +2144,9 @@
.WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(9)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(30)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -2029,9 +2188,9 @@
.WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(9)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(30)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -2545,6 +2704,8 @@
// processing.
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+
// --------------------------------------------------------------------
// Invocation
@@ -2839,7 +3000,7 @@
using Transition = TransitionVariant;
static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
- Display::injectHwcDisplay(test);
+ Display::injectHwcDisplayWithNoDefaultCapabilities(test);
auto display = Display::makeFakeExistingDisplayInjector(test);
display.inject();
display.mutableDisplayDevice()->setPowerMode(mode);
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index c18068f..3a7cfba 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -36,6 +36,9 @@
namespace android {
namespace {
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
+
class MockVSyncSource : public VSyncSource {
public:
MOCK_METHOD1(setVSyncEnabled, void(bool));
@@ -50,8 +53,10 @@
class MockEventThreadConnection : public EventThreadConnection {
public:
MockEventThreadConnection(android::impl::EventThread* eventThread,
- ResyncCallback&& resyncCallback)
- : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
+ ResyncCallback&& resyncCallback,
+ ResetIdleTimerCallback&& resetIdleTimerCallback)
+ : EventThreadConnection(eventThread, std::move(resyncCallback),
+ std::move(resetIdleTimerCallback)) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -72,17 +77,19 @@
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
- void expectHotplugEventReceivedByConnection(EventThread::DisplayType expectedDisplayType,
+ void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
+ AsyncCallRecorder<void (*)()> mResetIdleTimerCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
MockVSyncSource mVSyncSource;
+ VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<android::impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
};
@@ -103,12 +110,19 @@
createThread();
mConnection = createConnection(mConnectionEventCallRecorder);
+
+ // A display must be connected for VSYNC events to be delivered.
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
EventThreadTest::~EventThreadTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ // EventThread should unregister itself as VSyncSource callback.
+ EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
}
void EventThreadTest::createThread() {
@@ -116,12 +130,17 @@
std::make_unique<android::impl::EventThread>(&mVSyncSource,
mInterceptVSyncCallRecorder.getInvocable(),
"unit-test-event-thread");
+
+ // EventThread should register itself as VSyncSource callback.
+ mCallback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(mCallback);
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
ConnectionEventRecorder& recorder) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable());
+ new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
+ mResetIdleTimerCallRecorder.getInvocable());
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -171,16 +190,13 @@
expectedCount);
}
-void EventThreadTest::expectHotplugEventReceivedByConnection(
- EventThread::DisplayType expectedDisplayType, bool expectedConnected) {
- const uint32_t expectedDisplayId =
- expectedDisplayType == EventThread::DisplayType::Primary ? 0 : 1;
-
+void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
- EXPECT_EQ(expectedDisplayId, event.header.id);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConnected, event.hotplug.connected);
}
@@ -195,33 +211,43 @@
EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mResetIdleTimerCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
}
-TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
- // Signal that we want the next vsync event to be posted to the connection
+TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
+
+ // Signal that we want the next vsync event to be posted to the connection.
mThread->requestNextVsync(mConnection, false);
- // EventThread should immediately request a resync.
+ // EventThread should not enable vsync callbacks.
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection, true);
+
+ // EventThread should immediately reset the idle timer and request a resync.
+ EXPECT_TRUE(mResetIdleTimerCallRecorder.waitForCall().has_value());
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Use the received callback to signal a first vsync event.
// The interceptor should receive the event, as well as the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
// The interceptor should receive the event, but the the connection should
// not as it was only interested in the first.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -246,16 +272,13 @@
createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the second connection. The first connection should not
// get the event.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -265,25 +288,22 @@
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
mThread->setVsyncRate(1, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
- callback->onVSyncEvent(789);
+ mCallback->onVSyncEvent(789);
expectInterceptCallReceived(789);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -291,29 +311,26 @@
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
mThread->setVsyncRate(2, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// The third event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(789);
+ mCallback->onVSyncEvent(789);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
- callback->onVSyncEvent(101112);
+ mCallback->onVSyncEvent(101112);
expectInterceptCallReceived(101112);
expectVsyncEventReceivedByConnection(101112, 4u);
}
@@ -321,17 +338,14 @@
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
mThread->setVsyncRate(1, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Destroy the only (strong) reference to the connection.
mConnection = nullptr;
// The first event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -344,21 +358,18 @@
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor and not by the
// connection.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
@@ -371,21 +382,18 @@
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and by the connection,
// which then returns an non-fatal error.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor, and by the connection,
// which still then returns an non-fatal error.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
@@ -398,24 +406,24 @@
expectVSyncSetPhaseOffsetCallReceived(321);
}
-TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
- mThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
- expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false);
+TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
-TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
- mThread->onHotplugReceived(EventThread::DisplayType::Primary, true);
- expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true);
+TEST_F(EventThreadTest, postHotplugInternalConnect) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
- mThread->onHotplugReceived(EventThread::DisplayType::External, false);
- expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, false);
+ mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
- mThread->onHotplugReceived(EventThread::DisplayType::External, true);
- expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, true);
+ mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
new file mode 100644
index 0000000..0739f15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/PhaseOffsets.h"
+
+namespace android {
+namespace scheduler {
+
+class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
+ nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+
+public:
+ FakePhaseOffsets() = default;
+ ~FakePhaseOffsets() = default;
+
+ nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
+ nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ PhaseOffsets::Offsets getCurrentOffsets() const override {
+ return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& /*result*/) const override {}
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
index 9fe9a18..5e82225 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
@@ -34,93 +33,178 @@
IdleTimerTest() = default;
~IdleTimerTest() override = default;
+ // This timeout should be used when a 3ms callback is expected.
+ // While the tests typically request a callback after 3ms, the scheduler
+ // does not always cooperate, at it can take significantly longer (observed
+ // 30ms).
+ static constexpr auto waitTimeForExpected3msCallback = 100ms;
+
+ // This timeout should be used when an 3ms callback is not expected.
+ // Note that there can be false-negatives if the callback happens later.
+ static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
+
+ AsyncCallRecorder<void (*)()> mResetTimerCallback;
AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
std::unique_ptr<IdleTimer> mIdleTimer;
+
+ void clearPendingCallbacks() {
+ while (mExpiredTimerCallback.waitForCall(0us).has_value()) {
+ }
+ }
};
namespace {
TEST_F(IdleTimerTest, createAndDestroyTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, [] {});
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
}
TEST_F(IdleTimerTest, startStopTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ auto startTime = std::chrono::steady_clock::now();
mIdleTimer->start();
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- // The timer expires after 30 ms, so the call to the callback should not happen.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // The idle timer fires after 30ms, so there should be no callback within
+ // 25ms (waiting for a callback for the full 30ms would be problematic).
+ bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
+ // Under ideal conditions there should be no event. But occasionally
+ // it is possible that the wait just prior takes more than 30ms, and
+ // a callback is observed. We check the elapsed time since before the IdleTimer
+ // thread was started as a sanity check to not have a flakey test.
+ EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(25));
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
}
TEST_F(IdleTimerTest, resetTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- // The timer expires after 30 ms, so the call to the callback should not happen.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // Observe any event that happens in about 25ms. We don't care if one was
+ // observed or not.
+ mExpiredTimerCallback.waitForCall(25ms).has_value();
mIdleTimer->reset();
- // The timer was reset, so the call to the callback should not happen.
- std::this_thread::sleep_for(std::chrono::milliseconds(15));
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // There may have been a race with the reset. Clear any callbacks we
+ // received right afterwards.
+ clearPendingCallbacks();
+ // A single callback should be generated after 30ms
+ EXPECT_TRUE(
+ mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ // After one event, it should be idle, and not generate another.
+ EXPECT_FALSE(
+ mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback * 10).has_value());
mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, resetBackToBackTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ // A single callback should be generated after 30ms
+ EXPECT_TRUE(
+ mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
TEST_F(IdleTimerTest, startNotCalledTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
- std::this_thread::sleep_for(6ms);
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
// The start hasn't happened, so the callback does not happen.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1us).has_value());
mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
TEST_F(IdleTimerTest, idleTimerIdlesTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
- std::this_thread::sleep_for(6ms);
- // The timer expires after 3 ms, so the call to the callback happens.
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
- std::this_thread::sleep_for(6ms);
- // Timer can be idle.
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
- // Timer can be reset.
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ // A callback should be generated after 3ms
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ // After one event, it should be idle, and not generate another.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ // Once reset, it should generate another
mIdleTimer->reset();
- std::this_thread::sleep_for(6ms);
- // Timer fires again.
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
-
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
- std::this_thread::sleep_for(6ms);
- // The timer expires after 3 ms, so the call to the callback should happen.
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1us).has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
mIdleTimer->stop();
}
TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
- std::this_thread::sleep_for(6ms);
- EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
mIdleTimer->stop();
+ clearPendingCallbacks();
mIdleTimer->reset();
- std::this_thread::sleep_for(6ms);
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
}
TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
- std::this_thread::sleep_for(1ms);
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
+
mIdleTimer->stop();
- std::this_thread::sleep_for(3ms);
- EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+ clearPendingCallbacks();
+ mIdleTimer->reset();
+
+ // No more idle events should be observed
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
}
} // namespace
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
new file mode 100644
index 0000000..92c9f92
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <binder/Parcel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+class LayerMetadataTest : public testing::Test {
+public:
+ LayerMetadataTest();
+ ~LayerMetadataTest() override;
+};
+
+LayerMetadataTest::LayerMetadataTest() {
+ 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());
+}
+
+LayerMetadataTest::~LayerMetadataTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+TEST_F(LayerMetadataTest, testLayerMetadata) {
+ LayerMetadata metadata;
+
+ ASSERT_EQ(0, metadata.mMap.size());
+
+ // Test non-set
+ ASSERT_EQ(3, metadata.getInt32(4, 3));
+
+ // Make sure it's still unset
+ ASSERT_EQ(5, metadata.getInt32(4, 5));
+
+ metadata.setInt32(4, 2);
+ ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+ // data is too small
+ metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'};
+ ASSERT_EQ(0, metadata.getInt32(2, 0));
+
+ LayerMetadata second;
+ std::vector<uint8_t> someData{'c', 'd', '\0'};
+ second.mMap[2] = someData;
+ second.setInt32(6, 5);
+ metadata.merge(second);
+
+ ASSERT_EQ(3, metadata.mMap.size());
+ ASSERT_EQ(someData, second.mMap[2]);
+ ASSERT_EQ(5, metadata.getInt32(6, 0));
+ ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+ Parcel p;
+ metadata.writeToParcel(&p);
+ LayerMetadata reconstructed;
+ reconstructed.setInt32(3, 1); // to make sure it gets replaced
+ p.setDataPosition(0);
+ reconstructed.readFromParcel(&p);
+ ASSERT_EQ(metadata.mMap, reconstructed.mMap);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
new file mode 100644
index 0000000..3d887ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/RefreshRateStats.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockTimeStats.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+class RefreshRateStatsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_90 = 0;
+ static constexpr int CONFIG_ID_60 = 1;
+ static constexpr int64_t VSYNC_90 = 11111111;
+ static constexpr int64_t VSYNC_60 = 16666667;
+
+ RefreshRateStatsTest();
+ ~RefreshRateStatsTest();
+
+ void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs);
+
+ std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+ std::shared_ptr<android::mock::TimeStats> mTimeStats;
+};
+
+RefreshRateStatsTest::RefreshRateStatsTest() {
+ 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());
+}
+
+RefreshRateStatsTest::~RefreshRateStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
+ mTimeStats = std::make_shared<android::mock::TimeStats>();
+ mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ init(configs);
+
+ // There is one default config, so the refresh rates should have one item.
+ ASSERT_EQ(1, mRefreshRateStats->getTotalTimes().size());
+}
+
+TEST_F(RefreshRateStatsTest, oneConfigTest) {
+ auto display = new Hwc2::mock::Display();
+
+ auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config.setVsyncPeriod(VSYNC_90);
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ configs.push_back(config.build());
+
+ init(configs);
+
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(4);
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(2);
+
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(2, times.size());
+ ASSERT_EQ(0, times["ScreenOff"]);
+ ASSERT_EQ(0, times["90fps"]);
+ // Setting up tests on mobile harness can be flaky with time passing, so testing for
+ // exact time changes can result in flaxy numbers. To avoid that remember old
+ // numbers to make sure the correct values are increasing in the next test.
+ int screenOff = times["ScreenOff"];
+ int ninety = times["90fps"];
+
+ // Screen is off by default.
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(0, times["90fps"]);
+ screenOff = times["ScreenOff"];
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(screenOff, times["ScreenOff"]);
+ ASSERT_LT(ninety, times["90fps"]);
+ ninety = times["90fps"];
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ screenOff = times["ScreenOff"];
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+ // does not update refresh rates that come from the config.
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+}
+
+TEST_F(RefreshRateStatsTest, twoConfigsTest) {
+ auto display = new Hwc2::mock::Display();
+
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ configs.push_back(config90.build());
+
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ configs.push_back(config60.build());
+
+ init(configs);
+
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(6);
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(60, _)).Times(4);
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(4);
+
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(3, times.size());
+ ASSERT_EQ(0, times["ScreenOff"]);
+ ASSERT_EQ(0, times["60fps"]);
+ ASSERT_EQ(0, times["90fps"]);
+ // Setting up tests on mobile harness can be flaky with time passing, so testing for
+ // exact time changes can result in flaxy numbers. To avoid that remember old
+ // numbers to make sure the correct values are increasing in the next test.
+ int screenOff = times["ScreenOff"];
+ int sixty = times["60fps"];
+ int ninety = times["90fps"];
+
+ // Screen is off by default.
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(sixty, times["60fps"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ screenOff = times["ScreenOff"];
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(sixty, times["60fps"]);
+ ASSERT_LT(ninety, times["90fps"]);
+ ninety = times["90fps"];
+
+ // When power mode is normal, time for configs updates.
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ ASSERT_LT(sixty, times["60fps"]);
+ sixty = times["60fps"];
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(screenOff, times["ScreenOff"]);
+ ASSERT_LT(ninety, times["90fps"]);
+ ASSERT_EQ(sixty, times["60fps"]);
+ ninety = times["90fps"];
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ ASSERT_LT(sixty, times["60fps"]);
+ sixty = times["60fps"];
+
+ // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+ // does not update refresh rates that come from the config.
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ ASSERT_EQ(sixty, times["60fps"]);
+ screenOff = times["ScreenOff"];
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ ASSERT_LT(screenOff, times["ScreenOff"]);
+ ASSERT_EQ(ninety, times["90fps"]);
+ ASSERT_EQ(sixty, times["60fps"]);
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 3dd5143..ec76538 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -18,12 +18,14 @@
namespace android {
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999;
+
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback()) {}
+ : EventThreadConnection(eventThread, ResyncCallback(), ResetIdleTimerCallback()) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -41,7 +43,7 @@
: Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
std::unique_ptr<EventThread> makeEventThread(
- const std::string& /* connectionName */, DispSync* /* dispSync */,
+ const char* /* connectionName */, DispSync* /* dispSync */,
nsecs_t /* phaseOffsetNs */,
impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
return std::move(mEventThread);
@@ -76,7 +78,7 @@
// createConnection call to scheduler makes a createEventConnection call to EventThread. Make
// sure that call gets executed and returns an EventThread::Connection object.
- EXPECT_CALL(*mEventThread, createEventConnection(_))
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
@@ -104,8 +106,7 @@
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->hotplugReceived(nullptr, EventThread::DisplayType::Primary, false));
+ ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(nullptr, PHYSICAL_DISPLAY_ID, false));
ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr));
ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr));
std::string testString;
@@ -129,8 +130,8 @@
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(connectionHandle,
- EventThread::DisplayType::Primary, false));
+ ASSERT_NO_FATAL_FAILURE(
+ mScheduler->hotplugReceived(connectionHandle, PHYSICAL_DISPLAY_ID, false));
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle));
@@ -158,10 +159,9 @@
EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr);
- EXPECT_CALL(*mEventThread, onHotplugReceived(EventThread::DisplayType::Primary, false))
- .Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(mConnectionHandle,
- EventThread::DisplayType::Primary, false));
+ EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
+ ASSERT_NO_FATAL_FAILURE(
+ mScheduler->hotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 687941a..e639b4d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -23,6 +23,7 @@
#include "ColorLayer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
+#include "FakePhaseOffsets.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "StartPropertySetThread.h"
@@ -75,6 +76,10 @@
return std::make_unique<android::impl::MessageQueue>();
}
+ std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ return std::make_unique<scheduler::FakePhaseOffsets>();
+ }
+
std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
// TODO: Use test-fixture controlled factory
return nullptr;
@@ -139,9 +144,9 @@
return nullptr;
}
- std::unique_ptr<TimeStats> createTimeStats() override {
+ std::shared_ptr<TimeStats> createTimeStats() override {
// TODO: Use test-fixture controlled factory
- return std::make_unique<TimeStats>();
+ return std::make_shared<android::impl::TimeStats>();
}
using CreateBufferQueueFunction =
@@ -187,6 +192,10 @@
mFactory.mCreateNativeWindowSurface = f;
}
+ void setInternalDisplayPrimaries(const ui::DisplayPrimaries& primaries) {
+ memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
+ }
+
using HotplugEvent = SurfaceFlinger::HotplugEvent;
auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
@@ -260,6 +269,15 @@
return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
}
+ auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries) {
+ return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
+ }
+
+ void initDefaultDisplayNativePrimaries() {
+ mFlinger->SurfaceFlinger::initDefaultDisplayNativePrimaries();
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -290,6 +308,7 @@
auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
auto& mutableEventQueue() { return mFlinger->mEventQueue; }
auto& mutableEventThread() { return mFlinger->mEventThread; }
+ auto& mutableSFEventThread() { return mFlinger->mSFEventThread; }
auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
auto& mutableInterceptor() { return mFlinger->mInterceptor; }
@@ -317,6 +336,7 @@
mutableEventControlThread().reset();
mutableEventQueue().reset();
mutableEventThread().reset();
+ mutableSFEventThread().reset();
mutableInterceptor().reset();
mutablePrimaryDispSync().reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
@@ -335,11 +355,11 @@
void setExpensiveRenderingExpected(hwc2_display_t, bool) override {}
};
- struct HWC2Display : public HWC2::Display {
+ struct HWC2Display : public HWC2::impl::Display {
HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor,
const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
HWC2::DisplayType type)
- : HWC2::Display(composer, advisor, capabilities, id, type) {}
+ : HWC2::impl::Display(composer, advisor, capabilities, id, type) {}
~HWC2Display() {
// Prevents a call to disable vsyncs.
mType = HWC2::DisplayType::Invalid;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 86f1a39..f35758d 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -24,6 +25,7 @@
#include <utils/Vector.h>
#include <random>
+#include <unordered_set>
#include "TimeStats/TimeStats.h"
@@ -35,6 +37,10 @@
namespace android {
namespace {
+using testing::Contains;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
// clang-format off
#define FMT_PROTO true
#define FMT_STRING false
@@ -127,11 +133,10 @@
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
- std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+ std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
- size_t index = 0;
std::string result;
Vector<String16> args;
@@ -162,7 +167,7 @@
ALOGD("Invalid control command");
}
- EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+ EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result));
return result;
}
@@ -372,6 +377,63 @@
}
}
+TEST_F(TimeStatsTest, recordRefreshRateNewConfigs) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint32_t fpsOne = 30;
+ uint32_t fpsTwo = 90;
+ uint64_t millisOne = 5000;
+ uint64_t millisTwo = 7000;
+
+ mTimeStats->recordRefreshRate(fpsOne, ms2ns(millisOne));
+ mTimeStats->recordRefreshRate(fpsTwo, ms2ns(millisTwo));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ SFTimeStatsDisplayConfigBucketProto expectedBucketOne;
+ SFTimeStatsDisplayConfigProto* expectedConfigOne = expectedBucketOne.mutable_config();
+ expectedConfigOne->set_fps(fpsOne);
+ expectedBucketOne.set_duration_millis(millisOne);
+
+ SFTimeStatsDisplayConfigBucketProto expectedBucketTwo;
+ SFTimeStatsDisplayConfigProto* expectedConfigTwo = expectedBucketTwo.mutable_config();
+ expectedConfigTwo->set_fps(fpsTwo);
+ expectedBucketTwo.set_duration_millis(millisTwo);
+
+ EXPECT_THAT(globalProto.display_config_stats(), SizeIs(2));
+
+ std::unordered_set<uint32_t> seen_fps;
+ for (const auto& bucket : globalProto.display_config_stats()) {
+ seen_fps.emplace(bucket.config().fps());
+ if (fpsOne == bucket.config().fps()) {
+ EXPECT_EQ(millisOne, bucket.duration_millis());
+ } else if (fpsTwo == bucket.config().fps()) {
+ EXPECT_EQ(millisTwo, bucket.duration_millis());
+ } else {
+ FAIL() << "Unknown fps: " << bucket.config().fps();
+ }
+ }
+ EXPECT_THAT(seen_fps, UnorderedElementsAre(fpsOne, fpsTwo));
+}
+
+TEST_F(TimeStatsTest, recordRefreshRateUpdatesConfig) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint32_t fps = 30;
+ uint64_t millisOne = 5000;
+ uint64_t millisTwo = 7000;
+
+ mTimeStats->recordRefreshRate(fps, ms2ns(millisOne));
+ mTimeStats->recordRefreshRate(fps, ms2ns(millisTwo));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+ EXPECT_THAT(globalProto.display_config_stats(), SizeIs(1));
+ EXPECT_EQ(fps, globalProto.display_config_stats().Get(0).config().fps());
+ EXPECT_EQ(millisOne + millisTwo, globalProto.display_config_stats().Get(0).duration_millis());
+}
+
TEST_F(TimeStatsTest, canRemoveTimeRecord) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -395,7 +457,7 @@
uint64_t frameNumber = 1;
nsecs_t ts = 1000000;
insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
- for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+ for (size_t i = 0; i < impl::TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
frameNumber++;
ts += 1000000;
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
index bc1f00d..ed628b8 100644
--- a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
@@ -16,6 +16,11 @@
#include <gtest/gtest.h>
+#include <sched.h>
+
+#include <processgroup/sched_policy.h>
+#include <system/graphics.h>
+
#include "libsurfaceflinger_unittest_main.h"
// ------------------------------------------------------------------------
@@ -39,6 +44,12 @@
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ // The SurfaceFlinger implementation assumes that threads resume
+ // execution as quickly as possible once they become unblocked.
+ // (These same calls are made in main_surfaceflinger.cpp)
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+ set_sched_policy(0, SP_FOREGROUND);
+
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--no-slow") == 0) {
g_noSlowTests = true;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 551fae7..e6f1a06 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -29,11 +29,11 @@
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::PixelFormat;
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
using android::hardware::graphics::composer::V2_1::Display;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
new file mode 100644
index 0000000..2ec37c1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/DisplayHardware/MockDisplay.h"
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+Display::Display() = default;
+Display::~Display() = default;
+
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
new file mode 100644
index 0000000..d7e20c4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWC2.h"
+
+using HWC2::Error;
+using HWC2::Layer;
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+class Display : public HWC2::Display {
+public:
+ Display();
+ ~Display();
+
+ MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+ MOCK_CONST_METHOD0(isConnected, bool());
+ MOCK_METHOD1(setConnected, void(bool));
+ MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<HWC2::DisplayCapability>&());
+
+ MOCK_METHOD0(acceptChanges, Error());
+ MOCK_METHOD1(createLayer, Error(Layer**));
+ MOCK_METHOD1(destroyLayer, Error(Layer*));
+ MOCK_CONST_METHOD1(getActiveConfig, Error(std::shared_ptr<const Config>*));
+ MOCK_CONST_METHOD1(getActiveConfigIndex, Error(int* outIndex));
+ MOCK_METHOD1(getChangedCompositionTypes, Error(std::unordered_map<Layer*, HWC2::Composition>*));
+ MOCK_CONST_METHOD1(getColorModes, Error(std::vector<android::ui::ColorMode>*));
+
+ MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+ MOCK_CONST_METHOD2(getRenderIntents,
+ Error(android::ui::ColorMode, std::vector<android::ui::RenderIntent>*));
+ MOCK_METHOD2(getDataspaceSaturationMatrix, Error(android::ui::Dataspace, android::mat4*));
+ MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
+
+ MOCK_CONST_METHOD1(getName, Error(std::string*));
+ MOCK_METHOD2(getRequests,
+ Error(HWC2::DisplayRequest*, std::unordered_map<Layer*, HWC2::LayerRequest>*));
+ MOCK_CONST_METHOD1(getType, Error(HWC2::DisplayType*));
+ MOCK_CONST_METHOD1(supportsDoze, Error(bool*));
+ MOCK_CONST_METHOD1(getHdrCapabilities, Error(android::HdrCapabilities*));
+ MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
+ Error(android::ui::PixelFormat*, android::ui::Dataspace*, uint8_t*));
+ MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, Error(bool, uint8_t, uint64_t));
+ MOCK_CONST_METHOD3(getDisplayedContentSample,
+ Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
+ MOCK_CONST_METHOD1(getReleaseFences,
+ Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
+ MOCK_METHOD1(present, Error(android::sp<android::Fence>*));
+ MOCK_METHOD1(setActiveConfig, Error(const std::shared_ptr<const HWC2::Display::Config>&));
+ MOCK_METHOD4(setClientTarget,
+ Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&, android::ui::Dataspace));
+ MOCK_METHOD2(setColorMode, Error(android::ui::ColorMode, android::ui::RenderIntent));
+ MOCK_METHOD2(setColorTransform, Error(const android::mat4&, android_color_transform_t));
+ MOCK_METHOD2(setOutputBuffer,
+ Error(const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&));
+ MOCK_METHOD1(setPowerMode, Error(HWC2::PowerMode));
+ MOCK_METHOD1(setVsyncEnabled, Error(HWC2::Vsync));
+ MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*));
+ MOCK_METHOD4(presentOrValidate,
+ Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
+};
+
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 3242ef1..589237d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,10 +28,11 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
+ MOCK_CONST_METHOD2(createEventConnection,
+ sp<EventThreadConnection>(ResyncCallback, ResetIdleTimerCallback));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
- MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool));
+ MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
MOCK_METHOD1(registerDisplayEventConnection,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
new file mode 100644
index 0000000..d686939
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/MockTimeStats.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+TimeStats::TimeStats() = default;
+TimeStats::~TimeStats() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
new file mode 100644
index 0000000..08fdb9d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TimeStats/TimeStats.h"
+
+namespace android {
+namespace mock {
+
+class TimeStats : public android::TimeStats {
+public:
+ TimeStats();
+ ~TimeStats() override;
+
+ MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
+ MOCK_METHOD0(isEnabled, bool());
+ MOCK_METHOD0(incrementTotalFrames, void());
+ MOCK_METHOD0(incrementMissedFrames, void());
+ MOCK_METHOD0(incrementClientCompositionFrames, void());
+ MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(onDestroy, void(int32_t));
+ MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
+ MOCK_METHOD1(setPowerMode, void(int32_t));
+ MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t));
+ MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/utils/Android.bp b/services/utils/Android.bp
index 6132956..f3d2bc9 100644
--- a/services/utils/Android.bp
+++ b/services/utils/Android.bp
@@ -18,6 +18,8 @@
cc_library_static {
name: "libserviceutils",
+ vendor_available: true,
+
cflags: [
"-Wall",
"-Werror",
@@ -27,8 +29,13 @@
"PriorityDumper.cpp",
],
- clang: true,
+ header_libs: [
+ "libutils_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libutils_headers",
+ ],
+
export_include_dirs: ["include"],
}
-
-subdirs = ["tests"]
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index ba7d7f9..4e24a64 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -14,7 +14,6 @@
sharedLibraries = [
"libbase",
- "libbufferhubservice",
"libcutils",
"libgtest_prod",
"libgui",
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
deleted file mode 100644
index 997aeda..0000000
--- a/services/vr/bufferhubd/include/private/dvr/buffer_node.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
-#define ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
-// TODO(b/118891412) Remove this file
-
-#include <bufferhub/BufferNode.h>
-
-namespace android {
-namespace dvr {
-
-typedef android::frameworks::bufferhub::V1_0::implementation::BufferNode
- BufferNode;
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 96ef1a2..45593ad 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -69,7 +69,7 @@
bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
uint32_t format, uint64_t usage,
- size_t user_metadata_size);
+ size_t user_metadata_size) const;
private:
std::vector<ConsumerChannel*> consumer_channels_;
@@ -112,6 +112,10 @@
// This function is used for clean up for failures in CreateConsumer method.
void RemoveConsumerClientMask(uint32_t consumer_state_mask);
+ // Checks whether the buffer is released by all active clients, excluding
+ // orphaned consumers.
+ bool IsBufferReleasedByAllActiveClientsExceptForOrphans() const;
+
ProducerChannel(const ProducerChannel&) = delete;
void operator=(const ProducerChannel&) = delete;
};
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 895dee0..f3e54a0 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -253,7 +253,7 @@
uint32_t current_active_clients_bit_mask =
active_clients_bit_mask_->load(std::memory_order_acquire);
uint32_t consumer_state_mask =
- BufferHubDefs::FindNextAvailableClientStateMask(
+ BufferHubDefs::findNextAvailableClientStateMask(
current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
if (consumer_state_mask == 0U) {
ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
@@ -279,7 +279,7 @@
"condition.",
__FUNCTION__, updated_active_clients_bit_mask,
current_active_clients_bit_mask);
- consumer_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+ consumer_state_mask = BufferHubDefs::findNextAvailableClientStateMask(
current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
if (consumer_state_mask == 0U) {
ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
@@ -333,14 +333,17 @@
uint32_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
- BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ // Return the consumer channel handle without signal when adding the new
+ // consumer to a buffer that is available to producer (a.k.a a fully-released
+ // buffer) or a gained buffer.
+ if (current_buffer_state == 0U ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
return {status.take()};
}
// Signal the new consumer when adding it to a posted producer.
bool update_buffer_state = true;
- if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
consumer_state_mask)) {
uint32_t updated_buffer_state =
current_buffer_state ^
@@ -356,8 +359,8 @@
". About to try again if the buffer is still not gained nor fully "
"released.",
__FUNCTION__, current_buffer_state, updated_buffer_state);
- if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
- BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ if (current_buffer_state == 0U ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
ALOGI("%s: buffer is gained or fully released, state=%" PRIx32 ".",
__FUNCTION__, current_buffer_state);
update_buffer_state = false;
@@ -368,7 +371,7 @@
(consumer_state_mask & BufferHubDefs::kHighBitsMask);
}
}
- if (update_buffer_state || BufferHubDefs::IsClientPosted(
+ if (update_buffer_state || BufferHubDefs::isClientPosted(
buffer_state_->load(std::memory_order_acquire),
consumer_state_mask)) {
consumer->OnProducerPosted();
@@ -454,7 +457,7 @@
buffer_id());
uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsClientGained(
+ if (!BufferHubDefs::isClientGained(
buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a ProducerBuffer when it's in gained state.
ALOGW(
@@ -536,14 +539,7 @@
}
}
- uint32_t current_buffer_state =
- buffer_state_->load(std::memory_order_acquire);
- uint32_t current_active_clients_bit_mask =
- active_clients_bit_mask_->load(std::memory_order_acquire);
- // Signal producer if all current active consumers have released the buffer.
- if (BufferHubDefs::IsBufferReleased(current_buffer_state &
- ~orphaned_consumer_bit_mask_ &
- current_active_clients_bit_mask)) {
+ if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
buffer_state_->store(0U);
SignalAvailable();
if (orphaned_consumer_bit_mask_) {
@@ -568,14 +564,7 @@
__FUNCTION__, consumer_state_mask);
orphaned_consumer_bit_mask_ |= consumer_state_mask;
- uint32_t current_buffer_state =
- buffer_state_->load(std::memory_order_acquire);
- uint32_t current_active_clients_bit_mask =
- active_clients_bit_mask_->load(std::memory_order_acquire);
- // Signal producer if all current active consumers have released the buffer.
- if (BufferHubDefs::IsBufferReleased(current_buffer_state &
- ~orphaned_consumer_bit_mask_ &
- current_active_clients_bit_mask)) {
+ if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
buffer_state_->store(0U);
SignalAvailable();
}
@@ -627,9 +616,9 @@
const uint32_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (BufferHubDefs::IsClientPosted(current_buffer_state,
+ if (BufferHubDefs::isClientPosted(current_buffer_state,
consumer_state_mask) ||
- BufferHubDefs::IsClientAcquired(current_buffer_state,
+ BufferHubDefs::isClientAcquired(current_buffer_state,
consumer_state_mask)) {
// The consumer client is being destoryed without releasing. This could
// happen in corner cases when the consumer crashes. Here we mark it
@@ -638,9 +627,9 @@
return;
}
- if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ if (BufferHubDefs::isClientReleased(current_buffer_state,
consumer_state_mask) ||
- BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
// The consumer is being close while it is suppose to signal a release
// fence. Signal the dummy fence here.
if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
@@ -667,12 +656,19 @@
bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format,
uint64_t usage,
- size_t user_metadata_size) {
+ size_t user_metadata_size) const {
return user_metadata_size == user_metadata_size_ &&
buffer_.width() == width && buffer_.height() == height &&
buffer_.layer_count() == layer_count && buffer_.format() == format &&
buffer_.usage() == usage;
}
+bool ProducerChannel::IsBufferReleasedByAllActiveClientsExceptForOrphans()
+ const {
+ return (buffer_state_->load(std::memory_order_acquire) &
+ ~orphaned_consumer_bit_mask_ &
+ active_clients_bit_mask_->load(std::memory_order_acquire)) == 0U;
+}
+
} // namespace dvr
} // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index 6b33f50..004dc7c 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -323,7 +323,7 @@
// memory to indicate which client is the last producer of the buffer.
// Currently, the first client is the only producer to the buffer.
// Thus, it checks whether the first client gains the buffer below.
- if (!BufferHubDefs::IsClientGained(buffer_state,
+ if (!BufferHubDefs::isClientGained(buffer_state,
BufferHubDefs::kFirstClientBitMask)) {
// Rejects the request if the requested buffer is not in Gained state.
ALOGE(
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 673a066..71048db 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -21,6 +21,8 @@
// There are a few of them requiring manual code for things such as layer
// discovery or chaining. They call into functions defined in this file.
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdlib.h>
#include <string.h>
@@ -32,6 +34,7 @@
#include <android-base/strings.h>
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/Trace.h>
#include <vulkan/vk_layer_interface.h>
#include <graphicsenv/GraphicsEnv.h>
@@ -1176,6 +1179,8 @@
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1184,6 +1189,8 @@
void DestroyInstance(VkInstance instance,
const VkAllocationCallbacks* pAllocator) {
+ ATRACE_CALL();
+
if (instance != VK_NULL_HANDLE)
LayerChain::DestroyInstance(instance, pAllocator);
}
@@ -1192,17 +1199,23 @@
const VkDeviceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice) {
+ ATRACE_CALL();
+
return LayerChain::CreateDevice(physicalDevice, pCreateInfo, pAllocator,
pDevice);
}
void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
+ ATRACE_CALL();
+
if (device != VK_NULL_HANDLE)
LayerChain::DestroyDevice(device, pAllocator);
}
VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
VkLayerProperties* pProperties) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1225,6 +1238,8 @@
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1253,6 +1268,8 @@
VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
uint32_t* pPropertyCount,
VkLayerProperties* pProperties) {
+ ATRACE_CALL();
+
uint32_t count;
const LayerChain::ActiveLayer* layers =
LayerChain::GetActiveLayers(physicalDevice, count);
@@ -1275,6 +1292,8 @@
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
+ ATRACE_CALL();
+
if (pLayerName) {
// EnumerateDeviceLayerProperties enumerates active layers for
// backward compatibility. The extension query here should work for
@@ -1302,6 +1321,8 @@
}
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
+ ATRACE_CALL();
+
*pApiVersion = VK_API_VERSION_1_1;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index a607a5d..3a8e34e 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
@@ -31,6 +33,7 @@
#include <configstore/Utils.h>
#include <cutils/properties.h>
#include <graphicsenv/GraphicsEnv.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include "android-base/properties.h"
@@ -150,6 +153,8 @@
void* LoadLibrary(const android_dlextinfo& dlextinfo,
const char* subname,
int subname_len) {
+ ATRACE_CALL();
+
const char kLibFormat[] = "vulkan.%*s.so";
char* name = static_cast<char*>(
alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len)));
@@ -164,6 +169,8 @@
int LoadDriver(android_namespace_t* library_namespace,
const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = library_namespace,
@@ -198,6 +205,8 @@
}
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
auto ns = android_get_exported_namespace("sphal");
if (!ns)
return -ENOENT;
@@ -205,6 +214,8 @@
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
auto ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns)
return -ENOENT;
@@ -212,6 +223,8 @@
}
bool Hal::Open() {
+ ATRACE_CALL();
+
ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
// Use a stub device unless we successfully open a real HAL device.
@@ -241,10 +254,14 @@
return true;
}
+ android::GraphicsEnv::getInstance().sendGpuStats();
+
hwvulkan_device_t* device;
+ ATRACE_BEGIN("hwvulkan module open");
result =
module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
reinterpret_cast<hw_device_t**>(&device));
+ ATRACE_END();
if (result != 0) {
// Any device with a Vulkan HAL should be able to open the device.
ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
@@ -260,6 +277,8 @@
}
bool Hal::InitDebugReportIndex() {
+ ATRACE_CALL();
+
uint32_t count;
if (dev_->EnumerateInstanceExtensionProperties(nullptr, &count, nullptr) !=
VK_SUCCESS) {
@@ -821,8 +840,10 @@
}
}
+ ATRACE_BEGIN("driver.EnumerateInstanceExtensionProperties");
VkResult result = Hal::Device().EnumerateInstanceExtensionProperties(
pLayerName, pPropertyCount, pProperties);
+ ATRACE_END();
if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
int idx = Hal::Get().GetDebugReportIndex();
@@ -931,8 +952,10 @@
*pPropertyCount -= count;
}
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
VkResult result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
+ ATRACE_END();
if (pProperties) {
// map VK_ANDROID_native_buffer to VK_KHR_swapchain
@@ -968,12 +991,15 @@
if (result != VK_SUCCESS)
return result;
+ ATRACE_BEGIN("AllocateInstanceData");
InstanceData* data = AllocateInstanceData(data_allocator);
+ ATRACE_END();
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
data->hook_extensions |= wrapper.GetHookExtensions();
+ ATRACE_BEGIN("autoDowngradeApiVersion");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
uint32_t api_version = ((pCreateInfo->pApplicationInfo)
@@ -989,7 +1015,9 @@
if (!pfn_enumerate_instance_version) {
icd_api_version = VK_API_VERSION_1_0;
} else {
+ ATRACE_BEGIN("pfn_enumerate_instance_version");
result = (*pfn_enumerate_instance_version)(&icd_api_version);
+ ATRACE_END();
}
uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version);
uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version);
@@ -1000,12 +1028,15 @@
wrapper.DowngradeApiVersion();
}
#pragma clang diagnostic pop
+ ATRACE_END();
// call into the driver
VkInstance instance;
+ ATRACE_BEGIN("driver.CreateInstance");
result = Hal::Device().CreateInstance(
static_cast<const VkInstanceCreateInfo*>(wrapper), pAllocator,
&instance);
+ ATRACE_END();
if (result != VK_SUCCESS) {
FreeInstanceData(data, data_allocator);
return result;
@@ -1066,8 +1097,10 @@
if (result != VK_SUCCESS)
return result;
+ ATRACE_BEGIN("AllocateDeviceData");
DeviceData* data = AllocateDeviceData(data_allocator,
instance_data.debug_report_callbacks);
+ ATRACE_END();
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -1075,9 +1108,11 @@
// call into the driver
VkDevice dev;
+ ATRACE_BEGIN("driver.CreateDevice");
result = instance_data.driver.CreateDevice(
physicalDevice, static_cast<const VkDeviceCreateInfo*>(wrapper),
pAllocator, &dev);
+ ATRACE_END();
if (result != VK_SUCCESS) {
FreeDeviceData(data, data_allocator);
return result;
@@ -1114,8 +1149,10 @@
}
VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
&properties);
+ ATRACE_END();
data->driver_device = dev;
data->driver_version = properties.driverVersion;
@@ -1141,6 +1178,8 @@
VkResult EnumeratePhysicalDevices(VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices) {
+ ATRACE_CALL();
+
const auto& data = GetData(instance);
VkResult result = data.driver.EnumeratePhysicalDevices(
@@ -1157,6 +1196,8 @@
VkInstance instance,
uint32_t* pPhysicalDeviceGroupCount,
VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+ ATRACE_CALL();
+
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
@@ -1217,6 +1258,8 @@
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
@@ -1226,6 +1269,8 @@
void GetDeviceQueue2(VkDevice device,
const VkDeviceQueueInfo2* pQueueInfo,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue2(device, pQueueInfo, pQueue);
@@ -1236,6 +1281,8 @@
AllocateCommandBuffers(VkDevice device,
const VkCommandBufferAllocateInfo* pAllocateInfo,
VkCommandBuffer* pCommandBuffers) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
VkResult result = data.driver.AllocateCommandBuffers(device, pAllocateInfo,
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index ba4cf00..af1adcf 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "layers_extensions.h"
#include <alloca.h>
@@ -33,6 +35,7 @@
#include <log/log.h>
#include <nativebridge/native_bridge.h>
#include <nativeloader/native_loader.h>
+#include <utils/Trace.h>
#include <ziparchive/zip_archive.h>
// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
@@ -428,6 +431,8 @@
}
void DiscoverLayersInPathList(const std::string& pathstr) {
+ ATRACE_CALL();
+
std::vector<std::string> paths = android::base::Split(pathstr, ":");
for (const auto& path : paths) {
ForEachFileInPath(path, [&](const std::string& filename) {
@@ -477,6 +482,8 @@
} // anonymous namespace
void DiscoverLayers() {
+ ATRACE_CALL();
+
if (property_get_bool("ro.debuggable", false) &&
prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
DiscoverLayersInPathList(kSystemLayerLibraryDir);
diff --git a/vulkan/libvulkan/layers_extensions.h b/vulkan/libvulkan/layers_extensions.h
index 1dae456..9e2ff5b 100644
--- a/vulkan/libvulkan/layers_extensions.h
+++ b/vulkan/libvulkan/layers_extensions.h
@@ -33,6 +33,7 @@
LayerRef& operator=(const LayerRef&) = delete;
// provides bool-like behavior
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator const Layer*() const { return layer_; }
PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8601373..32e19f7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <algorithm>
#include <grallocusage/GrallocUsageConversion.h>
@@ -21,6 +23,7 @@
#include <ui/BufferQueueDefs.h>
#include <sync/sync.h>
#include <utils/StrongPointer.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include <system/window.h>
#include <android/hardware/graphics/common/1.0/types.h>
@@ -416,7 +419,7 @@
case VK_FORMAT_R16G16B16A16_SFLOAT:
native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
break;
- case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
break;
default:
@@ -486,6 +489,8 @@
const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* allocator,
VkSurfaceKHR* out_surface) {
+ ATRACE_CALL();
+
if (!allocator)
allocator = &GetData(instance).allocator;
void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Surface),
@@ -528,6 +533,8 @@
void DestroySurfaceKHR(VkInstance instance,
VkSurfaceKHR surface_handle,
const VkAllocationCallbacks* allocator) {
+ ATRACE_CALL();
+
Surface* surface = SurfaceFromHandle(surface_handle);
if (!surface)
return;
@@ -548,6 +555,8 @@
uint32_t /*queue_family*/,
VkSurfaceKHR surface_handle,
VkBool32* supported) {
+ ATRACE_CALL();
+
const Surface* surface = SurfaceFromHandle(surface_handle);
if (!surface) {
return VK_ERROR_SURFACE_LOST_KHR;
@@ -570,6 +579,7 @@
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
format_supported = true;
break;
default:
@@ -589,6 +599,8 @@
VkPhysicalDevice /*pdev*/,
VkSurfaceKHR surface,
VkSurfaceCapabilitiesKHR* capabilities) {
+ ATRACE_CALL();
+
int err;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -662,6 +674,8 @@
VkSurfaceKHR surface_handle,
uint32_t* count,
VkSurfaceFormatKHR* formats) {
+ ATRACE_CALL();
+
const InstanceData& instance_data = GetData(pdev);
// TODO(jessehall): Fill out the set of supported formats. Longer term, add
@@ -700,6 +714,8 @@
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
+ {VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
};
const uint32_t kNumWideColorFormats =
sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
@@ -734,6 +750,8 @@
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
+ ATRACE_CALL();
+
VkResult result = GetPhysicalDeviceSurfaceCapabilitiesKHR(
physicalDevice, pSurfaceInfo->surface,
&pSurfaceCapabilities->surfaceCapabilities);
@@ -769,6 +787,8 @@
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormat2KHR* pSurfaceFormats) {
+ ATRACE_CALL();
+
if (!pSurfaceFormats) {
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
@@ -800,6 +820,8 @@
VkSurfaceKHR surface,
uint32_t* count,
VkPresentModeKHR* modes) {
+ ATRACE_CALL();
+
int err;
int query_value;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -851,6 +873,8 @@
VkResult GetDeviceGroupPresentCapabilitiesKHR(
VkDevice,
VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
+ ATRACE_CALL();
+
ALOGV_IF(pDeviceGroupPresentCapabilities->sType !=
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR,
"vkGetDeviceGroupPresentCapabilitiesKHR: invalid "
@@ -873,6 +897,8 @@
VkDevice,
VkSurfaceKHR,
VkDeviceGroupPresentModeFlagsKHR* pModes) {
+ ATRACE_CALL();
+
*pModes = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR;
return VK_SUCCESS;
}
@@ -882,6 +908,8 @@
VkSurfaceKHR surface,
uint32_t* pRectCount,
VkRect2D* pRects) {
+ ATRACE_CALL();
+
if (!pRects) {
*pRectCount = 1;
} else {
@@ -923,6 +951,8 @@
const VkSwapchainCreateInfoKHR* create_info,
const VkAllocationCallbacks* allocator,
VkSwapchainKHR* swapchain_handle) {
+ ATRACE_CALL();
+
int err;
VkResult result = VK_SUCCESS;
@@ -1147,9 +1177,11 @@
int32_t legacy_usage = 0;
if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;
+ ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsage2ANDROID");
result = dispatch.GetSwapchainGrallocUsage2ANDROID(
device, create_info->imageFormat, create_info->imageUsage,
swapchain_image_usage, &consumer_usage, &producer_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1157,9 +1189,11 @@
legacy_usage =
android_convertGralloc1To0Usage(producer_usage, consumer_usage);
} else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsageANDROID");
result = dispatch.GetSwapchainGrallocUsageANDROID(
device, create_info->imageFormat, create_info->imageUsage,
&legacy_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1254,8 +1288,10 @@
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
+ ATRACE_BEGIN("dispatch.CreateImage");
result =
dispatch.CreateImage(device, &image_create, nullptr, &img.image);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGD("vkCreateImage w/ native buffer failed: %u", result);
break;
@@ -1279,8 +1315,11 @@
}
}
if (result != VK_SUCCESS) {
- if (img.image)
+ if (img.image) {
+ ATRACE_BEGIN("dispatch.DestroyImage");
dispatch.DestroyImage(device, img.image, nullptr);
+ ATRACE_END();
+ }
}
}
@@ -1299,6 +1338,8 @@
void DestroySwapchainKHR(VkDevice device,
VkSwapchainKHR swapchain_handle,
const VkAllocationCallbacks* allocator) {
+ ATRACE_CALL();
+
const auto& dispatch = GetData(device).driver;
Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
if (!swapchain)
@@ -1324,6 +1365,8 @@
VkSwapchainKHR swapchain_handle,
uint32_t* count,
VkImage* images) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
"getting images for non-active swapchain 0x%" PRIx64
@@ -1352,6 +1395,8 @@
VkSemaphore semaphore,
VkFence vk_fence,
uint32_t* image_index) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ANativeWindow* window = swapchain.surface.window.get();
VkResult result;
@@ -1432,6 +1477,8 @@
VkResult AcquireNextImage2KHR(VkDevice device,
const VkAcquireNextImageInfoKHR* pAcquireInfo,
uint32_t* pImageIndex) {
+ ATRACE_CALL();
+
// TODO: this should actually be the other way around and this function
// should handle any additional structures that get passed in
return AcquireNextImageKHR(device, pAcquireInfo->swapchain,
@@ -1461,6 +1508,8 @@
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
+ ATRACE_CALL();
+
ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
@@ -1672,6 +1721,8 @@
VkDevice,
VkSwapchainKHR swapchain_handle,
VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
@@ -1687,6 +1738,8 @@
VkSwapchainKHR swapchain_handle,
uint32_t* count,
VkPastPresentationTimingGOOGLE* timings) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ANativeWindow* window = swapchain.surface.window.get();
VkResult result = VK_SUCCESS;
@@ -1711,6 +1764,8 @@
VkResult GetSwapchainStatusKHR(
VkDevice,
VkSwapchainKHR swapchain_handle) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
@@ -1728,6 +1783,7 @@
uint32_t swapchainCount,
const VkSwapchainKHR* pSwapchains,
const VkHdrMetadataEXT* pHdrMetadataEXTs) {
+ ATRACE_CALL();
for (uint32_t idx = 0; idx < swapchainCount; idx++) {
Swapchain* swapchain = SwapchainFromHandle(pSwapchains[idx]);