Merge "[Shadows] Create a new composition layer for shadows [6/n]"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..8173c89
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,63 @@
+{
+ "presubmit": [
+ {
+ "name": "SurfaceFlinger_test",
+ "options": [
+ {
+ "include-filter": "*CredentialsTest.*"
+ },
+ {
+ "include-filter": "*SurfaceFlingerStress.*"
+ },
+ {
+ "include-filter": "*SurfaceInterceptorTest.*"
+ },
+ {
+ "include-filter": "*LayerTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerTypeTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerUpdateTest.*"
+ },
+ {
+ "include-filter": "*GeometryLatchingTest.*"
+ },
+ {
+ "include-filter": "*CropLatchingTest.*"
+ },
+ {
+ "include-filter": "*ChildLayerTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureChildOnlyTest.*"
+ },
+ {
+ "include-filter": "*DereferenceSurfaceControlTest.*"
+ },
+ {
+ "include-filter": "*BoundlessLayerTest.*"
+ },
+ {
+ "include-filter": "*MultiDisplayLayerBoundsTest.*"
+ },
+ {
+ "include-filter": "*InvalidHandleTest.*"
+ },
+ {
+ "include-filter": "*VirtualDisplayTest.*"
+ },
+ {
+ "include-filter": "*RelativeZTest.*"
+ }
+ ]
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
+ }
+ ]
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8400cdc..9d6a7ff 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1545,11 +1545,7 @@
* Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport
* with the caller.
*/
-static Dumpstate::RunStatus DumpstateDefault() {
- // Invoking the following dumpsys calls before DumpTraces() to try and
- // keep the system stats as close to its initial state as possible.
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical);
-
+Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() {
// Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the
// buffer.
DoLogcat();
@@ -1634,6 +1630,7 @@
// This method collects dumpsys for telephony debugging only
static void DumpstateTelephonyOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+
const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
DumpstateRadioCommon();
@@ -2060,12 +2057,12 @@
? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
: ds.bugreport_internal_dir_.c_str();
MYLOGD(
- "Bugreport dir: %s\n"
- "Base name: %s\n"
- "Suffix: %s\n"
- "Log path: %s\n"
- "Temporary path: %s\n"
- "Screenshot path: %s\n",
+ "Bugreport dir: [%s] "
+ "Base name: [%s] "
+ "Suffix: [%s] "
+ "Log path: [%s] "
+ "Temporary path: [%s] "
+ "Screenshot path: [%s]\n",
destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
@@ -2167,21 +2164,14 @@
}
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
- MYLOGI("do_zip_file: %d\n", options.do_zip_file);
- MYLOGI("do_add_date: %d\n", options.do_add_date);
- MYLOGI("do_vibrate: %d\n", options.do_vibrate);
- MYLOGI("use_socket: %d\n", options.use_socket);
- MYLOGI("use_control_socket: %d\n", options.use_control_socket);
- MYLOGI("do_fb: %d\n", options.do_fb);
- MYLOGI("is_remote_mode: %d\n", options.is_remote_mode);
- MYLOGI("show_header_only: %d\n", options.show_header_only);
- MYLOGI("do_start_service: %d\n", options.do_start_service);
- MYLOGI("telephony_only: %d\n", options.telephony_only);
- MYLOGI("wifi_only: %d\n", options.wifi_only);
- MYLOGI("do_progress_updates: %d\n", options.do_progress_updates);
- MYLOGI("fd: %d\n", options.bugreport_fd.get());
- MYLOGI("bugreport_mode: %s\n", options.bugreport_mode.c_str());
- MYLOGI("args: %s\n", options.args.c_str());
+ MYLOGI(
+ "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+ "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s args: %s\n",
+ options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+ options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+ options.telephony_only, options.wifi_only, options.do_progress_updates,
+ options.bugreport_fd.get(), options.bugreport_mode.c_str(), options.args.c_str());
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2353,11 +2343,6 @@
MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
calling_uid, calling_package.c_str());
- if (CalledByApi()) {
- // 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();
@@ -2374,8 +2359,6 @@
id_ = ++last_id;
android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
- MYLOGI("begin\n");
-
if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
} else {
@@ -2398,10 +2381,8 @@
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
- MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s)\n", id_, options_->args.c_str(),
- options_->bugreport_mode.c_str());
-
- MYLOGI("bugreport format version: %s\n", version_.c_str());
+ MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
+ id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
do_early_screenshot_ = options_->do_progress_updates;
@@ -2500,13 +2481,23 @@
PrintHeader();
if (options_->telephony_only) {
+ MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly();
DumpstateBoard();
} else if (options_->wifi_only) {
+ MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateWifiOnly();
} else {
+ // Invoking the critical dumpsys calls before DumpTraces() to try and
+ // keep the system stats as close to its initial state as possible.
+ RunDumpsysCritical();
+
+ // Run consent check only after critical dumpsys has finished -- so the consent
+ // isn't going to pollute the system state / logs.
+ MaybeCheckUserConsent(calling_uid, calling_package);
+
// Dump state for the default case. This also drops root.
- RunStatus s = DumpstateDefault();
+ RunStatus s = DumpstateDefaultAfterCritical();
if (s != RunStatus::OK) {
if (s == RunStatus::USER_CONSENT_DENIED) {
HandleUserConsentDenied();
@@ -2591,17 +2582,20 @@
: RunStatus::OK;
}
-void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
- if (calling_uid == AID_SHELL) {
+void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
+ if (calling_uid == AID_SHELL || !CalledByApi()) {
+ // No need to get consent for shell triggered dumpstates, or not through
+ // bugreporting API (i.e. no fd to copy back).
return;
}
consent_callback_ = new ConsentCallback();
const String16 incidentcompanion("incidentcompanion");
sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+ android::String16 package(calling_package.c_str());
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, String16(), String16(),
+ calling_uid, package, String16(), String16(),
0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
@@ -3498,8 +3492,8 @@
}
if (listener_ != nullptr) {
- if (percent % 5 == 0) {
- // We don't want to spam logcat, so only log multiples of 5.
+ if (percent % 10 == 0) {
+ // We don't want to spam logcat, so only log multiples of 10.
MYLOGD("Setting progress: %d/%d (%d%%)\n", progress, max, percent);
} else {
// stderr is ignored on normal invocations, but useful when calling
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 831574d..7d9b113 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -486,7 +486,9 @@
private:
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
- void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+ RunStatus DumpstateDefaultAfterCritical();
+
+ void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
// Removes the in progress files output files (tmp file, zip/txt file, screenshot),
// but leaves the log file alone.
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index bb92fd3..5476319 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -36,7 +36,10 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandCompose.cpp",
"vibrator/CommandGetCapabilities.cpp",
+ "vibrator/CommandGetCompositionDelayMax.cpp",
+ "vibrator/CommandGetCompositionSizeMax.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
new file mode 100644
index 0000000..705e40b
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositeEffect;
+
+class CommandCompose : public Command {
+ std::string getDescription() const override { return "Compose vibration."; }
+
+ std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<delay>", {"In milliseconds"}},
+ {"<primitive>", {"Primitive ID."}},
+ {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+ {"...", {"May repeat multiple times."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ while (!args.empty()) {
+ CompositeEffect effect;
+ if (auto delay = args.pop<decltype(effect.delayMs)>()) {
+ effect.delayMs = *delay;
+ std::cout << "Delay: " << effect.delayMs << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Delay!" << std::endl;
+ return USAGE;
+ }
+ // TODO: Use range validation when supported by AIDL
+ if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
+ effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (auto scale = args.pop<decltype(effect.scale)>();
+ scale && *scale > 0.0 && scale <= 1.0) {
+ effect.scale = *scale;
+ std::cout << "Scale: " << effect.scale << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Scale!" << std::endl;
+ return USAGE;
+ }
+ mComposite.emplace_back(std::move(effect));
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ std::vector<CompositeEffect> mComposite;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandCompose>("compose");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
new file mode 100644
index 0000000..b414307
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionDelayMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator composition delay max.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxDelayMs;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetCompositionDelayMax>(
+ "getCompositionDelayMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
new file mode 100644
index 0000000..360fc9d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionSizeMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator composition size max.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxSize;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Max Size: " << maxSize << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetCompositionSizeMax>(
+ "getCompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 6e2261f..33d7eed 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -54,7 +54,8 @@
Status ret;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::setAmplitude, mAmplitude);
+ auto status = hal->call(&aidl::IVibrator::setAmplitude,
+ static_cast<float>(mAmplitude) / UINT8_MAX);
statusStr = status.toString8();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e6e232c..95957a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -97,6 +97,7 @@
static constexpr int kVerityPageSize = 4096;
static constexpr size_t kSha256Size = 32;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+static constexpr const char* kFuseProp = "persist.sys.fuse";
namespace {
@@ -588,12 +589,21 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
if (flags & FLAG_CLEAR_CACHE_ONLY) {
// Clear only cached data from shared storage
auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
@@ -684,16 +694,26 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
+
path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
@@ -2574,12 +2594,19 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
+ } else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
- LOG(DEBUG) << "Found storage mount " << source << " at " << target;
- mStorageMounts[source] = target;
- }
+ if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
#endif
+ }
}
return ok();
}
diff --git a/include/android/input.h b/include/android/input.h
index ce439c6..f51cd79 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -158,7 +158,10 @@
AINPUT_EVENT_TYPE_KEY = 1,
/** Indicates that the input event is a motion event. */
- AINPUT_EVENT_TYPE_MOTION = 2
+ AINPUT_EVENT_TYPE_MOTION = 2,
+
+ /** Focus event */
+ AINPUT_EVENT_TYPE_FOCUS = 3,
};
/**
diff --git a/include/binder b/include/binder
deleted file mode 120000
index 35a022a..0000000
--- a/include/binder
+++ /dev/null
@@ -1 +0,0 @@
-../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/ActivityManager.h b/include/binder/ActivityManager.h
new file mode 120000
index 0000000..018f7a5
--- /dev/null
+++ b/include/binder/ActivityManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ActivityManager.h
\ No newline at end of file
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
new file mode 120000
index 0000000..4658269
--- /dev/null
+++ b/include/binder/AppOpsManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/AppOpsManager.h
\ No newline at end of file
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 120000
index 0000000..0fc6db7
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Binder.h
\ No newline at end of file
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 120000
index 0000000..370b260
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BinderService.h
\ No newline at end of file
diff --git a/include/binder/BpBinder.h b/include/binder/BpBinder.h
new file mode 120000
index 0000000..bc1f3d5
--- /dev/null
+++ b/include/binder/BpBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BpBinder.h
\ No newline at end of file
diff --git a/include/binder/BufferedTextOutput.h b/include/binder/BufferedTextOutput.h
new file mode 120000
index 0000000..fcad4fa
--- /dev/null
+++ b/include/binder/BufferedTextOutput.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BufferedTextOutput.h
\ No newline at end of file
diff --git a/include/binder/Debug.h b/include/binder/Debug.h
new file mode 120000
index 0000000..d76a7f1
--- /dev/null
+++ b/include/binder/Debug.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Debug.h
\ No newline at end of file
diff --git a/include/binder/IActivityManager.h b/include/binder/IActivityManager.h
new file mode 120000
index 0000000..4a868e0
--- /dev/null
+++ b/include/binder/IActivityManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IActivityManager.h
\ No newline at end of file
diff --git a/include/binder/IAppOpsCallback.h b/include/binder/IAppOpsCallback.h
new file mode 120000
index 0000000..d587a0c
--- /dev/null
+++ b/include/binder/IAppOpsCallback.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IAppOpsCallback.h
\ No newline at end of file
diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h
new file mode 120000
index 0000000..9e1c15a
--- /dev/null
+++ b/include/binder/IAppOpsService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IAppOpsService.h
\ No newline at end of file
diff --git a/include/binder/IBatteryStats.h b/include/binder/IBatteryStats.h
new file mode 120000
index 0000000..689b540
--- /dev/null
+++ b/include/binder/IBatteryStats.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBatteryStats.h
\ No newline at end of file
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 120000
index 0000000..93a6219
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBinder.h
\ No newline at end of file
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 120000
index 0000000..8579878
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IInterface.h
\ No newline at end of file
diff --git a/include/binder/IMediaResourceMonitor.h b/include/binder/IMediaResourceMonitor.h
new file mode 120000
index 0000000..d23a4da
--- /dev/null
+++ b/include/binder/IMediaResourceMonitor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMediaResourceMonitor.h
\ No newline at end of file
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 120000
index 0000000..5171c08
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMemory.h
\ No newline at end of file
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 120000
index 0000000..ecd4f81
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file
diff --git a/include/binder/IPermissionController.h b/include/binder/IPermissionController.h
new file mode 120000
index 0000000..6f33c58
--- /dev/null
+++ b/include/binder/IPermissionController.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPermissionController.h
\ No newline at end of file
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
new file mode 120000
index 0000000..be0933f
--- /dev/null
+++ b/include/binder/IProcessInfoService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IProcessInfoService.h
\ No newline at end of file
diff --git a/include/binder/IResultReceiver.h b/include/binder/IResultReceiver.h
new file mode 120000
index 0000000..b10d19a
--- /dev/null
+++ b/include/binder/IResultReceiver.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IResultReceiver.h
\ No newline at end of file
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 120000
index 0000000..33d18cc
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file
diff --git a/include/binder/IShellCallback.h b/include/binder/IShellCallback.h
new file mode 120000
index 0000000..8931195
--- /dev/null
+++ b/include/binder/IShellCallback.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IShellCallback.h
\ No newline at end of file
diff --git a/include/binder/IUidObserver.h b/include/binder/IUidObserver.h
new file mode 120000
index 0000000..1382897
--- /dev/null
+++ b/include/binder/IUidObserver.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IUidObserver.h
\ No newline at end of file
diff --git a/include/binder/IpPrefix.h b/include/binder/IpPrefix.h
new file mode 120000
index 0000000..655c774
--- /dev/null
+++ b/include/binder/IpPrefix.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IpPrefix.h
\ No newline at end of file
diff --git a/include/binder/MemoryBase.h b/include/binder/MemoryBase.h
new file mode 120000
index 0000000..3fd3e99
--- /dev/null
+++ b/include/binder/MemoryBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryBase.h
\ No newline at end of file
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 120000
index 0000000..71881fb
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 120000
index 0000000..8fb51cc
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 120000
index 0000000..23492be
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcel.h
\ No newline at end of file
diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h
new file mode 120000
index 0000000..777bd49
--- /dev/null
+++ b/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
new file mode 120000
index 0000000..438e223
--- /dev/null
+++ b/include/binder/Parcelable.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file
diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h
new file mode 120000
index 0000000..e910c12
--- /dev/null
+++ b/include/binder/PermissionCache.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file
diff --git a/include/binder/PermissionController.h b/include/binder/PermissionController.h
new file mode 120000
index 0000000..b6e1928
--- /dev/null
+++ b/include/binder/PermissionController.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionController.h
\ No newline at end of file
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
new file mode 120000
index 0000000..785f2b4
--- /dev/null
+++ b/include/binder/PersistableBundle.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file
diff --git a/include/binder/ProcessInfoService.h b/include/binder/ProcessInfoService.h
new file mode 120000
index 0000000..e67eb19
--- /dev/null
+++ b/include/binder/ProcessInfoService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessInfoService.h
\ No newline at end of file
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 120000
index 0000000..4cbe7a5
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file
diff --git a/include/binder/SafeInterface.h b/include/binder/SafeInterface.h
new file mode 120000
index 0000000..7cefe94
--- /dev/null
+++ b/include/binder/SafeInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/SafeInterface.h
\ No newline at end of file
diff --git a/include/binder/Stability.h b/include/binder/Stability.h
new file mode 120000
index 0000000..9b431d2
--- /dev/null
+++ b/include/binder/Stability.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Stability.h
\ No newline at end of file
diff --git a/include/binder/Status.h b/include/binder/Status.h
new file mode 120000
index 0000000..ccb994e
--- /dev/null
+++ b/include/binder/Status.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Status.h
\ No newline at end of file
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
new file mode 120000
index 0000000..2abd209
--- /dev/null
+++ b/include/binder/TextOutput.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/TextOutput.h
\ No newline at end of file
diff --git a/include/input/Input.h b/include/input/Input.h
index cbd1a41..f871847 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,8 +31,8 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
-
#include <limits>
+#include <queue>
/*
* Additional private constants not defined in ndk/ui/input.h.
@@ -167,6 +167,8 @@
class Parcel;
#endif
+const char* inputEventTypeToString(int32_t type);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -687,6 +689,28 @@
};
/*
+ * Focus events.
+ */
+class FocusEvent : public InputEvent {
+public:
+ virtual ~FocusEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; }
+
+ inline bool getHasFocus() const { return mHasFocus; }
+
+ inline bool getInTouchMode() const { return mInTouchMode; }
+
+ void initialize(bool hasFocus, bool inTouchMode);
+
+ void initialize(const FocusEvent& from);
+
+protected:
+ bool mHasFocus;
+ bool mInTouchMode;
+};
+
+/*
* Input event factory.
*/
class InputEventFactoryInterface {
@@ -698,6 +722,7 @@
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
+ virtual FocusEvent* createFocusEvent() = 0;
};
/*
@@ -709,12 +734,14 @@
PreallocatedInputEventFactory() { }
virtual ~PreallocatedInputEventFactory() { }
- virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
- virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
+ virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
+ virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
+ virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
+ FocusEvent mFocusEvent;
};
/*
@@ -725,16 +752,18 @@
explicit PooledInputEventFactory(size_t maxPoolSize = 20);
virtual ~PooledInputEventFactory();
- virtual KeyEvent* createKeyEvent();
- virtual MotionEvent* createMotionEvent();
+ virtual KeyEvent* createKeyEvent() override;
+ virtual MotionEvent* createMotionEvent() override;
+ virtual FocusEvent* createFocusEvent() override;
void recycle(InputEvent* event);
private:
const size_t mMaxPoolSize;
- Vector<KeyEvent*> mKeyEventPool;
- Vector<MotionEvent*> mMotionEventPool;
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
};
} // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 94d90ad..ae47438 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -64,6 +64,7 @@
KEY,
MOTION,
FINISHED,
+ FOCUS,
};
struct Header {
@@ -92,9 +93,7 @@
uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
- inline size_t size() const {
- return sizeof(Key);
- }
+ inline size_t size() const { return sizeof(Key); }
} key;
struct Motion {
@@ -110,7 +109,7 @@
int32_t metaState;
int32_t buttonState;
MotionClassification classification; // base type: uint8_t
- uint8_t empty2[3];
+ uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
float xOffset;
@@ -121,11 +120,16 @@
float yCursorPosition;
uint32_t pointerCount;
uint32_t empty3;
- // Note that PointerCoords requires 8 byte alignment.
+ /**
+ * The "pointers" field must be the last field of the struct InputMessage.
+ * When we send the struct InputMessage across the socket, we are not
+ * writing the entire "pointers" array, but only the pointerCount portion
+ * of it as an optimization. Adding a field after "pointers" would break this.
+ */
struct Pointer {
PointerProperties properties;
PointerCoords coords;
- } pointers[MAX_POINTERS];
+ } pointers[MAX_POINTERS] __attribute__((aligned(8)));
int32_t getActionId() const {
uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -141,12 +145,19 @@
struct Finished {
uint32_t seq;
- bool handled;
+ uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
- inline size_t size() const {
- return sizeof(Finished);
- }
+ inline size_t size() const { return sizeof(Finished); }
} finished;
+
+ struct Focus {
+ uint32_t seq;
+ // The following two fields take up 4 bytes total
+ uint16_t hasFocus; // actually a bool
+ uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+
+ inline size_t size() const { return sizeof(Focus); }
+ } focus;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -289,6 +300,15 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
+ /* Publishes a focus event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode);
+
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
* If a signal was received, returns the message sequence number,
* and whether the consumer handled the message.
@@ -344,8 +364,8 @@
* Returns NO_MEMORY if the event could not be created.
* Other errors probably indicate that the channel is broken.
*/
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
+ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent);
/* Sends a finished signal to the publisher to inform it that the message
* with the specified sequence number has finished being process and whether
@@ -516,6 +536,7 @@
static void rewriteMessage(TouchState& state, InputMessage& msg);
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+ static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 28ffa48..8d72a6b 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -38,12 +38,32 @@
// ----------------------------------------------------------------------
+/**
+ * If this is a local object and the descriptor matches, this will return the
+ * actual local object which is implementing the interface. Otherwise, this will
+ * return a proxy to the interface without checking the interface descriptor.
+ * This means that subsequent calls may fail with BAD_TYPE.
+ */
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
+/**
+ * This is the same as interface_cast, except that it always checks to make sure
+ * the descriptor matches, and if it doesn't match, it will return nullptr.
+ */
+template<typename INTERFACE>
+inline sp<INTERFACE> checked_interface_cast(const sp<IBinder>& obj)
+{
+ if (obj->getInterfaceDescriptor() != INTERFACE::descriptor) {
+ return nullptr;
+ }
+
+ return interface_cast<INTERFACE>(obj);
+}
+
// ----------------------------------------------------------------------
template<typename INTERFACE>
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 56b94c1..0477801 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <dumputils/dump_utils.h>
#include <log/log.h>
@@ -68,13 +70,34 @@
NULL,
};
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+ // extra hals to dump are already filled
+ if (extra_hal_interfaces_to_dump.size() > 0) {
+ return;
+ }
+ std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+ std::vector<std::string> tokens = android::base::Split(value, ",");
+ for (const auto &token : tokens) {
+ std::string trimmed_token = android::base::Trim(token);
+ if (trimmed_token.length() == 0) {
+ continue;
+ }
+ extra_hal_interfaces_to_dump.insert(trimmed_token);
+ }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
for (const char** i = hal_interfaces_to_dump; *i; i++) {
- if (!strcmp(*i, interface)) {
+ if (interface == *i) {
return true;
}
}
- return false;
+ return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
}
bool should_dump_native_traces(const char* path) {
@@ -94,13 +117,15 @@
sp<IServiceManager> manager = IServiceManager::getService();
std::set<int> pids;
+ read_extra_hals_to_dump_from_property();
+
Return<void> ret = manager->debugDump([&](auto& hals) {
for (const auto &info : hals) {
if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
continue;
}
- if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ if (!should_dump_hal_interface(info.interfaceName)) {
continue;
}
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 3bded7f..0330dac 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -39,77 +39,210 @@
namespace gralloc4 {
-bool isStandardMetadataType(const MetadataType& metadataType) {
- return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+static inline bool hasAdditionOverflow(size_t a, size_t b) {
+ return a > SIZE_MAX - b;
}
-StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
- return static_cast<StandardMetadataType>(metadataType.value);
-}
+/**
+ * OutputHidlVec represents the hidl_vec that is outputed when a type is encoded into a byte stream.
+ * This class is used to track the current state of a hidl_vec as it is filled with the encoded
+ * byte stream.
+ *
+ * This type is needed because hidl_vec's resize() allocates a new backing array every time.
+ * This type does not need an copies and only needs one resize operation.
+ */
+class OutputHidlVec {
+public:
+ OutputHidlVec(hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
-status_t copyToHidlVec(const std::vector<uint8_t>& vec, hidl_vec<uint8_t>* hidlVec) {
- if (!hidlVec) {
- return BAD_VALUE;
+ status_t resize() {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ mVec->resize(mNeededResize);
+ return NO_ERROR;
}
- hidlVec->setToExternal(const_cast<uint8_t*>(vec.data()), vec.size(), false /*shouldOwn*/);
+ status_t encode(const uint8_t* data, size_t size) {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ if (mVec->size() == 0) {
+ if (hasAdditionOverflow(mNeededResize, size)) {
+ clear();
+ return BAD_VALUE;
+ }
+ mNeededResize += size;
+ return NO_ERROR;
+ }
+
+ if (hasAdditionOverflow(mOffset, size) || (mVec->size() < size + mOffset)) {
+ clear();
+ return BAD_VALUE;
+ }
+
+ std::copy(data, data + size, mVec->data() + mOffset);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ void clear() {
+ if (mVec) {
+ mVec->resize(0);
+ }
+ mNeededResize = 0;
+ mOffset = 0;
+ }
+
+private:
+ hidl_vec<uint8_t>* mVec;
+ size_t mNeededResize = 0;
+ size_t mOffset = 0;
+};
+
+/**
+ * InputHidlVec represents the hidl_vec byte stream that is inputed when a type is decoded.
+ * This class is used to track the current index of the byte stream of the hidl_vec as it is
+ * decoded.
+ */
+class InputHidlVec {
+public:
+ InputHidlVec(const hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
+
+ status_t decode(uint8_t* data, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ std::copy(mVec->data() + mOffset, mVec->data() + mOffset + size, data);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ status_t decode(std::string* string, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ string->assign(mVec->data() + mOffset, mVec->data() + mOffset + size);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ bool hasRemainingData() {
+ if (!mVec) {
+ return false;
+ }
+ return mVec->size() - mOffset;
+ }
+
+private:
+ const hidl_vec<uint8_t>* mVec;
+ size_t mOffset = 0;
+};
+
+/**
+ * EncodeHelper is a function type that encodes T into the OutputHidlVec.
+ */
+template<class T>
+using EncodeHelper = status_t(*)(const T&, OutputHidlVec*);
+
+/**
+ * DecodeHelper is a function type that decodes InputHidlVec into T.
+ */
+template<class T>
+using DecodeHelper = status_t(*)(InputHidlVec*, T*);
+
+/**
+ * ErrorHandler is a function type that is called when the corresponding DecodeHelper function
+ * fails. ErrorHandler cleans up the object T so the caller doesn't receive a partially created
+ * T.
+ */
+template<class T>
+using ErrorHandler = void(*)(T*);
+
+/**
+ * encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
+ * into the hidl_vec byte stream.
+ *
+ * This function first calls the encodeHelper function to determine how large the hidl_vec
+ * needs to be. It resizes the hidl_vec. Finally, it reruns the encodeHelper function which
+ * encodes T into the hidl_vec byte stream.
+ */
+template <class T>
+status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ OutputHidlVec outputHidlVec{output};
+ status_t err = encodeHelper(input, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = outputHidlVec.resize();
+ if (err) {
+ return err;
+ }
+
+ return encodeHelper(input, &outputHidlVec);
+}
+
+/**
+ * decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
+ * turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
+ * T.
+ */
+template <class T>
+status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
+ ErrorHandler<T> errorHandler = nullptr) {
+ InputHidlVec inputHidlVec{&input};
+ status_t err = decodeHelper(&inputHidlVec, output);
+ if (err) {
+ return err;
+ }
+
+ err = inputHidlVec.hasRemainingData();
+ if (err) {
+ if (errorHandler) {
+ errorHandler(output);
+ }
+ return BAD_VALUE;
+ }
return NO_ERROR;
}
+/**
+ * Private helper functions
+ */
template <class T>
-status_t encodeInteger(T input, std::vector<uint8_t>* output) {
- static_assert(std::is_same<T, uint32_t>::value ||
- std::is_same<T, int32_t>::value ||
- std::is_same<T, uint64_t>::value ||
- std::is_same<T, int64_t>::value);
+status_t encodeInteger(const T& input, OutputHidlVec* output) {
+ static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
if (!output) {
return BAD_VALUE;
}
- size_t outputOffset = output->size();
- size_t size = sizeof(input);
-
- if (outputOffset > UINT_MAX - size) {
- return BAD_VALUE;
- }
- output->resize(size + outputOffset);
-
- uint8_t* tmp = reinterpret_cast<uint8_t*>(&input);
- std::copy(tmp, tmp + size, output->data() + outputOffset);
-
- return NO_ERROR;
+ const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+ return output->encode(tmp, sizeof(input));
}
template <class T>
-status_t decodeInteger(const hidl_vec<uint8_t>& input, T* output, size_t* inputOffset = nullptr) {
+status_t decodeInteger(InputHidlVec* input, T* output) {
+ static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
if (!output) {
return BAD_VALUE;
}
- size_t offset = (inputOffset)? *inputOffset: 0;
- if (offset >= input.size()) {
- return BAD_VALUE;
- }
- size_t inputMaxSize = input.size() - offset;
- size_t outputSize = sizeof(*output);
-
- if (inputMaxSize < outputSize) {
- return BAD_VALUE;
- }
-
uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
- const uint8_t* data = input.data() + offset;
- std::copy(data, data + outputSize, tmp);
-
- if (inputOffset) {
- *inputOffset += outputSize;
- }
-
- return NO_ERROR;
+ return input->decode(tmp, sizeof(*output));
}
-status_t encodeString(const std::string& input, std::vector<uint8_t>* output) {
+status_t encodeString(const std::string& input, OutputHidlVec* output) {
if (!output) {
return BAD_VALUE;
}
@@ -119,66 +252,60 @@
return err;
}
- size_t outputOffset = output->size();
- size_t size = input.size();
- output->resize(size + outputOffset);
-
- std::copy(input.c_str(), input.c_str() + size, output->data() + outputOffset);
-
- return NO_ERROR;
+ return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
}
-status_t decodeString(const hidl_vec<uint8_t>& input, std::string* output, size_t* inputOffset = nullptr) {
+status_t decodeString(InputHidlVec* input, std::string* output) {
if (!output) {
return BAD_VALUE;
}
int64_t size = 0;
- status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+ status_t err = decodeInteger<int64_t>(input, &size);
if (err || size < 0) {
return err;
}
- size_t offset = (inputOffset)? *inputOffset + sizeof(size): sizeof(size);
- if ((offset > UINT_MAX - size) || (offset + size > input.size())) {
- return BAD_VALUE;
- }
-
- auto data = input.data() + offset;
- output->assign(data, data + size);
-
- if (inputOffset) {
- *inputOffset += size;
- }
-
- return NO_ERROR;
+ return input->decode(output, size);
}
-status_t encodeExtendableType(const ExtendableType& input, std::vector<uint8_t>* output) {
+status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
status_t err = encodeString(input.name, output);
if (err) {
return err;
}
- return encodeInteger<int64_t>(input.value, output);
-}
-
-status_t decodeExtendableType(const hidl_vec<uint8_t>& input, ExtendableType* output, size_t* inputOffset = nullptr) {
- status_t err = decodeString(input, &output->name, inputOffset);
+ err = encodeInteger<int64_t>(input.value, output);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->value, inputOffset);
- if (err) {
- output->name.clear();
- return err;
- }
-
return NO_ERROR;
}
-status_t encodeRect(const Rect& input, std::vector<uint8_t>* output) {
+status_t decodeExtendableType(InputHidlVec* input, ExtendableType* output) {
+ status_t err = decodeString(input, &output->name);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+void clearExtendableType(ExtendableType* output) {
+ if (!output) {
+ return;
+ }
+ output->name.clear();
+ output->value = 0;
+}
+
+status_t encodeRect(const Rect& input, OutputHidlVec* output) {
status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
if (err) {
return err;
@@ -194,23 +321,23 @@
return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
}
-status_t decodeRect(const hidl_vec<uint8_t>& input, Rect* output, size_t* inputOffset = nullptr) {
- status_t err = decodeInteger<int32_t>(input, &output->left, inputOffset);
+status_t decodeRect(InputHidlVec* input, Rect* output) {
+ status_t err = decodeInteger<int32_t>(input, &output->left);
if (err) {
return err;
}
- err = decodeInteger<int32_t>(input, &output->top, inputOffset);
+ err = decodeInteger<int32_t>(input, &output->top);
if (err) {
return err;
}
- err = decodeInteger<int32_t>(input, &output->right, inputOffset);
+ err = decodeInteger<int32_t>(input, &output->right);
if (err) {
return err;
}
- return decodeInteger<int32_t>(input, &output->bottom, inputOffset);
+ return decodeInteger<int32_t>(input, &output->bottom);
}
-status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
if (!output) {
return BAD_VALUE;
}
@@ -226,23 +353,23 @@
return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
}
-status_t decodePlaneLayoutComponent(const hidl_vec<uint8_t>& input, PlaneLayoutComponent* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayoutComponent(InputHidlVec* input, PlaneLayoutComponent* output) {
if (!output) {
return BAD_VALUE;
}
- status_t err = decodeExtendableType(input, &output->type, inputOffset);
+ status_t err = decodeExtendableType(input, &output->type);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->offsetInBits, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->offsetInBits);
if (err) {
return err;
}
- return decodeInteger<int64_t>(input, &output->sizeInBits, inputOffset);
+ return decodeInteger<int64_t>(input, &output->sizeInBits);
}
-status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, OutputHidlVec* output) {
if (!output) {
return BAD_VALUE;
}
@@ -262,20 +389,20 @@
return NO_ERROR;
}
-status_t decodePlaneLayoutComponents(const hidl_vec<uint8_t>& input, std::vector<PlaneLayoutComponent>* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayoutComponents(InputHidlVec* input, std::vector<PlaneLayoutComponent>* output) {
if (!output) {
return BAD_VALUE;
}
int64_t size = 0;
- status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+ status_t err = decodeInteger<int64_t>(input, &size);
if (err || size < 0) {
return err;
}
for (int i = 0; i < size; i++) {
output->emplace_back();
- err = decodePlaneLayoutComponent(input, &output->back(), inputOffset);
+ err = decodePlaneLayoutComponent(input, &output->back());
if (err) {
return err;
}
@@ -283,7 +410,7 @@
return NO_ERROR;
}
-status_t encodePlaneLayout(const PlaneLayout& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayout(const PlaneLayout& input, OutputHidlVec* output) {
if (!output) {
return BAD_VALUE;
}
@@ -329,307 +456,239 @@
return encodeRect(input.crop, output);
}
-status_t decodePlaneLayout(const hidl_vec<uint8_t>& input, PlaneLayout* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
if (!output) {
return BAD_VALUE;
}
- status_t err = decodePlaneLayoutComponents(input, &output->components, inputOffset);
+ status_t err = decodePlaneLayoutComponents(input, &output->components);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->offsetInBytes, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->offsetInBytes);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->strideInBytes, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->strideInBytes);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->widthInSamples, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->widthInSamples);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->heightInSamples, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->heightInSamples);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->totalSizeInBytes, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->totalSizeInBytes);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->horizontalSubsampling, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->horizontalSubsampling);
if (err) {
return err;
}
- err = decodeInteger<int64_t>(input, &output->verticalSubsampling, inputOffset);
+ err = decodeInteger<int64_t>(input, &output->verticalSubsampling);
if (err) {
return err;
}
- return decodeRect(input, &output->crop, inputOffset);
+ return decodeRect(input, &output->crop);
}
-status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
- if (!outPlaneLayouts) {
- return BAD_VALUE;
- }
-
- std::vector<uint8_t> tmpOutPlaneLayouts;
-
- status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), &tmpOutPlaneLayouts);
+status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), outOutputHidlVec);
if (err) {
return err;
}
for (const auto& planeLayout : planeLayouts) {
- err = encodePlaneLayout(planeLayout, &tmpOutPlaneLayouts);
+ err = encodePlaneLayout(planeLayout, outOutputHidlVec);
if (err) {
return err;
}
}
- return copyToHidlVec(tmpOutPlaneLayouts, outPlaneLayouts);
+ return NO_ERROR;
}
-status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
- if (!outPlaneLayouts) {
- return BAD_VALUE;
- }
-
- size_t offset = 0;
+status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
int64_t size = 0;
- status_t err = decodeInteger<int64_t>(planeLayouts, &size, &offset);
+ status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
if (err || size < 0) {
return err;
}
for (size_t i = 0; i < size; i++) {
outPlaneLayouts->emplace_back();
- err = decodePlaneLayout(planeLayouts, &outPlaneLayouts->back(), &offset);
+ err = decodePlaneLayout(inputHidlVec, &outPlaneLayouts->back());
if (err) {
- outPlaneLayouts->resize(0);
return err;
}
}
- if (offset < planeLayouts.size()) {
- return BAD_VALUE;
- }
-
return NO_ERROR;
}
-status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
- std::vector<uint8_t> tmpOutBufferId;
- status_t err = encodeInteger<uint64_t>(bufferId, &tmpOutBufferId);
- if (err) {
- return err;
+void clearPlaneLayouts(std::vector<PlaneLayout>* output) {
+ if (!output) {
+ return;
}
- return copyToHidlVec(tmpOutBufferId, outBufferId);
+ output->clear();
+}
+
+/**
+ * Public API functions
+ */
+bool isStandardMetadataType(const MetadataType& metadataType) {
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+ return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+ return encode(bufferId, outBufferId, encodeInteger);
}
status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
- return decodeInteger<uint64_t>(bufferId, outBufferId);
+ return decode(bufferId, outBufferId, decodeInteger);
}
status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
- std::vector<uint8_t> tmpOutName;
- status_t err = encodeString(name, &tmpOutName);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutName, outName);
+ return encode(name, outName, encodeString);
}
status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
- return decodeString(name, outName);
+ return decode(name, outName, decodeString);
}
status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
- std::vector<uint8_t> tmpOutWidth;
- status_t err = encodeInteger<uint64_t>(width, &tmpOutWidth);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutWidth, outWidth);
+ return encode(width, outWidth, encodeInteger);
}
status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
- return decodeInteger<uint64_t>(width, outWidth);
+ return decode(width, outWidth, decodeInteger);
}
status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
- std::vector<uint8_t> tmpOutHeight;
- status_t err = encodeInteger<uint64_t>(height, &tmpOutHeight);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutHeight, outHeight);
+ return encode(height, outHeight, encodeInteger);
}
status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
- return decodeInteger<uint64_t>(height, outHeight);
+ return decode(height, outHeight, decodeInteger);
}
status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
- std::vector<uint8_t> tmpOutLayerCount;
- status_t err = encodeInteger<uint64_t>(layerCount, &tmpOutLayerCount);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutLayerCount, outLayerCount);
+ return encode(layerCount, outLayerCount, encodeInteger);
}
status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
- return decodeInteger<uint64_t>(layerCount, outLayerCount);
+ return decode(layerCount, outLayerCount, decodeInteger);
}
-status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, hidl_vec<uint8_t>* outPixelFormatRequested) {
- std::vector<uint8_t> tmpOutPixelFormatRequested;
- status_t err = encodeInteger<int32_t>(static_cast<int32_t>(pixelFormatRequested), &tmpOutPixelFormatRequested);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutPixelFormatRequested, outPixelFormatRequested);
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
+ hidl_vec<uint8_t>* outPixelFormatRequested) {
+ return encode(static_cast<int32_t>(pixelFormatRequested), outPixelFormatRequested, encodeInteger);
}
-status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
- return decodeInteger<int32_t>(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested));
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
+ hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+ return decode(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
}
status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
- std::vector<uint8_t> tmpOutPixelFormatFourCC;
- status_t err = encodeInteger<uint32_t>(pixelFormatFourCC, &tmpOutPixelFormatFourCC);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutPixelFormatFourCC, outPixelFormatFourCC);
+ return encode(pixelFormatFourCC, outPixelFormatFourCC, encodeInteger);
}
status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
- return decodeInteger<uint32_t>(pixelFormatFourCC, outPixelFormatFourCC);
+ return decode(pixelFormatFourCC, outPixelFormatFourCC, decodeInteger);
}
status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
- std::vector<uint8_t> tmpOutPixelFormatModifier;
- status_t err = encodeInteger<uint64_t>(pixelFormatModifier, &tmpOutPixelFormatModifier);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutPixelFormatModifier, outPixelFormatModifier);
+ return encode(pixelFormatModifier, outPixelFormatModifier, encodeInteger);
}
status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
- return decodeInteger<uint64_t>(pixelFormatModifier, outPixelFormatModifier);
+ return decode(pixelFormatModifier, outPixelFormatModifier, decodeInteger);
}
status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
- std::vector<uint8_t> tmpOutUsage;
- status_t err = encodeInteger<uint64_t>(usage, &tmpOutUsage);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutUsage, outUsage);
+ return encode(usage, outUsage, encodeInteger);
}
status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
- return decodeInteger<uint64_t>(usage, outUsage);
+ return decode(usage, outUsage, decodeInteger);
}
status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
- std::vector<uint8_t> tmpOutAllocationSize;
- status_t err = encodeInteger<uint64_t>(allocationSize, &tmpOutAllocationSize);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutAllocationSize, outAllocationSize);
+ return encode(allocationSize, outAllocationSize, encodeInteger);
}
status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
- return decodeInteger<uint64_t>(allocationSize, outAllocationSize);
+ return decode(allocationSize, outAllocationSize, decodeInteger);
}
status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
- std::vector<uint8_t> tmpOutProtectedContent;
- status_t err = encodeInteger<uint64_t>(protectedContent, &tmpOutProtectedContent);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutProtectedContent, outProtectedContent);
+ return encode(protectedContent, outProtectedContent, encodeInteger);
}
status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
- return decodeInteger<uint64_t>(protectedContent, outProtectedContent);
+ return decode(protectedContent, outProtectedContent, decodeInteger);
}
status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
- std::vector<uint8_t> tmpOutCompression;
- status_t err = encodeExtendableType(compression, &tmpOutCompression);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutCompression, outCompression);
+ return encode(compression, outCompression, encodeExtendableType);
}
status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
- return decodeExtendableType(compression, outCompression);
+ return decode(compression, outCompression, decodeExtendableType, clearExtendableType);
}
status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
- std::vector<uint8_t> tmpOutInterlaced;
- status_t err = encodeExtendableType(interlaced, &tmpOutInterlaced);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutInterlaced, outInterlaced);
+ return encode(interlaced, outInterlaced, encodeExtendableType);
}
status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
- return decodeExtendableType(interlaced, outInterlaced);
+ return decode(interlaced, outInterlaced, decodeExtendableType, clearExtendableType);
}
status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
- std::vector<uint8_t> tmpOutChromaSiting;
- status_t err = encodeExtendableType(chromaSiting, &tmpOutChromaSiting);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutChromaSiting, outChromaSiting);
+ return encode(chromaSiting, outChromaSiting, encodeExtendableType);
}
status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
- return decodeExtendableType(chromaSiting, outChromaSiting);
+ return decode(chromaSiting, outChromaSiting, decodeExtendableType, clearExtendableType);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+ return encode(planeLayouts, outPlaneLayouts, encodePlaneLayoutsHelper);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+ return decode(planeLayouts, outPlaneLayouts, decodePlaneLayoutsHelper, clearPlaneLayouts);
}
status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
- std::vector<uint8_t> tmpOutDataspace;
- status_t err = encodeInteger<int32_t>(static_cast<int32_t>(dataspace), &tmpOutDataspace);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutDataspace, outDataspace);
+ return encode(static_cast<int32_t>(dataspace), outDataspace, encodeInteger);
}
status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
- return decodeInteger<int32_t>(dataspace, reinterpret_cast<int32_t*>(outDataspace));
+ return decode(dataspace, reinterpret_cast<int32_t*>(outDataspace), decodeInteger);
}
status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
- std::vector<uint8_t> tmpOutBlendMode;
- status_t err = encodeInteger<int32_t>(static_cast<int32_t>(blendMode), &tmpOutBlendMode);
- if (err) {
- return err;
- }
- return copyToHidlVec(tmpOutBlendMode, outBlendMode);
+ return encode(static_cast<int32_t>(blendMode), outBlendMode, encodeInteger);
}
status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
- return decodeInteger<int32_t>(blendMode, reinterpret_cast<int32_t*>(outBlendMode));
+ return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
}
} // namespace gralloc4
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
new file mode 100644
index 0000000..8444883
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+cc_fuzz {
+ name: "libgralloctypes_fuzzer",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["marissaw@google.com"],
+ },
+
+ srcs: [
+ "gralloctypes.cpp",
+ "main.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libgralloctypes",
+ "libhidlbase",
+ "liblog",
+ "libprocessgroup",
+ "libjsoncpp",
+ "libutils",
+ ],
+
+ // This flag enables verbose output in the fuzz target, and is very useful
+ // for debugging a failure. If you are trying to diagnose how a crash was
+ // produced, you may find uncommenting the below line very useful.
+ // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
new file mode 100644
index 0000000..23c90b8
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 FUZZ_LOG_TAG "gralloctypes"
+
+#include <cstdint>
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+#include <utils/Errors.h>
+
+#include "gralloctypes.h"
+#include "util.h"
+
+using ::android::status_t;
+
+#define GRALLOCTYPES_DECODE(T, FUNC) \
+ [] (const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
+
+// clang-format off
+std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeBufferId),
+ GRALLOCTYPES_DECODE(std::string, ::android::gralloc4::decodeName),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeWidth),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeHeight),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeLayerCount),
+ GRALLOCTYPES_DECODE(::android::hardware::graphics::common::V1_2::PixelFormat, ::android::gralloc4::decodePixelFormatRequested),
+ GRALLOCTYPES_DECODE(uint32_t, ::android::gralloc4::decodePixelFormatFourCC),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodePixelFormatModifier),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeUsage),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeAllocationSize),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeProtectedContent),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeCompression),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeInterlaced),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeChromaSiting),
+ GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+};
+// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
new file mode 100644
index 0000000..b995fce
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+
+#include <vector>
+
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t data)>;
+
+extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
new file mode 100644
index 0000000..2807878
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 FUZZ_LOG_TAG "main"
+
+#include "gralloctypes.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include <cstdlib>
+#include <ctime>
+
+void doFuzz(
+ const std::vector<GrallocTypesDecode>& decodes,
+ const std::vector<uint8_t>& input,
+ const std::vector<uint8_t>& instructions) {
+
+ ::android::hardware::hidl_vec<uint8_t> vec;
+ vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
+
+ // since we are only using a byte to index
+ CHECK(decodes.size() <= 255) << decodes.size();
+
+ for (size_t i = 0; i < instructions.size() - 1; i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t decodeIdx = a % decodes.size();
+
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+ << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(decodeIdx)
+ << ") arg: " << static_cast<size_t>(b) << " size: " << vec.size();
+
+ decodes[decodeIdx](vec, b);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // data to fill out parcel
+ size_t inputLen = size / 2;
+ std::vector<uint8_t> input(data, data + inputLen);
+ data += inputLen;
+ size -= inputLen;
+
+ // data to use to determine what to do
+ size_t instructionLen = size;
+ std::vector<uint8_t> instructions(data, data + instructionLen);
+ data += instructionLen;
+ size -= instructionLen;
+
+ CHECK(size == 0) << "size: " << size;
+
+ FUZZ_LOG() << "inputLen: " << inputLen << " instructionLen: " << instructionLen;
+ FUZZ_LOG() << "input: " << hexString(input);
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, input, instructions);
+ return 0;
+}
diff --git a/libs/gralloc/types/fuzzer/util.cpp b/libs/gralloc/types/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+ return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/gralloc/types/fuzzer/util.h b/libs/gralloc/types/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+ FuzzLog(const char* tag) : mTag(tag) {}
+ ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+ std::stringstream& log() { return mOs; }
+
+private:
+ const char* mTag = nullptr;
+ std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+ FuzzLog(const char* /*tag*/) {}
+ template <typename T>
+ FuzzLog& operator<<(const T& /*t*/) {
+ return *this;
+ }
+ FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index e062345..80588cd 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -33,15 +33,6 @@
namespace gralloc4 {
-/**
- * This library is compiled into VNDK-SP and FWK_ONLY copies. When a device is upgraded, the vendor
- * partition may choose to use an older copy of the VNDK-SP.
- *
- * Prepend the version to every encode and decode so the system partition can fallback to an older
- * version if necessary.
- */
-#define GRALLOC4_METADATA_VERSION 1
-
#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
#define GRALLOC4_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
#define GRALLOC4_COMPRESSION "android.hardware.graphics.common.Compression"
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4f605e0..1ae148c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -50,6 +50,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DebugEGLImageTracker.cpp",
+ "DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3c31d74..06a5f06 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "BLASTBufferQueue"
+
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -24,8 +27,14 @@
namespace android {
BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
- : mSurfaceControl(surface), mWidth(width), mHeight(height) {
+ : mSurfaceControl(surface),
+ mPendingCallbacks(0),
+ mWidth(width),
+ mHeight(height),
+ mNextTransaction(nullptr) {
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mConsumer->setMaxBufferCount(MAX_BUFFERS);
+ mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
mBufferItemConsumer =
new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
mBufferItemConsumer->setName(String8("BLAST Consumer"));
@@ -34,6 +43,8 @@
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
+
+ mAcquired = false;
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -59,20 +70,32 @@
const std::vector<SurfaceControlStats>& stats) {
std::unique_lock _lock{mMutex};
- if (stats.size() > 0 && mNextCallbackBufferItem.mGraphicBuffer != nullptr) {
+ if (stats.size() > 0 && !mShadowQueue.empty()) {
mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
stats[0].previousReleaseFence
? stats[0].previousReleaseFence
: Fence::NO_FENCE);
+ mAcquired = false;
mNextCallbackBufferItem = BufferItem();
mBufferItemConsumer->setTransformHint(stats[0].transformHint);
}
- mDequeueWaitCV.notify_all();
+ mPendingCallbacks--;
+ processNextBufferLocked();
+ mCallbackCV.notify_all();
decStrong((void*)transactionCallbackThunk);
}
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
- std::unique_lock _lock{mMutex};
+void BLASTBufferQueue::processNextBufferLocked() {
+ if (mShadowQueue.empty()) {
+ return;
+ }
+
+ if (mAcquired) {
+ return;
+ }
+
+ BufferItem item = std::move(mShadowQueue.front());
+ mShadowQueue.pop();
SurfaceComposerClient::Transaction localTransaction;
bool applyTransaction = true;
@@ -83,11 +106,11 @@
applyTransaction = false;
}
- int status = OK;
mNextCallbackBufferItem = mLastSubmittedBufferItem;
-
mLastSubmittedBufferItem = BufferItem();
- status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
+
+ status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
+ mAcquired = true;
if (status != OK) {
ALOGE("Failed to acquire?");
}
@@ -99,7 +122,6 @@
return;
}
-
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
@@ -112,17 +134,21 @@
t->setCrop(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
if (applyTransaction) {
- ALOGE("Apply transaction");
t->apply();
-
- if (mNextCallbackBufferItem.mGraphicBuffer != nullptr) {
- mDequeueWaitCV.wait_for(_lock, 5000ms);
- }
}
}
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+ std::lock_guard _lock{mMutex};
+
+ // add to shadow queue
+ mShadowQueue.push(item);
+ processNextBufferLocked();
+ mPendingCallbacks++;
+}
+
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
- std::unique_lock _lock{mMutex};
+ std::lock_guard _lock{mMutex};
mNextTransaction = t;
}
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..54f383e
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+ ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+ status_t result = mReceiver.initCheck();
+ if (result) {
+ ALOGW("Failed to initialize display event receiver, status=%d", result);
+ return result;
+ }
+
+ int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+ if (rc < 0) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+void DisplayEventDispatcher::dispose() {
+ ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
+
+ if (!mReceiver.initCheck()) {
+ mLooper->removeFd(mReceiver.getFd());
+ }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+ if (!mWaitingForVsync) {
+ ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+ // Drain all pending events.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+ ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+ }
+
+ status_t status = mReceiver.requestNextVsync();
+ if (status) {
+ ALOGW("Failed to request next vsync, status=%d", status);
+ return status;
+ }
+
+ mWaitingForVsync = true;
+ }
+ return OK;
+}
+
+int DisplayEventDispatcher::handleEvent(int, int events, void*) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x",
+ events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ events);
+ return 1; // keep the callback
+ }
+
+ // Drain all pending events, keep the last vsync.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+ ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+ this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ mWaitingForVsync = false;
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ }
+
+ return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+ PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount) {
+ bool gotVsync = false;
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ ssize_t n;
+ while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ // Later vsync events will just overwrite the info from earlier
+ // ones. That's fine, we only care about the most recent.
+ gotVsync = true;
+ *outTimestamp = ev.header.timestamp;
+ *outDisplayId = ev.header.displayId;
+ *outCount = ev.vsync.count;
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+ ev.config.configId);
+ break;
+ default:
+ ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+ }
+ return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index b9597db..546757b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -905,6 +905,85 @@
return reply.readInt32();
}
+ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
+ return result;
+ }
+ result = data.writeInt32(defaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write defaultModeId: %d", result);
+ return result;
+ }
+ result = data.writeFloat(minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write minRefreshRate: %d", result);
+ return result;
+ }
+ result = data.writeFloat(maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write maxRefreshRate: %d", result);
+ return result;
+ }
+
+ result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ if (!outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32(outDefaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read defaultModeId: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outMinRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read minRefreshRate: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outMaxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read maxRefreshRate: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
Parcel data, reply;
@@ -1583,6 +1662,72 @@
reply->writeInt32(result);
return result;
}
+ case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ int32_t defaultModeId;
+ status_t result = data.readInt32(&defaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultModeId: %d", result);
+ return result;
+ }
+ float minRefreshRate;
+ result = data.readFloat(&minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read minRefreshRate: %d", result);
+ return result;
+ }
+ float maxRefreshRate;
+ result = data.readFloat(&maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
+ return result;
+ }
+ result = setDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+ maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
+ reply->writeInt32(result);
+ return result;
+ }
+ case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ int32_t defaultModeId;
+ float minRefreshRate;
+ float maxRefreshRate;
+
+ status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultModeId,
+ &minRefreshRate, &maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
+
+ result = reply->writeInt32(defaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultModeId: %d", result);
+ return result;
+ }
+ result = reply->writeFloat(minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write minRefreshRate: %d", result);
+ return result;
+ }
+ result = reply->writeFloat(maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write maxRefreshRate: %d", result);
+ return result;
+ }
+ reply->writeInt32(result);
+ return result;
+ }
case GET_DISPLAY_BRIGHTNESS_SUPPORT: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2ab4d8a..9d7d7d0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -189,48 +189,49 @@
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ {
+ std::lock_guard<std::mutex> 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>, SurfaceComposerClient::IBinderHash>
- surfaceControls;
- for (const auto& transactionStats : listenerStats.transactionStats) {
- for (auto callbackId : transactionStats.callbackIds) {
- auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
- surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+ /* 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.
+ */
+ callbacksMap = mCallbacks;
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto& callbackId : transactionStats.callbackIds) {
+ mCallbacks.erase(callbackId);
+ }
}
}
-
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto callbackId : transactionStats.callbackIds) {
- auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
continue;
}
std::vector<SurfaceControlStats> surfaceControlStats;
for (const auto& surfaceStats : transactionStats.surfaceStats) {
- surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
- surfaceStats.acquireTime,
- surfaceStats.previousReleaseFence,
- surfaceStats.transformHint);
- surfaceControls[surfaceStats.surfaceControl]->setTransformHint(
- surfaceStats.transformHint);
+ surfaceControlStats
+ .emplace_back(callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl],
+ surfaceStats.acquireTime, surfaceStats.previousReleaseFence,
+ surfaceStats.transformHint);
+ callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl]
+ ->setTransformHint(surfaceStats.transformHint);
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
- mCallbacks.erase(callbackId);
}
}
}
@@ -1616,6 +1617,26 @@
outAllowedConfigs);
}
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId,
+ float minRefreshRate,
+ float maxRefreshRate) {
+ return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
+ defaultModeId,
+ minRefreshRate,
+ maxRefreshRate);
+}
+
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
+ outDefaultModeId,
+ outMinRefreshRate,
+ outMaxRefreshRate);
+}
+
status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
Vector<ColorMode>* outColorModes) {
return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 6320556..f1758a2 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -27,6 +27,7 @@
#include <utils/RefBase.h>
#include <system/window.h>
+#include <thread>
namespace android {
@@ -50,7 +51,6 @@
void setNextTransaction(SurfaceComposerClient::Transaction *t);
void update(const sp<SurfaceControl>& surface, int width, int height);
-
virtual ~BLASTBufferQueue() = default;
@@ -61,32 +61,35 @@
BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
BLASTBufferQueue(const BLASTBufferQueue& rhs);
- sp<SurfaceControl> mSurfaceControl;
-
- mutable std::mutex mMutex;
+ void processNextBufferLocked() REQUIRES(mMutex);
- static const int MAX_BUFFERS = 2;
+ sp<SurfaceControl> mSurfaceControl;
+
+ std::mutex mMutex;
+ std::condition_variable mCallbackCV;
+ uint64_t mPendingCallbacks GUARDED_BY(mMutex);
+
+ static const int MAX_BUFFERS = 3;
struct BufferInfo {
sp<GraphicBuffer> buffer;
int fence;
};
-
- int mDequeuedBuffers = 0;
- int mWidth;
- int mHeight;
+ std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
+ bool mAcquired GUARDED_BY(mMutex);
- BufferItem mLastSubmittedBufferItem;
- BufferItem mNextCallbackBufferItem;
- sp<Fence> mLastFence;
+ int mWidth GUARDED_BY(mMutex);
+ int mHeight GUARDED_BY(mMutex);
- std::condition_variable mDequeueWaitCV;
+ BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
+ BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
+ sp<Fence> mLastFence GUARDED_BY(mMutex);
sp<IGraphicBufferConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
sp<BufferItemConsumer> mBufferItemConsumer;
- SurfaceComposerClient::Transaction* mNextTransaction = nullptr;
+ SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..f0b7ff5
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+ explicit DisplayEventDispatcher(
+ const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ status_t initialize();
+ void dispose();
+ status_t scheduleVsync();
+
+protected:
+ virtual ~DisplayEventDispatcher() = default;
+
+private:
+ sp<Looper> mLooper;
+ DisplayEventReceiver mReceiver;
+ bool mWaitingForVsync;
+
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) = 0;
+ virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) = 0;
+
+ virtual int handleEvent(int receiveFd, int events, void* data);
+ bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 514dfe2..345425d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -401,6 +401,19 @@
virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
std::vector<int32_t>* outAllowedConfigs) = 0;
/*
+ * Sets the refresh rate boundaries for display configuration.
+ * For all other parameters, default configuration is used. The index for the default is
+ * corresponding to the configs returned from getDisplayConfigs().
+ */
+ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate) = 0;
+
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) = 0;
+ /*
* Gets whether brightness operations are supported on a display.
*
* displayToken
@@ -512,6 +525,8 @@
REMOVE_REGION_SAMPLING_LISTENER,
SET_ALLOWED_DISPLAY_CONFIGS,
GET_ALLOWED_DISPLAY_CONFIGS,
+ SET_DESIRED_DISPLAY_CONFIG_SPECS,
+ GET_DESIRED_DISPLAY_CONFIG_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d218356..2c0b143 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -129,6 +129,22 @@
static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
std::vector<int32_t>* outAllowedConfigs);
+ // Sets the refresh rate boundaries for display configuration.
+ // For all other parameters, default configuration is used. The index for the default is
+ // corresponting to the configs returned from getDisplayConfigs().
+ static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate);
+ // Gets the refresh rate boundaries for display configuration.
+ // For all other parameters, default configuration is used. The index for the default is
+ // corresponting to the configs returned from getDisplayConfigs().
+ // The reason is passed in for telemetry tracking, and it corresponds to the list of all
+ // the policy rules that were used.
+ static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate);
+
// Gets the list of supported color modes for the given display
static status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index ff22913..6ecdae5 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -19,6 +19,8 @@
#include <gui/BLASTBufferQueue.h>
#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
#include <gui/SurfaceComposerClient.h>
@@ -65,9 +67,11 @@
return mBlastBufferQueueAdapter->mSurfaceControl;
}
- void waitForCallback() {
+ void waitForCallbacks() {
std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
- mBlastBufferQueueAdapter->mDequeueWaitCV.wait_for(lock, 1s);
+ while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+ mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
+ }
}
private:
@@ -116,6 +120,17 @@
.apply();
}
+ void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+ auto igbProducer = adapter.getIGraphicBufferProducer();
+ ASSERT_NE(nullptr, igbProducer.get());
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ ASSERT_EQ(NO_ERROR,
+ igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+ &qbOutput));
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+ producer = igbProducer;
+ }
+
void fillBuffer(uint32_t* bufData, uint32_t width, uint32_t height, uint32_t stride, uint8_t r,
uint8_t g, uint8_t b) {
for (uint32_t row = 0; row < height; row++) {
@@ -195,14 +210,8 @@
uint8_t b = 0;
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- auto igbProducer = adapter.getIGraphicBufferProducer();
- ASSERT_NE(nullptr, igbProducer.get());
- IGraphicBufferProducer::QueueBufferOutput qbOutput;
- ASSERT_EQ(NO_ERROR,
- igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbOutput));
- ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3));
- ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
int slot;
sp<Fence> fence;
@@ -219,6 +228,7 @@
fillBuffer(bufData, buf->getWidth(), buf->getHeight(), buf->getStride(), r, g, b);
buf->unlock();
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
Rect(mDisplayWidth, mDisplayHeight),
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
@@ -226,7 +236,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallback();
+ sleep(1);
// capture screen and verify that it is red
bool capturedSecureLayers;
@@ -237,4 +247,43 @@
/*useIdentityTransform*/ false));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b));
}
+
+TEST_F(BLASTBufferQueueTest, TripleBuffering) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ std::vector<std::pair<int, sp<Fence>>> allocated;
+ for (int i = 0; i < 3; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+ allocated.push_back({slot, fence});
+ }
+ for (int i = 0; i < allocated.size(); i++) {
+ igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
+ }
+
+ for (int i = 0; i < 10; i++) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(NO_ERROR, ret);
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ }
+ adapter.waitForCallbacks();
+}
} // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index aef7aed..103f775 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -543,7 +543,6 @@
// Should now be able to dequeue up to minBuffers times
DequeueBufferResult result;
for (int i = 0; i < minBuffers; ++i) {
-
EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
TEST_PRODUCER_USAGE_BITS, &result)))
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c4f35ae..3d90369 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -831,6 +831,17 @@
std::vector<int32_t>* /*outAllowedConfigs*/) override {
return NO_ERROR;
}
+ status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t /*defaultModeId*/, float /*minRefreshRate*/,
+ float /*maxRefreshRate*/) override {
+ return NO_ERROR;
+ }
+ status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t* /*outDefaultModeId*/,
+ float* /*outMinRefreshRate*/,
+ float* /*outMaxRefreshRate*/) override {
+ return NO_ERROR;
+ };
status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 34b305e..8ccbc7f 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
#ifdef __ANDROID__
@@ -41,6 +42,21 @@
// --- InputEvent ---
+const char* inputEventTypeToString(int32_t type) {
+ switch (type) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ return "KEY";
+ }
+ case AINPUT_EVENT_TYPE_MOTION: {
+ return "MOTION";
+ }
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ return "FOCUS";
+ }
+ }
+ return "UNKNOWN";
+}
+
void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
mDeviceId = deviceId;
mSource = source;
@@ -587,6 +603,20 @@
return getAxisByLabel(label);
}
+// --- FocusEvent ---
+
+void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
+ InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE);
+ mHasFocus = hasFocus;
+ mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+ InputEvent::initialize(from);
+ mHasFocus = from.mHasFocus;
+ mInTouchMode = from.mInTouchMode;
+}
// --- PooledInputEventFactory ---
@@ -595,43 +625,52 @@
}
PooledInputEventFactory::~PooledInputEventFactory() {
- for (size_t i = 0; i < mKeyEventPool.size(); i++) {
- delete mKeyEventPool.itemAt(i);
- }
- for (size_t i = 0; i < mMotionEventPool.size(); i++) {
- delete mMotionEventPool.itemAt(i);
- }
}
KeyEvent* PooledInputEventFactory::createKeyEvent() {
- if (!mKeyEventPool.isEmpty()) {
- KeyEvent* event = mKeyEventPool.top();
- mKeyEventPool.pop();
- return event;
+ if (mKeyEventPool.empty()) {
+ return new KeyEvent();
}
- return new KeyEvent();
+ KeyEvent* event = mKeyEventPool.front().release();
+ mKeyEventPool.pop();
+ return event;
}
MotionEvent* PooledInputEventFactory::createMotionEvent() {
- if (!mMotionEventPool.isEmpty()) {
- MotionEvent* event = mMotionEventPool.top();
- mMotionEventPool.pop();
- return event;
+ if (mMotionEventPool.empty()) {
+ return new MotionEvent();
}
- return new MotionEvent();
+ MotionEvent* event = mMotionEventPool.front().release();
+ mMotionEventPool.pop();
+ return event;
+}
+
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+ if (mFocusEventPool.empty()) {
+ return new FocusEvent();
+ }
+ FocusEvent* event = mFocusEventPool.front().release();
+ mFocusEventPool.pop();
+ return event;
}
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
if (mKeyEventPool.size() < mMaxPoolSize) {
- mKeyEventPool.push(static_cast<KeyEvent*>(event));
+ mKeyEventPool.push(std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event)));
return;
}
break;
case AINPUT_EVENT_TYPE_MOTION:
if (mMotionEventPool.size() < mMaxPoolSize) {
- mMotionEventPool.push(static_cast<MotionEvent*>(event));
+ mMotionEventPool.push(std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event)));
+ return;
+ }
+ break;
+ case AINPUT_EVENT_TYPE_FOCUS:
+ if (mFocusEventPool.size() < mMaxPoolSize) {
+ mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
return;
}
break;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index a5dd3c0..200e1f3 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -14,7 +14,7 @@
static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
// Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
+static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
// Log debug messages about touch event resampling
#define DEBUG_RESAMPLING 0
@@ -88,6 +88,10 @@
return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
}
+inline static const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
@@ -99,6 +103,8 @@
return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
case Type::FINISHED:
return true;
+ case Type::FOCUS:
+ return true;
}
}
return false;
@@ -112,6 +118,8 @@
return sizeof(Header) + body.motion.size();
case Type::FINISHED:
return sizeof(Header) + body.finished.size();
+ case Type::FOCUS:
+ return sizeof(Header) + body.focus.size();
}
return sizeof(Header);
}
@@ -216,8 +224,10 @@
msg->body.finished.handled = body.finished.handled;
break;
}
- default: {
- LOG_FATAL("Unexpected message type %i", header.type);
+ case InputMessage::Type::FOCUS: {
+ msg->body.focus.seq = body.focus.seq;
+ msg->body.focus.hasFocus = body.focus.hasFocus;
+ msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
}
@@ -432,14 +442,13 @@
mChannel->getName().c_str(), keyCode);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), seq,
- deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
- downTime, eventTime);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
+ mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime, eventTime);
+ }
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -476,18 +485,18 @@
mChannel->getName().c_str(), action);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
- "displayId=%" PRId32 ", "
- "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
- "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
- mChannel->getName().c_str(), seq,
- deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
- buttonState, motionClassificationToString(classification),
- xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "displayId=%" PRId32 ", "
+ "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
+ "pointerCount=%" PRIu32,
+ mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
+ flags, edgeFlags, metaState, buttonState,
+ motionClassificationToString(classification), xOffset, yOffset, xPrecision,
+ yPrecision, downTime, eventTime, pointerCount);
+ }
if (!seq) {
ALOGE("Attempted to publish a motion event with sequence number 0.");
@@ -530,11 +539,27 @@
return mChannel->sendMessage(&msg);
}
+status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+ mChannel->getName().c_str(), toString(hasFocus),
+ toString(inTouchMode));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FOCUS;
+ msg.body.focus.seq = seq;
+ msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+ msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+ return mChannel->sendMessage(&msg);
+}
+
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
- mChannel->getName().c_str());
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+ }
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
@@ -549,7 +574,7 @@
return UNKNOWN_ERROR;
}
*outSeq = msg.body.finished.seq;
- *outHandled = msg.body.finished.handled;
+ *outHandled = msg.body.finished.handled == 1;
return OK;
}
@@ -567,12 +592,12 @@
return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
}
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
-#endif
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
+ }
*outSeq = 0;
*outEvent = nullptr;
@@ -592,10 +617,10 @@
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
}
@@ -611,10 +636,10 @@
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
@@ -624,10 +649,10 @@
Batch& batch = mBatches.editItemAt(batchIndex);
if (canAddSample(batch, &mMsg)) {
batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ }
break;
} else if (isPointerEvent(mMsg.body.motion.source) &&
mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
@@ -649,47 +674,58 @@
if (result) {
return result;
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
}
- // Start a new batch if needed.
- if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
- || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
-#endif
+ // Start a new batch if needed.
+ if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ mBatches.push();
+ Batch& batch = mBatches.editTop();
+ batch.samples.push(mMsg);
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
+ }
+ break;
+ }
+
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
+
+ updateTouchState(mMsg);
+ initializeMotionEvent(motionEvent, &mMsg);
+ *outSeq = mMsg.body.motion.seq;
+ *outEvent = motionEvent;
+
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
-
- updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
- *outEvent = motionEvent;
-
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
- break;
+ case InputMessage::Type::FINISHED: {
+ LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
+ "InputConsumer!");
+ break;
}
- default:
- ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
- mChannel->getName().c_str(), mMsg.header.type);
- return UNKNOWN_ERROR;
+ case InputMessage::Type::FOCUS: {
+ FocusEvent* focusEvent = factory->createFocusEvent();
+ if (!focusEvent) return NO_MEMORY;
+
+ initializeFocusEvent(focusEvent, &mMsg);
+ *outSeq = mMsg.body.focus.seq;
+ *outEvent = focusEvent;
+ break;
+ }
}
}
return OK;
@@ -1014,10 +1050,10 @@
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, handled ? "true" : "false");
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
+ }
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1066,7 +1102,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
msg.body.finished.seq = seq;
- msg.body.finished.handled = handled;
+ msg.body.finished.handled = handled ? 1 : 0;
return mChannel->sendMessage(&msg);
}
@@ -1114,6 +1150,10 @@
msg->body.key.eventTime);
}
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1);
+}
+
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerProperties pointerProperties[pointerCount];
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index a362f32..2fc77e9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -60,6 +60,7 @@
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
+ void PublishAndConsumeFocusEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -256,6 +257,43 @@
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ constexpr bool hasFocus = true;
+ constexpr bool inTouchMode = true;
+
+ status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+ << "consumer should have returned a focus event";
+
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ uint32_t finishedSeq = 0;
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+ ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, finishedSeq)
+ << "publisher receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
@@ -264,6 +302,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -322,6 +364,7 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8d8cf06..9ab0dba 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -69,8 +69,29 @@
CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+ CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+
CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
+void TestHeaderSize() {
+ static_assert(sizeof(InputMessage::Header) == 8);
+}
+
+/**
+ * We cannot use the Body::size() method here because it is not static for
+ * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+ */
+void TestBodySize() {
+ static_assert(sizeof(InputMessage::Body::Key) == 64);
+ static_assert(sizeof(InputMessage::Body::Motion) ==
+ offsetof(InputMessage::Body::Motion, pointers) +
+ sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+ static_assert(sizeof(InputMessage::Body::Finished) == 8);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
+}
+
} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 63e0734..05ff93e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -22,7 +22,7 @@
#include <thread>
#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/DisplayEventDispatcher.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <utils/Looper.h>
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 6665635..6566538 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -18,6 +18,7 @@
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
#include <algorithm>
#include <optional>
@@ -82,6 +83,16 @@
ADisplayType type;
/**
+ * The preferred WCG dataspace
+ */
+ ADataSpace wcgDataspace;
+
+ /**
+ * The preferred WCG pixel format
+ */
+ AHardwareBuffer_Format wcgPixelFormat;
+
+ /**
* Number of supported configs
*/
size_t numConfigs;
@@ -151,6 +162,17 @@
const std::optional<PhysicalDisplayId> internalId =
SurfaceComposerClient::getInternalDisplayId();
+ ui::Dataspace defaultDataspace;
+ ui::PixelFormat defaultPixelFormat;
+ ui::Dataspace wcgDataspace;
+ ui::PixelFormat wcgPixelFormat;
+
+ const status_t status =
+ SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+ &wcgDataspace, &wcgPixelFormat);
+ if (status != NO_ERROR) {
+ return status;
+ }
// Here we allocate all our required memory in one block. The layout is as
// follows:
@@ -176,7 +198,12 @@
const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
- displayData[i] = DisplayImpl{id, type, configs.size(), configData};
+ displayData[i] = DisplayImpl{id,
+ type,
+ static_cast<ADataSpace>(wcgDataspace),
+ static_cast<AHardwareBuffer_Format>(wcgPixelFormat),
+ configs.size(),
+ configData};
impls[i] = displayData + i;
// Advance the configData pointer so that future configs are written to
// the correct display.
@@ -210,6 +237,17 @@
return reinterpret_cast<DisplayImpl*>(display)->type;
}
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat) {
+ CHECK_NOT_NULL(display);
+ CHECK_NOT_NULL(outDataspace);
+ CHECK_NOT_NULL(outPixelFormat);
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ *outDataspace = impl->wcgDataspace;
+ *outPixelFormat = impl->wcgPixelFormat;
+}
+
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 45b935a..7a497ea 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -46,9 +46,9 @@
],
shared_libs: [
- "libandroidfw",
"libgui",
"liblog",
+ "libnativewindow",
"libui",
"libutils",
],
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index 7af452a..9be401e 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -16,6 +16,8 @@
#pragma once
+#include <android/data_space.h>
+#include <android/hardware_buffer.h>
#include <inttypes.h>
__BEGIN_DECLS
@@ -72,6 +74,12 @@
ADisplayType ADisplay_getDisplayType(ADisplay* display);
/**
+ * Queries the display's preferred WCG format
+ */
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat);
+
+/**
* Gets the current display configuration for the given display.
*
* Memory is *not* allocated for the caller. As such, the returned output
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 15d025b..394d05a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -983,6 +983,8 @@
Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
for (auto layer : layers) {
+ mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
+ mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
const FloatRect bounds = layer.geometry.boundaries;
@@ -998,7 +1000,6 @@
bool usePremultipliedAlpha = true;
bool disableTexture = true;
bool isOpaque = false;
-
if (layer.source.buffer.buffer != nullptr) {
disableTexture = false;
isOpaque = layer.source.buffer.isOpaque;
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index fe9d909..4eb5eb6 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -65,6 +65,8 @@
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+ mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+ mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
@@ -138,6 +140,12 @@
if (mDisplayMaxLuminanceLoc >= 0) {
glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
}
+ if (mMaxMasteringLuminanceLoc >= 0) {
+ glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+ }
+ if (mMaxContentLuminanceLoc >= 0) {
+ glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+ }
if (mCornerRadiusLoc >= 0) {
glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
}
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index bc9cf08..c9beb68 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -90,6 +90,10 @@
/* location of display luminance uniform */
GLint mDisplayMaxLuminanceLoc;
+ /* location of max mastering luminance uniform */
+ GLint mMaxMasteringLuminanceLoc;
+ /* location of max content luminance uniform */
+ GLint mMaxContentLuminanceLoc;
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 494623e..e2757e1 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -339,9 +339,9 @@
default:
fs << R"__SHADER__(
highp vec3 ToneMap(highp vec3 color) {
- const float maxMasteringLumi = 1000.0;
- const float maxContentLumi = 1000.0;
- const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+ float maxMasteringLumi = maxMasteringLuminance;
+ float maxContentLumi = maxContentLuminance;
+ float maxInLumi = min(maxMasteringLumi, maxContentLumi);
float maxOutLumi = displayMaxLuminance;
float nits = color.y;
@@ -633,9 +633,10 @@
}
if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
- // Currently, display maximum luminance is needed when doing tone mapping.
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
+ fs << "uniform float maxMasteringLuminance;";
+ fs << "uniform float maxContentLuminance;";
}
if (needs.hasInputTransformMatrix()) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b851d5c..d890ccd 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -60,6 +60,8 @@
// HDR color-space setting for Y410.
bool isY410BT2020 = false;
+ float maxMasteringLuminance = 0.0;
+ float maxContentLuminance = 0.0;
};
// Metadata describing the layer geometry.
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bd2055f..bad64c2 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -71,6 +71,8 @@
TransferFunction outputTransferFunction = TransferFunction::LINEAR;
float displayMaxLuminance;
+ float maxMasteringLuminance;
+ float maxContentLuminance;
// projection matrix
mat4 projectionMatrix;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index dc105c0..73945cf 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -32,7 +32,6 @@
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
-using android::hardware::graphics::mapper::V4_0::YCbCrLayout;
namespace android {
@@ -190,6 +189,16 @@
status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
int acquireFence, void** outData, int32_t* outBytesPerPixel,
int32_t* outBytesPerStride) const {
+ // In Gralloc 4 we can get this info per plane. Clients should check per plane.
+ if (outBytesPerPixel) {
+ // TODO add support to check per plane
+ *outBytesPerPixel = -1;
+ }
+ if (outBytesPerStride) {
+ // TODO add support to check per plane
+ *outBytesPerStride = -1;
+ }
+
auto buffer = const_cast<native_handle_t*>(bufferHandle);
IMapper::Rect accessRegion = sGralloc4Rect(bounds);
@@ -205,19 +214,12 @@
Error error;
auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpData,
- const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
+ [&](const auto& tmpError, const auto& tmpData) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outData = tmpData;
- if (outBytesPerPixel) {
- *outBytesPerPixel = tmpBytesPerPixel;
- }
- if (outBytesPerStride) {
- *outBytesPerStride = tmpBytesPerStride;
- }
});
// we own acquireFence even on errors
@@ -232,48 +234,11 @@
return static_cast<status_t>(error);
}
-status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
- int acquireFence, android_ycbcr* ycbcr) const {
- auto buffer = const_cast<native_handle_t*>(bufferHandle);
-
- IMapper::Rect accessRegion = sGralloc4Rect(bounds);
-
- // put acquireFence in a hidl_handle
- hardware::hidl_handle acquireFenceHandle;
- NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
- if (acquireFence >= 0) {
- auto h = native_handle_init(acquireFenceStorage, 1, 0);
- h->data[0] = acquireFence;
- acquireFenceHandle = h;
- }
-
- YCbCrLayout layout;
- Error error;
- auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpLayout) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- layout = tmpLayout;
- });
-
- if (error == Error::NONE) {
- ycbcr->y = layout.y;
- ycbcr->cb = layout.cb;
- ycbcr->cr = layout.cr;
- ycbcr->ystride = static_cast<size_t>(layout.yStride);
- ycbcr->cstride = static_cast<size_t>(layout.cStride);
- ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
- }
-
- // we own acquireFence even on errors
- if (acquireFence >= 0) {
- close(acquireFence);
- }
-
- return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+status_t Gralloc4Mapper::lock(buffer_handle_t /*bufferHandle*/, uint64_t /*usage*/,
+ const Rect& /*bounds*/, int /*acquireFence*/,
+ android_ycbcr* /*ycbcr*/) const {
+ // TODO add lockYCbCr support
+ return static_cast<status_t>(Error::UNSUPPORTED);
}
int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index fcc2547..efe0931 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -111,11 +111,10 @@
ALOGD("%s", s.c_str());
}
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage,
- buffer_handle_t* handle, uint32_t* stride,
- uint64_t /*graphicBufferId*/, std::string requestorName)
-{
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName) {
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -175,6 +174,13 @@
}
}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ uint64_t /*graphicBufferId*/, std::string requestorName) {
+ return allocate(width, height, format, layerCount, usage, handle, stride, requestorName);
+}
+
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
ATRACE_CALL();
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 28c3f7b..85abb38 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -33,8 +33,8 @@
: mMatrix(other.mMatrix), mType(other.mType) {
}
-Transform::Transform(uint32_t orientation) {
- set(orientation, 0, 0);
+Transform::Transform(uint32_t orientation, int w, int h) {
+ set(orientation, w, h);
}
Transform::~Transform() = default;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 324d9e1..9f6159a 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -42,11 +42,16 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ // DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
std::string requestorName);
+ status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
status_t free(buffer_handle_t handle);
size_t getTotalSize() const;
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index c401a48..83fb144 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -23,6 +23,7 @@
#include <memory>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <utils/Singleton.h>
@@ -36,7 +37,6 @@
// ---------------------------------------------------------------------------
class GrallocMapper;
-class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
{
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index f29a370..fab2d9e 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -38,7 +38,7 @@
public:
Transform();
Transform(const Transform& other);
- explicit Transform(uint32_t orientation);
+ explicit Transform(uint32_t orientation, int w = 0, int h = 0);
~Transform();
enum orientation_flags {
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 7b8e0f8..baba64f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -1,20 +1,6 @@
-filegroup {
- name: "gpuservice_sources",
- srcs: [
- "GpuService.cpp",
- "gpustats/GpuStats.cpp"
- ],
-}
-
-filegroup {
- name: "gpuservice_binary_sources",
- srcs: ["main_gpuservice.cpp"],
-}
-
cc_defaults {
name: "gpuservice_defaults",
cflags: [
- "-DLOG_TAG=\"GpuService\"",
"-Wall",
"-Werror",
"-Wformat",
@@ -22,12 +8,13 @@
"-Wunused",
"-Wunreachable-code",
],
- srcs: [
- ":gpuservice_sources",
- ],
- include_dirs: [
- "frameworks/native/vulkan/vkjson",
- "frameworks/native/vulkan/include",
+}
+
+cc_defaults {
+ name: "libgpuservice_defaults",
+ defaults: ["gpuservice_defaults"],
+ cflags: [
+ "-DLOG_TAG=\"GpuService\"",
],
shared_libs: [
"libbase",
@@ -36,17 +23,22 @@
"libgraphicsenv",
"liblog",
"libutils",
- "libvulkan",
+ "libvkjson",
],
static_libs: [
"libserviceutils",
- "libvkjson",
+ ],
+ export_static_lib_headers: [
+ "libserviceutils",
+ ],
+ export_shared_lib_headers: [
+ "libgraphicsenv",
],
}
cc_defaults {
- name: "gpuservice_production_defaults",
- defaults: ["gpuservice_defaults"],
+ name: "libgpuservice_production_defaults",
+ defaults: ["libgpuservice_defaults"],
cflags: [
"-fvisibility=hidden",
"-fwhole-program-vtables", // requires ThinLTO
@@ -56,8 +48,24 @@
},
}
+filegroup {
+ name: "libgpuservice_sources",
+ srcs: [
+ "GpuService.cpp",
+ "gpustats/GpuStats.cpp"
+ ],
+}
+
+cc_library_shared {
+ name: "libgpuservice",
+ defaults: ["libgpuservice_production_defaults"],
+ srcs: [
+ ":libgpuservice_sources",
+ ],
+}
+
cc_defaults {
- name: "gpuservice_binary",
+ name: "libgpuservice_binary",
defaults: ["gpuservice_defaults"],
shared_libs: [
"libbinder",
@@ -68,9 +76,17 @@
ldflags: ["-Wl,--export-dynamic"],
}
+filegroup {
+ name: "gpuservice_binary_sources",
+ srcs: ["main_gpuservice.cpp"],
+}
+
cc_binary {
name: "gpuservice",
- defaults: ["gpuservice_binary"],
+ defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
srcs: [":gpuservice_binary_sources"],
+ shared_libs: [
+ "libgpuservice",
+ ],
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c219941..b9bec44 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -252,6 +252,10 @@
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
+ // mInTouchMode will be initialized by the WindowManager to the default device config.
+ // To avoid leaking stack in case that call never comes, and for tests,
+ // initialize it here anyways.
+ mInTouchMode(true),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
@@ -294,6 +298,12 @@
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
+
+ // We are about to enter an infinitely long sleep, because we have no commands or
+ // pending or queued events
+ if (nextWakeupTime == LONG_LONG_MAX) {
+ mDispatcherEnteredIdle.notify_all();
+ }
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
@@ -2295,10 +2305,12 @@
reportTouchEventForStatistics(*motionEntry);
break;
}
-
- default:
- ALOG_ASSERT(false);
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
+ EventEntry::typeToString(eventEntry->type));
return;
+ }
}
// Check the result.
@@ -3030,7 +3042,7 @@
}
default:
- ALOGW("Cannot inject event of type %d", event->getType());
+ ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -3536,6 +3548,11 @@
mLooper->wake();
}
+void InputDispatcher::setInTouchMode(bool inTouchMode) {
+ std::scoped_lock lock(mLock);
+ mInTouchMode = inTouchMode;
+}
+
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
@@ -4571,4 +4588,22 @@
mDispatcherIsAlive.wait(_l);
}
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+ /**
+ * Timeout should represent the longest possible time that a device might spend processing
+ * events and commands.
+ */
+ constexpr std::chrono::duration TIMEOUT = 100ms;
+ std::unique_lock lock(mLock);
+ mLooper->wake();
+ std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+ return result == std::cv_status::no_timeout;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d21b0a1..38f8674 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,6 +81,7 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
+ virtual bool waitForIdle() override;
virtual void dispatchOnce() override;
@@ -102,6 +103,7 @@
virtual void setFocusedDisplay(int32_t displayId) override;
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
+ virtual void setInTouchMode(bool inTouchMode) override;
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
@@ -128,6 +130,7 @@
std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
+ std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
@@ -247,6 +250,7 @@
bool mDispatchEnabled GUARDED_BY(mLock);
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
+ bool mInTouchMode GUARDED_BY(mLock);
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5acf92b..2e9bca2 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -93,21 +93,22 @@
sp<InputChannel> inputChannel;
// Flags for the input target.
- int32_t flags;
+ int32_t flags = 0;
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float xOffset, yOffset;
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float globalScaleFactor;
+ float globalScaleFactor = 1.0f;
float windowXScale = 1.0f;
float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
- BitSet32 pointerIds;
+ BitSet32 pointerIds{};
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index ce7366f..db9fba6 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,6 +63,14 @@
/* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
virtual void monitor() = 0;
+ /**
+ * Wait until dispatcher is idle. That means, there are no further events to be processed,
+ * and all of the policy callbacks have been completed.
+ * Return true if the dispatcher is idle.
+ * Return false if the timeout waiting for the dispatcher to become idle has expired.
+ */
+ virtual bool waitForIdle() = 0;
+
/* Runs a single iteration of the dispatch loop.
* Nominally processes one queued event, a timeout, or a response from an input consumer.
*
@@ -116,6 +124,14 @@
*/
virtual void setInputFilterEnabled(bool enabled) = 0;
+ /**
+ * Set the touch mode state.
+ * Touch mode is a global state that apps may enter / exit based on specific
+ * user interactions with input devices.
+ * If true, the device is in touch mode.
+ */
+ virtual void setInTouchMode(bool inTouchMode) = 0;
+
/* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3c16070..23c08b2 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -58,7 +58,6 @@
"liblog",
"libui",
"libutils",
- "libhardware_legacy",
],
header_libs: [
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index c8da0ab..264d287 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,8 +37,6 @@
#include "EventHub.h"
-#include <hardware_legacy/power.h>
-
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <openssl/sha.h>
@@ -71,7 +69,6 @@
static constexpr bool DEBUG = false;
-static const char* WAKE_LOCK_ID = "KeyEvents";
static const char* DEVICE_PATH = "/dev/input";
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
@@ -296,7 +293,6 @@
mPendingEventIndex(0),
mPendingINotify(false) {
ensureProcessCanBlockSuspend();
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
@@ -354,8 +350,6 @@
::close(mINotifyFd);
::close(mWakeReadPipeFd);
::close(mWakeWritePipeFd);
-
- release_wake_lock(WAKE_LOCK_ID);
}
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
@@ -1046,26 +1040,24 @@
break;
}
- // Poll for events. Mind the wake lock dance!
- // We hold a wake lock at all times except during epoll_wait(). This works due to some
- // subtle choreography. When a device driver has pending (unread) events, it acquires
- // a kernel wake lock. However, once the last pending event has been read, the device
- // driver will release the kernel wake lock. To prevent the system from going to sleep
- // when this happens, the EventHub holds onto its own user wake lock while the client
- // is processing events. Thus the system can only sleep if there are no events
- // pending or currently being processed.
+ // Poll for events.
+ // When a device driver has pending (unread) events, it acquires
+ // a kernel wake lock. Once the last pending event has been read, the device
+ // driver will release the kernel wake lock, but the epoll will hold the wakelock,
+ // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
+ // is called again for the same fd that produced the event.
+ // Thus the system can only sleep if there are no events pending or
+ // currently being processed.
//
// The timeout is advisory only. If the device is asleep, it will not wake just to
// service the timeout.
mPendingEventIndex = 0;
- mLock.unlock(); // release lock before poll, must be before release_wake_lock
- release_wake_lock(WAKE_LOCK_ID);
+ mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
+ mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// Timed out.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8863ec2..c8d39cf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,6 +19,7 @@
#include <InputDispatcherThread.h>
#include <binder/Binder.h>
+#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
@@ -50,49 +51,55 @@
public:
FakeInputDispatcherPolicy() {
- mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY);
-
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
- ASSERT_EQ(keyEvent.getEventTime(), args.eventTime);
- ASSERT_EQ(keyEvent.getAction(), args.action);
- ASSERT_EQ(keyEvent.getDisplayId(), args.displayId);
-
- reset();
+ assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
+ args.displayId);
}
void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
- ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION);
-
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
- ASSERT_EQ(motionEvent.getEventTime(), args.eventTime);
- ASSERT_EQ(motionEvent.getAction(), args.action);
- ASSERT_EQ(motionEvent.getDisplayId(), args.displayId);
-
- reset();
+ assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
+ args.displayId);
}
- void assertFilterInputEventWasNotCalled() {
- ASSERT_EQ(nullptr, mFilteredEvent)
- << "Expected filterInputEvent() to not have been called.";
+ void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+
+ void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ ASSERT_TRUE(mConfigurationChangedTime)
+ << "Timed out waiting for configuration changed call";
+ ASSERT_EQ(*mConfigurationChangedTime, when);
+ mConfigurationChangedTime = std::nullopt;
+ }
+
+ void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ ASSERT_TRUE(mLastNotifySwitch);
+ // We do not check sequenceNum because it is not exposed to the policy
+ EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+ EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+ EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+ EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+ mLastNotifySwitch = std::nullopt;
}
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
- ASSERT_EQ(mOnPointerDownToken, touchedToken)
- << "Expected token from onPointerDownOutsideFocus was not matched";
- reset();
+ ASSERT_EQ(touchedToken, mOnPointerDownToken);
+ mOnPointerDownToken.clear();
+ }
+
+ void assertOnPointerDownWasNotCalled() {
+ ASSERT_TRUE(mOnPointerDownToken == nullptr)
+ << "Expected onPointerDownOutsideFocus to not have been called";
}
private:
std::unique_ptr<InputEvent> mFilteredEvent;
+ std::optional<nsecs_t> mConfigurationChangedTime;
sp<IBinder> mOnPointerDownToken;
+ std::optional<NotifySwitchArgs> mLastNotifySwitch;
- virtual void notifyConfigurationChanged(nsecs_t) {
+ virtual void notifyConfigurationChanged(nsecs_t when) override {
+ mConfigurationChangedTime = when;
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -144,7 +151,13 @@
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+ virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
+ /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+ * essentially a passthrough for notifySwitch.
+ */
+ mLastNotifySwitch =
+ NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
}
virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -158,9 +171,26 @@
mOnPointerDownToken = newToken;
}
- void reset() {
+ void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
+ int32_t displayId) {
+ ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+ ASSERT_EQ(mFilteredEvent->getType(), type);
+
+ if (type == AINPUT_EVENT_TYPE_KEY) {
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+ EXPECT_EQ(keyEvent.getEventTime(), eventTime);
+ EXPECT_EQ(keyEvent.getAction(), action);
+ EXPECT_EQ(keyEvent.getDisplayId(), displayId);
+ } else if (type == AINPUT_EVENT_TYPE_MOTION) {
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+ EXPECT_EQ(motionEvent.getEventTime(), eventTime);
+ EXPECT_EQ(motionEvent.getAction(), action);
+ EXPECT_EQ(motionEvent.getDisplayId(), displayId);
+ } else {
+ FAIL() << "Unknown type: " << type;
+ }
+
mFilteredEvent = nullptr;
- mOnPointerDownToken.clear();
}
};
@@ -348,9 +378,30 @@
<< "Should reject motion events with duplicate pointer ids.";
}
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+ constexpr nsecs_t eventTime = 20;
+ NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+ mDispatcher->notifyConfigurationChanged(&args);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+
+ mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+ NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+ 1 /*switchValues*/, 2 /*switchMask*/);
+ mDispatcher->notifySwitch(&args);
+
+ // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+ args.policyFlags |= POLICY_FLAG_TRUSTED;
+ mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
// --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
class FakeApplicationHandle : public InputApplicationHandle {
public:
@@ -408,7 +459,8 @@
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << ": event type should match.";
+ << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -532,9 +584,7 @@
InputWindowHandle::releaseChannel();
}
protected:
- virtual bool handled() {
- return true;
- }
+ virtual bool handled() override { return true; }
bool mFocused;
Rect mFrame;
@@ -634,10 +684,7 @@
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(window);
-
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -654,11 +701,7 @@
sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
-
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -680,11 +723,8 @@
// Expect one focus window exist in display.
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -706,11 +746,8 @@
// Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
windowTop->setFocus();
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -732,12 +769,9 @@
windowTop->setFocus();
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
// Release channel for window is no longer valid.
windowTop->releaseChannel();
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
// Test inject a key down, should dispatch to a valid window.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -762,8 +796,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- std::vector<sp<InputWindowHandle>> inputWindowHandles{windowLeft, windowRight};
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT);
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
@@ -774,6 +807,51 @@
windowRight->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocus();
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Window should receive motion down event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -784,25 +862,22 @@
application1 = new FakeApplicationHandle();
windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowInPrimary);
+
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocus();
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
application2 = new FakeApplicationHandle();
windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
SECOND_DISPLAY_ID);
// Set focus to second display window.
- std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
- inputWindowHandles_Second.push_back(windowInSecondary);
// Set focus display to second one.
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocus();
- mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+ mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
}
virtual void TearDown() {
@@ -850,9 +925,8 @@
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- // Remove secondary display.
- std::vector<sp<InputWindowHandle>> noWindows;
- mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+ // Remove all windows in secondary display.
+ mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID);
// Expect old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
@@ -941,7 +1015,7 @@
motionArgs = generateMotionArgs(
AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
-
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
} else {
@@ -956,6 +1030,7 @@
mDispatcher->notifyKey(&keyArgs);
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
mDispatcher->notifyKey(&keyArgs);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
@@ -1012,34 +1087,31 @@
// window.
mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
- mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
- mWindowFocused->setFrame(Rect(50, 50, 100, 100));
- mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
- mWindowFocusedTouchPoint = 60;
+ mFocusedWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+ mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mFocusedWindowTouchPoint = 60;
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mWindowFocused->setFocus();
+ mFocusedWindow->setFocus();
// Expect one focus window exist in display.
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(mUnfocusedWindow);
- inputWindowHandles.push_back(mWindowFocused);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
}
virtual void TearDown() {
InputDispatcherTest::TearDown();
mUnfocusedWindow.clear();
- mWindowFocused.clear();
+ mFocusedWindow.clear();
}
protected:
sp<FakeWindowHandle> mUnfocusedWindow;
- sp<FakeWindowHandle> mWindowFocused;
- int32_t mWindowFocusedTouchPoint;
+ sp<FakeWindowHandle> mFocusedWindow;
+ int32_t mFocusedWindowTouchPoint;
};
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1049,9 +1121,8 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
}
@@ -1062,10 +1133,9 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1073,10 +1143,9 @@
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1084,14 +1153,13 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus,
OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
- mWindowFocusedTouchPoint))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
} // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 14ed73d..c2e1204 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1173,6 +1173,11 @@
return nullptr;
}
int fd = resource->data[0];
+ if (!ashmem_valid(fd)) {
+ ALOGE("Supplied Ashmem memory region is invalid");
+ return nullptr;
+ }
+
int size2 = ashmem_get_size_region(fd);
// check size consistency
if (size2 < static_cast<int64_t>(size)) {
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1cb0489..caf7f03 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -15,19 +15,20 @@
*/
#include <inttypes.h>
+#include <android/hardware_buffer.h>
#include <android/sensor.h>
#include <sensor/Sensor.h>
#include <sensor/SensorManager.h>
#include <sensor/SensorEventQueue.h>
#include <utils/Looper.h>
+#include <vndk/hardware_buffer.h>
using namespace android;
static nsecs_t sStartTime = 0;
-int receiver(__unused int fd, __unused int events, void* data)
-{
+int receiver(__unused int fd, __unused int events, void* data) {
sp<SensorEventQueue> q((SensorEventQueue*)data);
ssize_t n;
ASensorEvent buffer[8];
@@ -59,11 +60,42 @@
return 1;
}
+void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
+ AHardwareBuffer *hardwareBuffer;
+ char* buffer;
-int main()
-{
+ constexpr size_t kEventSize = sizeof(ASensorEvent);
+ constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
+ constexpr size_t kMemSize = kEventSize * kNEvent;
+ AHardwareBuffer_Desc desc = {
+ .width = static_cast<uint32_t>(kMemSize),
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA
+ | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ };
+
+ AHardwareBuffer_allocate(&desc, &hardwareBuffer);
+ AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+ -1, nullptr, reinterpret_cast<void **>(&buffer));
+
+ const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+
+ // Pass in AHardwareBuffer, but with the wrong DIRECT_CHANNEL_TYPE to see
+ // if anything in the Sensor framework crashes
+ int ret = mgr.createDirectChannel(
+ kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
+
+ // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+ printf("createInvalidDirectChannel=%d\n", ret);
+}
+
+int main() {
SensorManager& mgr = SensorManager::getInstanceForPackage(String16("Sensor Service Test"));
+ testInvalidSharedMem_NoCrash(mgr);
+
Sensor const* const* list;
ssize_t count = mgr.getSensorList(&list);
printf("numSensors=%d\n", int(count));
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 963d755..d476f7b4 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -167,12 +167,14 @@
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/PhaseOffsets.cpp",
+ "Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
"Scheduler/VSyncModulator.cpp",
+ "Scheduler/VSyncReactor.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 94c4a81..054acc5 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -56,6 +56,9 @@
namespace android {
+static constexpr float defaultMaxMasteringLuminance = 1000.0;
+static constexpr float defaultMaxContentLuminance = 1000.0;
+
BufferLayer::BufferLayer(const LayerCreationArgs& args)
: Layer(args),
mTextureName(args.textureName),
@@ -184,6 +187,14 @@
layer.source.buffer.textureName = mTextureName;
layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
layer.source.buffer.isY410BT2020 = isHdrY410();
+ bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ layer.source.buffer.maxMasteringLuminance = hasSmpte2086
+ ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
+ : defaultMaxMasteringLuminance;
+ layer.source.buffer.maxContentLuminance = hasCta861_3
+ ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
+ : defaultMaxContentLuminance;
// TODO: we could be more subtle with isFixedSize()
const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 4d71d43..3a4df74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -21,8 +21,10 @@
#include <compositionengine/DisplaySurface.h>
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/RenderSurface.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/NativeWindow.h>
@@ -39,6 +41,8 @@
using testing::_;
using testing::DoAll;
+using testing::InSequence;
+using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
@@ -46,6 +50,8 @@
using testing::StrictMock;
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
struct DisplayTest : public testing::Test {
class Display : public impl::Display {
@@ -768,5 +774,59 @@
nonHwcDisplay->finishFrame(refreshArgs);
}
+/*
+ * Display functional tests
+ */
+
+struct DisplayFunctionalTest : public testing::Test {
+ class Display : public impl::Display {
+ public:
+ explicit Display(const compositionengine::DisplayCreationArgs& args)
+ : impl::Display(args) {}
+
+ using impl::Display::injectOutputLayerForTest;
+ virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+ };
+
+ static std::shared_ptr<Display> createDisplay(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::DisplayCreationArgs&& args) {
+ return impl::createDisplayTemplated<Display>(compositionEngine, args);
+ }
+
+ DisplayFunctionalTest() {
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ NiceMock<android::mock::HWComposer> mHwComposer;
+ NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ NiceMock<mock::CompositionEngine> mCompositionEngine;
+ sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
+ sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+ std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setDisplayId(DEFAULT_DISPLAY_ID)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+ impl::RenderSurface* mRenderSurface =
+ new impl::RenderSurface{mCompositionEngine, *mDisplay,
+ RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+ mDisplaySurface}};
+};
+
+TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+ InSequence seq;
+
+ mDisplay->editState().isEnabled = true;
+
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+ EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
+
+ mDisplay->postFramebuffer();
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index f0a29f0..9e79062 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -753,8 +753,8 @@
struct OutputPrepareFrameTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by prepareFrame to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD0(chooseCompositionStrategy, void());
};
@@ -803,14 +803,246 @@
}
/*
+ * Output::prepare()
+ */
+
+struct OutputPrepareTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(rebuildLayerStacks,
+ void(const compositionengine::CompositionRefreshArgs&,
+ compositionengine::LayerFESet&));
+ };
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ compositionengine::LayerFESet mGeomSnapshots;
+};
+
+TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+ InSequence seq;
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+/*
+ * Output::rebuildLayerStacks()
+ */
+
+struct OutputRebuildLayerStacksTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(collectVisibleLayers,
+ void(const compositionengine::CompositionRefreshArgs&,
+ compositionengine::Output::CoverageState&));
+ };
+
+ OutputRebuildLayerStacksTest() {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.transform = kIdentityTransform;
+ mOutput.mState.bounds = kOutputBounds;
+
+ mRefreshArgs.updatingOutputGeometryThisFrame = true;
+
+ mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10));
+
+ EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _))
+ .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues));
+ }
+
+ void setTestCoverageValues(const CompositionRefreshArgs&,
+ compositionengine::Output::CoverageState& state) {
+ state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet;
+ state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet;
+ state.dirtyRegion = mCoverageDirtyRegionToSet;
+ }
+
+ static const ui::Transform kIdentityTransform;
+ static const ui::Transform kRotate90Transform;
+ static const Rect kOutputBounds;
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ compositionengine::LayerFESet mGeomSnapshots;
+ Region mCoverageAboveCoveredLayersToSet;
+ Region mCoverageAboveOpaqueLayersToSet;
+ Region mCoverageDirtyRegionToSet;
+};
+
+const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080};
+const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080};
+const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080};
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) {
+ mRefreshArgs.updatingOutputGeometryThisFrame = false;
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) {
+ mOutput.mState.transform = kIdentityTransform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) {
+ mOutput.mState.transform = kIdentityTransform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) {
+ mOutput.mState.transform = kRotate90Transform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) {
+ mOutput.mState.transform = kRotate90Transform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) {
+ mOutput.mState.transform = kIdentityTransform;
+ mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080));
+
+ mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) {
+ mOutput.mState.transform = kRotate90Transform;
+ mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920));
+
+ mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920))));
+}
+
+/*
+ * Output::collectVisibleLayers()
+ */
+
+struct OutputCollectVisibleLayersTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(ensureOutputLayerIfVisible,
+ void(std::shared_ptr<compositionengine::Layer>,
+ compositionengine::Output::CoverageState&));
+ MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+ }
+
+ StrictMock<mock::OutputLayer> outputLayer;
+ std::shared_ptr<StrictMock<mock::Layer>> layer{new StrictMock<mock::Layer>()};
+ impl::OutputLayerCompositionState outputLayerState;
+ };
+
+ OutputCollectVisibleLayersTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+ .WillRepeatedly(Return(&mLayer3.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layer);
+ mRefreshArgs.layers.push_back(mLayer2.layer);
+ mRefreshArgs.layers.push_back(mLayer3.layer);
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ compositionengine::LayerFESet mGeomSnapshots;
+ compositionengine::Output::CoverageState mCoverageState{mGeomSnapshots};
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+};
+
+TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
+ mRefreshArgs.layers.clear();
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+
+ EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+ mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+}
+
+TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) {
+ // Enforce a call order sequence for this test.
+ InSequence seq;
+
+ // Layer coverage is evaluated from front to back!
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layer), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layer), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layer), Ref(mCoverageState)));
+
+ EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+ mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+
+ // Ensure all output layers have been assigned a simple/flattened z-order.
+ EXPECT_EQ(0u, mLayer1.outputLayerState.z);
+ EXPECT_EQ(1u, mLayer2.outputLayerState.z);
+ EXPECT_EQ(2u, mLayer3.outputLayerState.z);
+}
+
+/*
+ * Output::ensureOutputLayerIfVisible()
+ */
+
+// TODO(b/144060211) - Add coverage
+
+/*
* Output::present()
*/
struct OutputPresentTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // All child helper functions Output::present() are defined as mocks,
- // and those are tested separately, allowing the present() test to
- // just cover the high level flow.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(updateAndWriteCompositionState,
void(const compositionengine::CompositionRefreshArgs&));
@@ -849,9 +1081,8 @@
using TestType = OutputUpdateColorProfileTest;
struct OutputPartialMock : public OutputPartialMockBase {
- // All child helper functions Output::present() are defined as mocks,
- // and those are tested separately, allowing the present() test to
- // just cover the high level flow.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
};
@@ -1580,8 +1811,8 @@
using TestType = OutputBeginFrameTest;
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by begiNFrame to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
};
@@ -1733,8 +1964,8 @@
struct OutputDevOptRepaintFlashTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by composeSurfaces to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
MOCK_METHOD0(postFramebuffer, void());
@@ -1815,8 +2046,8 @@
struct OutputFinishFrameTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by composeSurfaces to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
MOCK_METHOD0(postFramebuffer, void());
};
@@ -1865,8 +2096,8 @@
struct OutputPostFramebufferTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by composeSurfaces to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
};
@@ -2045,8 +2276,8 @@
static const mat4 kDefaultColorTransformMat;
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by composeSurfaces to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD3(generateClientCompositionRequests,
std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 31946c1..843d3ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -670,6 +670,15 @@
const LayerVector::Visitor& visitor);
size_t getChildrenCount() const;
+
+ // ONLY CALL THIS FROM THE LAYER DTOR!
+ // See b/141111965. We need to add current children to offscreen layers in
+ // the layer dtor so as not to dangle layers. Since the layer has not
+ // committed its transaction when the layer is destroyed, we must add
+ // current children. This is safe in the dtor as we will no longer update
+ // the current state, but should not be called anywhere else!
+ LayerVector& getCurrentChildren() { return mCurrentChildren; }
+
void addChild(const sp<Layer>& layer);
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
@@ -707,6 +716,14 @@
Region debugGetVisibleRegionOnDefaultDisplay() 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.
+ * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+ * bounds are constrained by its parent bounds.
+ */
+ Rect getCroppedBufferSize(const Layer::State& s) const;
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -915,13 +932,6 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
- /**
- * 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.
- * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
- * 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.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
new file mode 100644
index 0000000..7dc98cc
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 "RefreshRateConfigs.h"
+
+namespace android::scheduler {
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+
+// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+// from multiple threads. This can only be called if refreshRateSwitching() returns true.
+// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+// baking them in.
+const std::map<RefreshRateType, RefreshRate>& RefreshRateConfigs::getRefreshRateMap() const {
+ LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
+ return mRefreshRateMap;
+}
+
+const RefreshRate& RefreshRateConfigs::getRefreshRateFromType(RefreshRateType type) const {
+ if (!mRefreshRateSwitchingSupported) {
+ return getCurrentRefreshRate().second;
+ } else {
+ auto refreshRate = mRefreshRateMap.find(type);
+ LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
+ return refreshRate->second;
+ }
+}
+
+std::pair<RefreshRateType, const RefreshRate&> RefreshRateConfigs::getCurrentRefreshRate() const {
+ int currentConfig = mCurrentConfig;
+ if (mRefreshRateSwitchingSupported) {
+ for (const auto& [type, refresh] : mRefreshRateMap) {
+ if (refresh.configId == currentConfig) {
+ return {type, refresh};
+ }
+ }
+ LOG_ALWAYS_FATAL();
+ }
+ return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
+}
+
+const RefreshRate& RefreshRateConfigs::getRefreshRateFromConfigId(int configId) const {
+ LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
+ return mRefreshRates[configId];
+}
+
+RefreshRateType RefreshRateConfigs::getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
+ if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+
+ for (const auto& [type, refreshRate] : mRefreshRateMap) {
+ if (refreshRate.hwcId == hwcId) {
+ return type;
+ }
+ }
+
+ return RefreshRateType::DEFAULT;
+}
+
+void RefreshRateConfigs::setCurrentConfig(int config) {
+ LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
+ mCurrentConfig = config;
+}
+
+RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
+ const std::vector<InputConfig>& configs, int currentConfig) {
+ init(refreshRateSwitching, configs, currentConfig);
+}
+
+RefreshRateConfigs::RefreshRateConfigs(
+ bool refreshRateSwitching,
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ int currentConfig) {
+ std::vector<InputConfig> inputConfigs;
+ for (const auto& config : configs) {
+ inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
+ }
+ init(refreshRateSwitching, inputConfigs, currentConfig);
+}
+
+void RefreshRateConfigs::init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ int currentConfig) {
+ mRefreshRateSwitchingSupported = refreshRateSwitching;
+ LOG_ALWAYS_FATAL_IF(configs.empty());
+ LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
+ mCurrentConfig = currentConfig;
+
+ auto buildRefreshRate = [&](int configId) -> RefreshRate {
+ const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
+ const float fps = 1e9 / vsyncPeriod;
+ return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
+ vsyncPeriod, configs[configId].hwcId};
+ };
+
+ for (int i = 0; i < configs.size(); ++i) {
+ mRefreshRates.push_back(buildRefreshRate(i));
+ }
+
+ if (!mRefreshRateSwitchingSupported) return;
+
+ auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
+ if (configs.size() < 2) {
+ return {};
+ }
+
+ std::vector<const RefreshRate*> sortedRefreshRates;
+ for (const auto& refreshRate : mRefreshRates) {
+ sortedRefreshRates.push_back(&refreshRate);
+ }
+ std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
+ [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+
+ // When the configs are ordered by the resync rate, we assume that
+ // the first one is DEFAULT and the second one is PERFORMANCE,
+ // i.e. the higher rate.
+ if (sortedRefreshRates[0]->vsyncPeriod == 0 || sortedRefreshRates[1]->vsyncPeriod == 0) {
+ return {};
+ }
+
+ return std::pair<int, int>(sortedRefreshRates[0]->configId,
+ sortedRefreshRates[1]->configId);
+ };
+
+ auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
+ if (!defaultAndPerfConfigs) {
+ mRefreshRateSwitchingSupported = false;
+ return;
+ }
+
+ mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
+ mRefreshRateMap[RefreshRateType::PERFORMANCE] = mRefreshRates[defaultAndPerfConfigs->second];
+}
+
+} // namespace android::scheduler
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2fd100f..90bba24 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -66,55 +66,17 @@
// from multiple threads. This can only be called if refreshRateSwitching() returns true.
// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
// baking them in.
- const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
- LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
- return mRefreshRateMap;
- }
+ const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const;
- const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
- if (!mRefreshRateSwitchingSupported) {
- return getCurrentRefreshRate().second;
- } else {
- auto refreshRate = mRefreshRateMap.find(type);
- LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
- return refreshRate->second;
- }
- }
+ const RefreshRate& getRefreshRateFromType(RefreshRateType type) const;
- std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
- int currentConfig = mCurrentConfig;
- if (mRefreshRateSwitchingSupported) {
- for (const auto& [type, refresh] : mRefreshRateMap) {
- if (refresh.configId == currentConfig) {
- return {type, refresh};
- }
- }
- LOG_ALWAYS_FATAL();
- }
- return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
- }
+ std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const;
- const RefreshRate& getRefreshRateFromConfigId(int configId) const {
- LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
- return mRefreshRates[configId];
- }
+ const RefreshRate& getRefreshRateFromConfigId(int configId) const;
- RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
- if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+ RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const;
- for (const auto& [type, refreshRate] : mRefreshRateMap) {
- if (refreshRate.hwcId == hwcId) {
- return type;
- }
- }
-
- return RefreshRateType::DEFAULT;
- }
-
- void setCurrentConfig(int config) {
- LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
- mCurrentConfig = config;
- }
+ void setCurrentConfig(int config);
struct InputConfig {
hwc2_config_t hwcId = 0;
@@ -122,78 +84,15 @@
};
RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- init(refreshRateSwitching, configs, currentConfig);
- }
+ int currentConfig);
RefreshRateConfigs(bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig) {
- std::vector<InputConfig> inputConfigs;
- for (const auto& config : configs) {
- inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
- }
- init(refreshRateSwitching, inputConfigs, currentConfig);
- }
+ int currentConfig);
private:
void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- mRefreshRateSwitchingSupported = refreshRateSwitching;
- LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
- mCurrentConfig = currentConfig;
-
- auto buildRefreshRate = [&](int configId) -> RefreshRate {
- const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
- const float fps = 1e9 / vsyncPeriod;
- return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
- vsyncPeriod, configs[configId].hwcId};
- };
-
- for (int i = 0; i < configs.size(); ++i) {
- mRefreshRates.push_back(buildRefreshRate(i));
- }
-
- if (!mRefreshRateSwitchingSupported) return;
-
- auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
- if (configs.size() < 2) {
- return {};
- }
-
- std::vector<const RefreshRate*> sortedRefreshRates;
- for (const auto& refreshRate : mRefreshRates) {
- sortedRefreshRates.push_back(&refreshRate);
- }
- std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
- [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
- });
-
- // When the configs are ordered by the resync rate, we assume that
- // the first one is DEFAULT and the second one is PERFORMANCE,
- // i.e. the higher rate.
- if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
- sortedRefreshRates[1]->vsyncPeriod == 0) {
- return {};
- }
-
- return std::pair<int, int>(sortedRefreshRates[0]->configId,
- sortedRefreshRates[1]->configId);
- };
-
- auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
- if (!defaultAndPerfConfigs) {
- mRefreshRateSwitchingSupported = false;
- return;
- }
-
- mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
- mRefreshRateMap[RefreshRateType::PERFORMANCE] =
- mRefreshRates[defaultAndPerfConfigs->second];
- }
-
+ int currentConfig);
// Whether this device is doing refresh rate switching or not. This must not change after this
// object is initialized.
bool mRefreshRateSwitchingSupported;
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index 699cd50..38f0708 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -21,10 +21,24 @@
namespace android::scheduler {
+class Clock {
+public:
+ virtual ~Clock();
+ /*
+ * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+ */
+ virtual nsecs_t now() const = 0;
+
+protected:
+ Clock() = default;
+ Clock(Clock const&) = delete;
+ Clock& operator=(Clock const&) = delete;
+};
+
/*
* TimeKeeper is the interface for a single-shot timer primitive.
*/
-class TimeKeeper {
+class TimeKeeper : public Clock {
public:
virtual ~TimeKeeper();
@@ -39,11 +53,6 @@
*/
virtual void alarmCancel() = 0;
- /*
- * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
- */
- virtual nsecs_t now() const = 0;
-
protected:
TimeKeeper(TimeKeeper const&) = delete;
TimeKeeper& operator=(TimeKeeper const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 7922484..a79fe98 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -47,22 +47,26 @@
return {mArmedInfo->mActualWakeupTime};
}
-nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
- VSyncTracker& tracker, nsecs_t now) {
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ VSyncTracker& tracker, nsecs_t now) {
+ auto const nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+ if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
+ return ScheduleResult::CannotSchedule;
+ }
+
+ auto const nextWakeupTime = nextVsyncTime - workDuration;
+ auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
- arm(tracker, now);
- return mArmedInfo->mActualWakeupTime;
+ mArmedInfo = {nextWakeupTime, nextVsyncTime};
+ return result;
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
if (!mArmedInfo) {
return;
}
- arm(tracker, now);
-}
-
-void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
auto const nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
@@ -214,16 +218,13 @@
return result;
}
auto& callback = it->second;
- result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
-
auto const now = mTimeKeeper->now();
- auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
-
- if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
- return ScheduleResult::CannotSchedule;
+ result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+ if (result == ScheduleResult::CannotSchedule) {
+ return result;
}
- if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+ if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index f058099..530e0a6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -44,8 +44,8 @@
std::optional<nsecs_t> lastExecutedVsyncTarget() const;
// This moves the state from disarmed->armed and will calculate the wakeupTime.
- nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
- nsecs_t now);
+ ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ nsecs_t now);
// This will update armed entries with the latest vsync information. Entry remains armed.
void update(VSyncTracker& tracker, nsecs_t now);
@@ -67,7 +67,6 @@
void ensureNotRunning();
private:
- void arm(VSyncTracker& tracker, nsecs_t now);
std::string const mName;
std::function<void(nsecs_t)> const mCallback;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 643c5d2..3894992 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -54,6 +54,11 @@
return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
}
+nsecs_t VSyncPredictor::currentPeriod() const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+}
+
void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 1590f49..4210b3c 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -39,6 +39,7 @@
void addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+ nsecs_t currentPeriod() const final;
/*
* Inform the model that the period is anticipated to change to a new value.
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..6588d1b
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 "VSyncReactor.h"
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+Clock::~Clock() = default;
+
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+ std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+ : mClock(std::move(clock)),
+ mDispatch(std::move(dispatch)),
+ mTracker(std::move(tracker)),
+ mPendingLimit(pendingFenceLimit) {}
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+ if (!fence) {
+ return false;
+ }
+
+ nsecs_t const signalTime = fence->getCachedSignalTime();
+ if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+ return true;
+ }
+
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (mIgnorePresentFences) {
+ return true;
+ }
+
+ for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+ auto const time = (*it)->getCachedSignalTime();
+ if (time == Fence::SIGNAL_TIME_PENDING) {
+ it++;
+ } else if (time == Fence::SIGNAL_TIME_INVALID) {
+ it = mUnfiredFences.erase(it);
+ } else {
+ mTracker->addVsyncTimestamp(time);
+ it = mUnfiredFences.erase(it);
+ }
+ }
+
+ if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+ if (mPendingLimit == mUnfiredFences.size()) {
+ mUnfiredFences.erase(mUnfiredFences.begin());
+ }
+ mUnfiredFences.push_back(fence);
+ } else {
+ mTracker->addVsyncTimestamp(signalTime);
+ }
+
+ return false; // TODO(b/144707443): add policy for turning on HWVsync.
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mIgnorePresentFences = ignoration;
+ if (mIgnorePresentFences == true) {
+ mUnfiredFences.clear();
+ }
+}
+
+nsecs_t VSyncReactor::computeNextRefresh(int periodOffset) const {
+ auto const now = mClock->now();
+ auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
+ return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+}
+
+nsecs_t VSyncReactor::expectedPresentTime() {
+ return mTracker->nextAnticipatedVSyncTimeFrom(mClock->now());
+}
+
+void VSyncReactor::setPeriod(nsecs_t period) {
+ mTracker->setPeriod(period);
+}
+
+nsecs_t VSyncReactor::getPeriod() {
+ return mTracker->currentPeriod();
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..73a09f1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -0,0 +1,57 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android::scheduler {
+
+class Clock;
+class VSyncDispatch;
+class VSyncTracker;
+
+// TODO (b/145217110): consider renaming.
+class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
+public:
+ VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+ std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+
+ bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
+ void setIgnorePresentFences(bool ignoration);
+
+ nsecs_t computeNextRefresh(int periodOffset) const;
+ nsecs_t expectedPresentTime();
+
+ void setPeriod(nsecs_t period);
+ nsecs_t getPeriod();
+
+private:
+ std::unique_ptr<Clock> const mClock;
+ std::unique_ptr<VSyncDispatch> const mDispatch;
+ std::unique_ptr<VSyncTracker> const mTracker;
+ size_t const mPendingLimit;
+
+ std::mutex mMutex;
+ bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+ std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 97b9620..6be63fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -47,6 +47,20 @@
*/
virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+ /*
+ * The current period of the vsync signal.
+ *
+ * \return The current period of the vsync signal
+ */
+ virtual nsecs_t currentPeriod() const = 0;
+
+ /*
+ * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
+ *
+ * \param [in] period The period that the system is changing into.
+ */
+ virtual void setPeriod(nsecs_t period) = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f5cbb4b..12206e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1610,6 +1610,7 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ const nsecs_t frameStart = systemTime();
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
@@ -1674,6 +1675,13 @@
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
+ if (mFrameStartTime <= 0) {
+ // We should only use the time of the first invalidate
+ // message that signals a refresh as the beginning of the
+ // frame. Otherwise the real frame time will be
+ // underestimated.
+ mFrameStartTime = frameStart;
+ }
signalRefresh();
}
break;
@@ -1752,6 +1760,9 @@
mGeometryInvalid = false;
mCompositionEngine->present(refreshArgs);
+ mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
+ // Reset the frame start time now that we've recorded this frame.
+ mFrameStartTime = 0;
postFrame();
postComposition();
@@ -3118,7 +3129,13 @@
listenerCallbacks.insert(listener);
}
- sp<Layer> layer(fromHandle(s.surface));
+ sp<Layer> layer = nullptr;
+ if (s.surface) {
+ layer = fromHandle(s.surface);
+ } else {
+ // The client may provide us a null handle. Treat it as if the layer was removed.
+ ALOGW("Attempt to set client state with a null layer handle");
+ }
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
mTransactionCompletedThread.registerUnpresentedCallbackHandle(
@@ -3721,9 +3738,6 @@
}
if (currentMode == HWC_POWER_MODE_OFF) {
- // Turn on the display
- // TODO: @vhau temp fix only! See b/141111965
- mTransactionCompletedThread.clearAllPending();
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -3959,6 +3973,12 @@
StringAppendF(&result, "%" PRIu32 " Hz, ",
mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
}
+ StringAppendF(&result,
+ "DesiredDisplayConfigSpecs: default config ID: %" PRIu32
+ ", min: %.2f Hz, max: %.2f Hz",
+ mDesiredDisplayConfigSpecs.defaultModeId,
+ mDesiredDisplayConfigSpecs.minRefreshRate,
+ mDesiredDisplayConfigSpecs.maxRefreshRate);
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -4388,6 +4408,8 @@
case SET_ACTIVE_CONFIG:
case SET_ALLOWED_DISPLAY_CONFIGS:
case GET_ALLOWED_DISPLAY_CONFIGS:
+ case SET_DESIRED_DISPLAY_CONFIG_SPECS:
+ case GET_DESIRED_DISPLAY_CONFIG_SPECS:
case SET_ACTIVE_COLOR_MODE:
case INJECT_VSYNC:
case SET_POWER_MODE:
@@ -5071,14 +5093,21 @@
return PERMISSION_DENIED;
}
+ Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+ crop.right = parentSourceBounds.getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+ crop.bottom = parentSourceBounds.getHeight();
+ }
+
+ if (crop.isEmpty() || frameScale <= 0.0f) {
+ // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+ // crop was not specified, or an invalid frame scale was provided.
+ return BAD_VALUE;
}
reqWidth = crop.width() * frameScale;
reqHeight = crop.height() * frameScale;
@@ -5501,6 +5530,66 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate) {
+ ATRACE_CALL();
+
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ postMessageSync(new LambdaMessage([&]() {
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set desired display configs for invalid display token %p",
+ displayToken.get());
+ } else if (display->isVirtual()) {
+ ALOGW("Attempt to set desired display configs for virtual display");
+ } else {
+ // TODO(b/142507213): Plug through to HWC once the interface is ready.
+ Mutex::Autolock lock(mStateLock);
+ const DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = {defaultModeId,
+ minRefreshRate,
+ maxRefreshRate};
+ if (desiredDisplayConfigSpecs == mDesiredDisplayConfigSpecs) {
+ return;
+ }
+ ALOGV("Updating desired display configs");
+ ALOGD("desiredDisplayConfigSpecs: defaultId: %d min: %.f max: %.f decisions: ",
+ desiredDisplayConfigSpecs.defaultModeId, desiredDisplayConfigSpecs.minRefreshRate,
+ desiredDisplayConfigSpecs.maxRefreshRate);
+ mDesiredDisplayConfigSpecs = desiredDisplayConfigSpecs;
+ }
+ }));
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ ATRACE_CALL();
+
+ if (!displayToken || !outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ if (display->isPrimary()) {
+ *outDefaultModeId = mDesiredDisplayConfigSpecs.defaultModeId;
+ *outMinRefreshRate = mDesiredDisplayConfigSpecs.minRefreshRate;
+ *outMaxRefreshRate = mDesiredDisplayConfigSpecs.maxRefreshRate;
+ }
+
+ return NO_ERROR;
+}
+
void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
mFlinger->setInputWindowsFinished();
}
@@ -5524,6 +5613,20 @@
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
mNumLayers--;
+ removeFromOffscreenLayers(layer);
+}
+
+// WARNING: ONLY CALL THIS FROM LAYER DTOR
+// Here we add children in the current state to offscreen layers and remove the
+// layer itself from the offscreen layer list. Since
+// this is the dtor, it is safe to access the current state. This keeps us
+// from dangling children layers such that they are not reachable from the
+// Drawing state nor the offscreen layer list
+// See b/141111965
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+ for (auto& child : layer->getCurrentChildren()) {
+ mOffscreenLayers.emplace(child.get());
+ }
mOffscreenLayers.erase(layer);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a09528d..7144608 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -314,6 +314,8 @@
void onLayerFirstRef(Layer*);
void onLayerDestroyed(Layer*);
+ void removeFromOffscreenLayers(Layer* layer);
+
TransactionCompletedThread& getTransactionCompletedThread() {
return mTransactionCompletedThread;
}
@@ -384,6 +386,17 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
+ struct DesiredDisplayConfigSpecs {
+ int32_t defaultModeId;
+ float minRefreshRate;
+ float maxRefreshRate;
+
+ bool operator==(const DesiredDisplayConfigSpecs& other) const {
+ return defaultModeId == other.defaultModeId && minRefreshRate == other.minRefreshRate &&
+ maxRefreshRate == other.maxRefreshRate;
+ }
+ };
+
/* ------------------------------------------------------------------------
* IBinder interface
*/
@@ -471,6 +484,11 @@
const std::vector<int32_t>& allowedConfigs) override;
status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
std::vector<int32_t>* outAllowedConfigs) override;
+ status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
+ float minRefreshRate, float maxRefreshRate) override;
+ status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId, float* outMinRefreshRate,
+ float* outMaxRefreshRate) override;
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
@@ -1121,6 +1139,7 @@
// All configs are allowed if the set is empty.
using DisplayConfigs = std::set<int32_t>;
DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
+ DesiredDisplayConfigSpecs mDesiredDisplayConfigSpecs GUARDED_BY(mStateLock);
std::mutex mActiveConfigLock;
// This bit is set once we start setting the config. We read from this bit during the
@@ -1158,6 +1177,9 @@
bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
Hwc2::impl::PowerAdvisor mPowerAdvisor;
+ // This should only be accessed on the main thread.
+ nsecs_t mFrameStartTime = 0;
+
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
// Flag used to set override allowed display configs from backdoor
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 611afce..626efb8 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -20,14 +20,13 @@
#include "TimeStats.h"
#include <android-base/stringprintf.h>
-
#include <log/log.h>
-
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
+#include <chrono>
namespace android {
@@ -113,6 +112,23 @@
mTimeStats.clientCompositionFrames++;
}
+static int32_t msBetween(nsecs_t start, nsecs_t end) {
+ int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::nanoseconds(end - start))
+ .count();
+ delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
+ return static_cast<int32_t>(delta);
+}
+
+void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mPowerTime.powerMode == HWC_POWER_MODE_NORMAL) {
+ mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+ }
+}
+
bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
@@ -149,12 +165,6 @@
return true;
}
-static int32_t msBetween(nsecs_t start, nsecs_t end) {
- int64_t delta = (end - start) / 1000000;
- delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
- return static_cast<int32_t>(delta);
-}
-
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
ATRACE_CALL();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 6e71f5a..670bc8e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -44,6 +44,13 @@
virtual void incrementMissedFrames() = 0;
virtual void incrementClientCompositionFrames() = 0;
+ // Records the start and end times for a frame.
+ // The start time is the same as the beginning of a SurfaceFlinger
+ // invalidate message.
+ // The end time corresponds to when SurfaceFlinger finishes submitting the
+ // request to HWC to present a frame.
+ virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 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;
@@ -116,6 +123,8 @@
void incrementMissedFrames() override;
void incrementClientCompositionFrames() override;
+ void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 16d2da0..83cd45a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -111,6 +111,8 @@
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
result.append(presentToPresent.toString());
+ StringAppendF(&result, "frameDuration histogram is as below:\n");
+ result.append(frameDuration.toString());
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
result.append(ele->toString());
@@ -158,6 +160,11 @@
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
+ for (const auto& histEle : frameDuration.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index f2ac7ff..6b28970 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -61,6 +61,7 @@
int32_t clientCompositionFrames = 0;
int64_t displayOnTime = 0;
Histogram presentToPresent;
+ Histogram frameDuration;
std::unordered_map<std::string, TimeStatsLayer> stats;
std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 0dacbeb..96430b3 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: 10
+// Next tag: 11
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -43,6 +43,8 @@
repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
// Present to present histogram.
repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+ // Frame CPU duration histogram.
+ repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
// Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index c15355d..8db03db 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -189,15 +189,6 @@
return NO_ERROR;
}
-void TransactionCompletedThread::clearAllPending() {
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- return;
- }
- mPendingTransactions.clear();
- mConditionVariable.notify_all();
-}
-
status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
std::lock_guard lock(mMutex);
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index cd95bfb..12ea8fe 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -70,8 +70,6 @@
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
- void clearAllPending();
-
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 51b20cb..049c872 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -260,7 +260,7 @@
prop {
api_name: "color_space_agnostic_dataspace"
type: Long
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
}
@@ -339,7 +339,7 @@
prop {
api_name: "set_display_power_timer_ms"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.set_display_power_timer_ms"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index d021fc2..ca7e18d 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -18,9 +18,11 @@
test_suites: ["device-tests"],
srcs: [
"BufferGenerator.cpp",
+ "CommonTypes_test.cpp",
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
"DisplayActiveConfig_test.cpp",
+ "DisplayConfigs_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
@@ -41,6 +43,8 @@
"libtrace_proto",
],
shared_libs: [
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.composer@2.1",
"libandroid",
"libbinder",
"libcutils",
@@ -53,6 +57,7 @@
"libtimestats_proto",
"libui",
"libutils",
+ "vintf-graphics-common-ndk_platform",
]
}
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
new file mode 100644
index 0000000..a3e16f9
--- /dev/null
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
+
+using AidlBlendMode = aidl::android::hardware::graphics::common::BlendMode;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+
+using HidlBlendMode = android::hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+using HidlDataspace = android::hardware::graphics::common::V1_2::Dataspace;
+
+static_assert(static_cast<uint32_t>(AidlBlendMode::INVALID) ==
+ static_cast<uint32_t>(HidlBlendMode::INVALID));
+static_assert(static_cast<uint32_t>(AidlBlendMode::NONE) ==
+ static_cast<uint32_t>(HidlBlendMode::NONE));
+static_assert(static_cast<uint32_t>(AidlBlendMode::PREMULTIPLIED) ==
+ static_cast<uint32_t>(HidlBlendMode::PREMULTIPLIED));
+static_assert(static_cast<uint32_t>(AidlBlendMode::COVERAGE) ==
+ static_cast<uint32_t>(HidlBlendMode::COVERAGE));
+
+static_assert(static_cast<uint32_t>(AidlDataspace::UNKNOWN) ==
+ static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(AidlDataspace::ARBITRARY) ==
+ static_cast<uint32_t>(HidlDataspace::ARBITRARY));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT709) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625_UNADJUSTED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525_UNADJUSTED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT470M) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT470M));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_FILM) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_FILM));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_DCI_P3) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_ADOBE_RGB) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SRGB) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SMPTE_170M) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SMPTE_170M));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_2) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_2));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_6) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_6));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_8) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_8));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_ST2084) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_ST2084));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_FULL) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_FULL));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_LIMITED) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_LIMITED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_EXTENDED) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_EXTENDED));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_JFIF) ==
+ static_cast<uint32_t>(HidlDataspace::V0_JFIF));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_625) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_525) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT709) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::DCI_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::ADOBE_RGB) ==
+ static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_PQ) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::DEPTH) ==
+ static_cast<uint32_t>(HidlDataspace::DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::SENSOR) ==
+ static_cast<uint32_t>(HidlDataspace::SENSOR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_PQ) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::DYNAMIC_DEPTH) ==
+ static_cast<uint32_t>(HidlDataspace::DYNAMIC_DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::JPEG_APP_SEGMENTS) ==
+ static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
+static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
+ static_cast<uint32_t>(HidlDataspace::HEIF));
+
+// Below are the dataspaces that have been deprecated for sometime. They are required to behave
+// the same as their V0_* counterparts. We redefined them in AIDL to be the same as the
+// their V0_* counterparts.
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
+ static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
+ static_cast<uint32_t>(AidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
+ static_cast<uint32_t>(AidlDataspace::V0_JFIF));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
+ static_cast<uint32_t>(AidlDataspace::V0_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
+ static_cast<uint32_t>(AidlDataspace::V0_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
+ static_cast<uint32_t>(AidlDataspace::V0_BT709));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..b1bb7fd 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -274,7 +274,7 @@
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), FRAME_SCALE, &outBuffer);
+ Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
new file mode 100644
index 0000000..420fb29
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <thread>
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+/**
+ * Test class for setting display configs and passing around refresh rate ranges.
+ */
+class RefreshRateRangeTest : public ::testing::Test {
+protected:
+ void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+
+ sp<IBinder> mDisplayToken;
+ int32_t defaultConfigId;
+ float minRefreshRate;
+ float maxRefreshRate;
+};
+
+TEST_F(RefreshRateRangeTest, simpleSetAndGet) {
+ status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
+ EXPECT_EQ(res, NO_ERROR);
+
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 1);
+ EXPECT_EQ(minRefreshRate, 45);
+ EXPECT_EQ(maxRefreshRate, 75);
+}
+
+TEST_F(RefreshRateRangeTest, complexSetAndGet) {
+ status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
+ EXPECT_EQ(res, NO_ERROR);
+
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 1);
+ EXPECT_EQ(minRefreshRate, 45);
+ EXPECT_EQ(maxRefreshRate, 75);
+
+ // Second call overrides the first one.
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 10, 145, 875);
+ EXPECT_EQ(res, NO_ERROR);
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 10);
+ EXPECT_EQ(minRefreshRate, 145);
+ EXPECT_EQ(maxRefreshRate, 875);
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 73f563d..0ad122b 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -1443,6 +1443,61 @@
mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
}
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop(0, 0, 10, 10);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+ TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0c4a752..ccfa6c5 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
"VSyncPredictorTest.cpp",
+ "VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7b60fa2..069344a 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -20,11 +20,11 @@
#include <TimeStats/TimeStats.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
#include <utils/String16.h>
#include <utils/Vector.h>
+#include <chrono>
#include <random>
#include <unordered_set>
@@ -278,6 +278,31 @@
EXPECT_EQ(2, histogramProto.time_millis());
}
+TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ using namespace std::chrono_literals;
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_OFF);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+ .count());
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.frame_duration_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.frame_duration().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ EXPECT_EQ(3, histogramProto.time_millis());
+}
+
TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c012616..484947d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -47,6 +47,10 @@
return timePoint - floor + mPeriod;
}
+ nsecs_t currentPeriod() const final { return mPeriod; }
+
+ void setPeriod(nsecs_t) final {}
+
private:
nsecs_t const mPeriod;
};
@@ -73,6 +77,13 @@
mBase = last_known;
}
+ nsecs_t currentPeriod() const final {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ return mPeriod;
+ }
+
+ void setPeriod(nsecs_t) final {}
+
private:
std::mutex mutable mMutex;
nsecs_t mPeriod;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 82950b5..d668a41 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,6 +39,8 @@
MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
@@ -551,6 +553,32 @@
EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
}
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
+ CountingCallback cb0(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ CountingCallback cb0(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 600));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
+}
+
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
@@ -599,11 +627,10 @@
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
- EXPECT_THAT(*queried, Eq(900));
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
entry.disarm();
EXPECT_FALSE(entry.wakeupTime());
@@ -619,11 +646,10 @@
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
- EXPECT_THAT(*queried, Eq(9500));
+ EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(9500));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
@@ -634,8 +660,10 @@
calledTime = time;
});
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
- EXPECT_THAT(wakeup, Eq(900));
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
entry.callback(entry.executing());
@@ -659,23 +687,40 @@
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
entry.update(mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(920));
+ wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(920));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(wakeup));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ entry.executing();
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+ EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+ EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..2e01d5c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+ MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+ VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+ void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+ return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+ }
+ nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
+ void setPeriod(nsecs_t period) { mTracker->setPeriod(period); }
+
+private:
+ std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockClock : public Clock {
+public:
+ MOCK_CONST_METHOD0(now, nsecs_t());
+};
+
+class ClockWrapper : public Clock {
+public:
+ ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+ nsecs_t now() const { return mClock->now(); }
+
+private:
+ std::shared_ptr<Clock> const mClock;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+ MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+ MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+ VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) final {
+ return mDispatch->registerCallback(callbackFn, callbackName);
+ }
+
+ void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) final {
+ return mDispatch->schedule(token, workDuration, earliestVsync);
+ }
+
+ CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+private:
+ std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+ sp<Fence> fence = new Fence();
+ return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+ sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+ return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+ FenceTime::Snapshot snap(time);
+ fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+ sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+ std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+ signalFenceWithTime(ft, time);
+ return ft;
+}
+
+class VSyncReactorTest : public testing::Test {
+protected:
+ VSyncReactorTest()
+ : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
+ mMockTracker(std::make_shared<MockVSyncTracker>()),
+ mMockClock(std::make_shared<NiceMock<MockClock>>()),
+ mReactor(std::make_unique<ClockWrapper>(mMockClock),
+ std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+ std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}
+
+ std::shared_ptr<MockVSyncDispatch> mMockDispatch;
+ std::shared_ptr<MockVSyncTracker> mMockTracker;
+ std::shared_ptr<MockClock> mMockClock;
+ static constexpr size_t kPendingLimit = 3;
+ static constexpr nsecs_t dummyTime = 47;
+ VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+ EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+ EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+ nsecs_t anotherDummyTime = 2919019201;
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+ auto pendingFence = generatePendingFence();
+ EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+ Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+ signalFenceWithTime(pendingFence, dummyTime);
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+ std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+ std::array<nsecs_t, fences.size()> fakeTimes;
+ std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+ std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+ i++;
+ return i * i;
+ });
+
+ for (auto const& fence : fences) {
+ mReactor.addPresentFence(fence);
+ }
+
+ for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+ }
+
+ for (auto i = 0u; i < fences.size(); i++) {
+ signalFenceWithTime(fences[i], fakeTimes[i]);
+ }
+ mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+ static constexpr size_t aFewTimes = 8;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+
+ mReactor.setIgnorePresentFences(true);
+ for (auto i = 0; i < aFewTimes; i++) {
+ mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+ }
+
+ mReactor.setIgnorePresentFences(false);
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
+ nsecs_t const fakeTimestamp = 4839;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(1)
+ .WillOnce(Return(fakeTimestamp));
+
+ EXPECT_THAT(mReactor.computeNextRefresh(0), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
+ nsecs_t const fakeTimestamp = 4839;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(1)
+ .WillOnce(Return(fakeTimestamp));
+
+ EXPECT_THAT(mReactor.expectedPresentTime(), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
+ nsecs_t const fakeTimestamp = 4839;
+ nsecs_t const fakePeriod = 1010;
+ nsecs_t const fakeNow = 2214;
+ int const numPeriodsOut = 3;
+ EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+ EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+ .WillOnce(Return(fakeTimestamp));
+ EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, getPeriod) {
+ nsecs_t const fakePeriod = 1010;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+ EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
+}
+
+TEST_F(VSyncReactorTest, setPeriod) {
+ nsecs_t const fakePeriod = 4098;
+ EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
+ mReactor.setPeriod(fakePeriod);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index b1634a8..e94af49 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -34,6 +34,7 @@
MOCK_METHOD0(incrementTotalFrames, void());
MOCK_METHOD0(incrementMissedFrames, void());
MOCK_METHOD0(incrementClientCompositionFrames, void());
+ MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
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));
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 22df255..2c63da0 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -136,6 +136,11 @@
}
}
+ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+ bool unlock = true) {
+ fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+ }
+
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
bool unlock = true) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8f4667e..a020e74 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1311,7 +1311,14 @@
&img.dequeue_fence);
if (err != 0) {
ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
- result = VK_ERROR_SURFACE_LOST_KHR;
+ switch (-err) {
+ case ENOMEM:
+ result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ break;
+ default:
+ result = VK_ERROR_SURFACE_LOST_KHR;
+ break;
+ }
break;
}
img.buffer = buffer;
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index a626e48..8528898 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library_shared {
name: "libvkjson",
srcs: [
"vkjson.cc",
@@ -15,11 +15,14 @@
export_include_dirs: [
".",
],
+ shared_libs: [
+ "libvulkan",
+ ],
whole_static_libs: [
"libjsoncpp",
],
- header_libs: [
- "vulkan_headers",
+ export_shared_lib_headers: [
+ "libvulkan",
],
}