Merge "SF: Improve LayerInfo::calculateAverageFrameTime"
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 491c629..a1d8ce5 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -25,4 +25,5 @@
METADATA_MOUSE_CURSOR = 4,
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
+ METADATA_DEQUEUE_TIME = 7,
}
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index bfcc058..ba25a5a 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -39,8 +39,13 @@
std::string calling_package;
};
-static binder::Status exception(uint32_t code, const std::string& msg) {
- MYLOGE("%s (%d) ", msg.c_str(), code);
+static binder::Status exception(uint32_t code, const std::string& msg,
+ const std::string& extra_msg = "") {
+ if (extra_msg.empty()) {
+ MYLOGE("%s (%d) ", msg.c_str(), code);
+ } else {
+ MYLOGE("%s %s (%d) ", msg.c_str(), extra_msg.c_str(), code);
+ }
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
@@ -60,7 +65,7 @@
} // namespace
-DumpstateService::DumpstateService() : ds_(nullptr) {
+DumpstateService::DumpstateService() : ds_(nullptr), calling_uid_(-1), calling_package_() {
}
char const* DumpstateService::getServiceName() {
@@ -131,6 +136,10 @@
ds_->SetOptions(std::move(options));
ds_->listener_ = listener;
+ // Track caller info for cancellation purposes.
+ calling_uid_ = calling_uid;
+ calling_package_ = calling_package;
+
DumpstateInfo* ds_info = new DumpstateInfo();
ds_info->ds = ds_;
ds_info->calling_uid = calling_uid;
@@ -149,8 +158,20 @@
return binder::Status::ok();
}
-binder::Status DumpstateService::cancelBugreport() {
+binder::Status DumpstateService::cancelBugreport(int32_t calling_uid,
+ const std::string& calling_package) {
std::lock_guard<std::mutex> lock(lock_);
+ if (calling_uid != calling_uid_ || calling_package != calling_package_) {
+ // Note: we use a SecurityException to prevent BugreportManagerServiceImpl from killing the
+ // report in progress (from another caller).
+ return exception(
+ binder::Status::EX_SECURITY,
+ StringPrintf("Cancellation requested by %d/%s does not match report in "
+ "progress",
+ calling_uid, calling_package.c_str()),
+ // Sharing the owner of the BR is a (minor) leak, so leave it out of the app's exception
+ StringPrintf("started by %d/%s", calling_uid_, calling_package_.c_str()));
+ }
ds_->Cancel();
return binder::Status::ok();
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index ac8d3ac..3ec8471 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -44,8 +44,7 @@
const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) override;
- // No-op
- binder::Status cancelBugreport();
+ binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
private:
// Dumpstate object which contains all the bugreporting logic.
@@ -53,6 +52,8 @@
// one bugreport.
// This service does not own this object.
Dumpstate* ds_;
+ int32_t calling_uid_;
+ std::string calling_package_;
std::mutex lock_;
};
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index eeaa5a3..c833d0e 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -124,6 +124,12 @@
return *this;
}
+CommandOptions::CommandOptionsBuilder&
+CommandOptions::CommandOptionsBuilder::CloseAllFileDescriptorsOnExec() {
+ values.close_all_fds_on_exec_ = true;
+ return *this;
+}
+
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
const std::string& message) {
values.logging_message_ = message;
@@ -137,6 +143,7 @@
CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms)
: timeout_ms_(timeout_ms),
always_(false),
+ close_all_fds_on_exec_(false),
account_mode_(DONT_DROP_ROOT),
output_mode_(NORMAL_OUTPUT),
logging_message_("") {
@@ -157,6 +164,10 @@
return values.always_;
}
+bool CommandOptions::ShouldCloseAllFileDescriptorsOnExec() const {
+ return values.close_all_fds_on_exec_;
+}
+
PrivilegeMode CommandOptions::PrivilegeMode() const {
return values.account_mode_;
}
@@ -277,7 +288,8 @@
MYLOGI(logging_message.c_str(), command_string.c_str());
}
- bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
+ bool silent = (options.OutputMode() == REDIRECT_TO_STDERR ||
+ options.ShouldCloseAllFileDescriptorsOnExec());
bool redirecting_to_fd = STDOUT_FILENO != fd;
if (PropertiesHelper::IsDryRun() && !options.Always()) {
@@ -314,7 +326,27 @@
return -1;
}
- if (silent) {
+ if (options.ShouldCloseAllFileDescriptorsOnExec()) {
+ int devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
+ TEMP_FAILURE_RETRY(dup2(devnull_fd, STDIN_FILENO));
+ close(devnull_fd);
+ devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
+ TEMP_FAILURE_RETRY(dup2(devnull_fd, STDOUT_FILENO));
+ TEMP_FAILURE_RETRY(dup2(devnull_fd, STDERR_FILENO));
+ close(devnull_fd);
+ // This is to avoid leaking FDs that, accidentally, have not been
+ // marked as O_CLOEXEC. Leaking FDs across exec can cause failures
+ // when execing a process that has a SELinux auto_trans rule.
+ // Here we assume that the dumpstate process didn't open more than
+ // 1000 FDs. In theory we could iterate through /proc/self/fd/, but
+ // doing that in a fork-safe way is too complex and not worth it
+ // (opendir()/readdir() do heap allocations and take locks).
+ for (int i = 0; i < 1000; i++) {
+ if (i != STDIN_FILENO && i!= STDOUT_FILENO && i != STDERR_FILENO) {
+ close(i);
+ }
+ }
+ } else if (silent) {
// Redirects stdout to stderr
TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
} else if (redirecting_to_fd) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b099443..b00c46e 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -80,6 +80,7 @@
int64_t timeout_ms_;
bool always_;
+ bool close_all_fds_on_exec_;
PrivilegeMode account_mode_;
OutputMode output_mode_;
std::string logging_message_;
@@ -112,6 +113,13 @@
CommandOptionsBuilder& DropRoot();
/* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
CommandOptionsBuilder& RedirectStderr();
+ /* Closes all file descriptors before exec-ing the target process. This
+ * includes also stdio pipes, which are dup-ed on /dev/null. It prevents
+ * leaking opened FDs to the target process, which in turn can hit
+ * selinux denials in presence of auto_trans rules.
+ */
+ CommandOptionsBuilder& CloseAllFileDescriptorsOnExec();
+
/* When not empty, logs a message before executing the command.
* Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
CommandOptionsBuilder& Log(const std::string& message);
@@ -130,6 +138,8 @@
int64_t TimeoutInMs() const;
/* Checks whether the command should always be run, even on dry-run mode. */
bool Always() const;
+ /* Checks whether all FDs should be closed prior to the exec() calls. */
+ bool ShouldCloseAllFileDescriptorsOnExec() const;
/** Gets the PrivilegeMode of the command. */
PrivilegeMode PrivilegeMode() const;
/** Gets the OutputMode of the command. */
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index ba008bb..0793f0b 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
import android.os.IDumpstateListener;
/**
- * Binder interface for the currently running dumpstate process.
- * {@hide}
- */
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
interface IDumpstate {
// NOTE: If you add to or change these modes, please also change the corresponding enums
@@ -49,10 +49,10 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
- /*
+ /**
* Starts a bugreport in the background.
*
- *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling
+ * <p>Shows the user a dialog to get consent for sharing the bugreport with the calling
* application. If they deny {@link IDumpstateListener#onError} will be called. If they
* consent and bugreport generation is successful artifacts will be copied to the given fds and
* {@link IDumpstateListener#onFinished} will be called. If there
@@ -71,8 +71,15 @@
int bugreportMode, IDumpstateListener listener,
boolean isScreenshotRequested);
- /*
+ /**
* Cancels the bugreport currently in progress.
+ *
+ * <p>The caller must match the original caller of {@link #startBugreport} in order for the
+ * report to actually be cancelled. A {@link SecurityException} is reported if a mismatch is
+ * detected.
+ *
+ * @param callingUid UID of the original application that requested the cancellation.
+ * @param callingPackage package of the original application that requested the cancellation.
*/
- void cancelBugreport();
+ void cancelBugreport(int callingUid, @utf8InCpp String callingPackage);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index afa0b4d..4a4a510 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mount.h>
#include <sys/poll.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -174,6 +175,7 @@
#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
#define LINKERCONFIG_DIR "/linkerconfig"
#define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
+#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -227,7 +229,6 @@
static const std::string DUMP_HALS_TASK = "DUMP HALS";
static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
-static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
namespace android {
namespace os {
@@ -1053,6 +1054,24 @@
}
}
+static void MaybeAddSystemTraceToZip() {
+ // This function copies into the .zip the system trace that was snapshotted
+ // by the early call to MaybeSnapshotSystemTrace(), if any background
+ // tracing was happening.
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping system trace because it's not a zipped bugreport\n");
+ return;
+ }
+ if (!ds.has_system_trace_) {
+ // No background trace was happening at the time dumpstate was invoked.
+ return;
+ }
+ ds.AddZipEntry(
+ ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT,
+ SYSTEM_TRACE_SNAPSHOT);
+ android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT);
+}
+
static void DumpVisibleWindowViews() {
if (!ds.IsZipping()) {
MYLOGD("Not dumping visible views because it's not a zipped bugreport\n");
@@ -1550,7 +1569,7 @@
dprintf(out_fd, "========================================================\n");
RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
- DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
dprintf(out_fd, "========================================================\n");
dprintf(out_fd, "== Running Application Providers (non-platform)\n");
@@ -1577,7 +1596,6 @@
ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
- ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1);
}
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1651,6 +1669,8 @@
AddAnrTraceFiles();
+ MaybeAddSystemTraceToZip();
+
// NOTE: tombstones are always added as separate entries in the zip archive
// and are not interspersed with the main report.
const bool tombstones_dumped = AddDumps(ds.tombstone_data_.begin(), ds.tombstone_data_.end(),
@@ -1730,11 +1750,7 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
}
- if (ds.dump_pool_) {
- WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
- } else {
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
- }
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos);
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -2163,6 +2179,22 @@
return;
}
+ /*
+ * mount debugfs for non-user builds which launch with S and unmount it
+ * after invoking dumpstateBoard_* methods. This is to enable debug builds
+ * to not have debugfs mounted during runtime. It will also ensure that
+ * debugfs is only accessed by the dumpstate HAL.
+ */
+ auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
+ bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31;
+
+ if (mount_debugfs) {
+ RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
+ AS_ROOT_20);
+ RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"},
+ AS_ROOT_20);
+ }
+
std::vector<std::string> paths;
std::vector<android::base::ScopeGuard<std::function<void()>>> remover;
for (int i = 0; i < NUM_OF_DUMPS; i++) {
@@ -2262,6 +2294,10 @@
"there might be racing in content\n", killing_timeout_sec);
}
+ if (mount_debugfs) {
+ RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
+ }
+
auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
for (size_t i = 0; i < paths.size(); i++) {
struct stat s;
@@ -2872,6 +2908,13 @@
RunDumpsysCritical();
}
MaybeTakeEarlyScreenshot();
+
+ if (!is_dumpstate_restricted) {
+ // Snapshot the system trace now (if running) to avoid that dumpstate's
+ // own activity pushes out interesting data from the trace ring buffer.
+ // The trace file is added to the zip by MaybeAddSystemTraceToZip().
+ MaybeSnapshotSystemTrace();
+ }
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
if (options_->telephony_only) {
@@ -2962,6 +3005,26 @@
TakeScreenshot();
}
+void Dumpstate::MaybeSnapshotSystemTrace() {
+ // If a background system trace is happening and is marked as "suitable for
+ // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
+ // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
+ // case that no trace is ongoing, this command is a no-op.
+ // Note: this should not be enqueued as we need to freeze the trace before
+ // dumpstate starts. Otherwise the trace ring buffers will contain mostly
+ // the dumpstate's own activity which is irrelevant.
+ int res = RunCommand(
+ "SERIALIZE PERFETTO TRACE",
+ {"perfetto", "--save-for-bugreport"},
+ CommandOptions::WithTimeout(10)
+ .DropRoot()
+ .CloseAllFileDescriptorsOnExec()
+ .Build());
+ has_system_trace_ = res == 0;
+ // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
+ // file in the later stages.
+}
+
void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
if (calling_uid == AID_SHELL || !CalledByApi()) {
return;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 255243f..f83968b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -458,6 +458,11 @@
// Whether it should take an screenshot earlier in the process.
bool do_early_screenshot_ = false;
+ // This is set to true when the trace snapshot request in the early call to
+ // MaybeSnapshotSystemTrace(). When this is true, the later stages of
+ // dumpstate will append the trace to the zip archive.
+ bool has_system_trace_ = false;
+
std::unique_ptr<Progress> progress_;
// When set, defines a socket file-descriptor use to report progress to bugreportz
@@ -543,6 +548,7 @@
RunStatus DumpstateDefaultAfterCritical();
void MaybeTakeEarlyScreenshot();
+ void MaybeSnapshotSystemTrace();
void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index fe6a34a..0e366cb 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -319,6 +319,16 @@
*/
class BugreportSectionTest : public Test {
public:
+ ZipArchiveHandle handle;
+
+ void SetUp() {
+ ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
+ }
+
+ void TearDown() {
+ CloseArchive(handle);
+ }
+
static void SetUpTestCase() {
ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
ZippedBugreportGenerationTest::sections.get());
@@ -343,6 +353,19 @@
}
FAIL() << sectionName << " not found.";
}
+
+ /**
+ * Whether or not the content of the section is injected by other commands.
+ */
+ bool IsContentInjectedByOthers(const std::string& line) {
+ // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`.
+ static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"};
+ std::smatch match;
+ if (std::regex_match(line, match, kCommandHeader)) {
+ return true;
+ }
+ return false;
+ }
};
TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
@@ -400,6 +423,28 @@
SectionExists("wifi", /* bytes= */ 100000);
}
+TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) {
+ // Extract the main entry to a temp file
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ExtractBugreport(&handle, tmp_binary.fd);
+
+ // Read line by line and identify sections
+ std::ifstream ifs(tmp_binary.path, std::ifstream::in);
+ std::string line;
+ std::string current_section_name;
+ while (std::getline(ifs, line)) {
+ std::string section_name;
+ if (IsSectionStart(line, §ion_name)) {
+ current_section_name = section_name;
+ } else if (IsSectionEnd(line)) {
+ current_section_name = "";
+ } else if (!current_section_name.empty()) {
+ EXPECT_FALSE(IsContentInjectedByOthers(line));
+ }
+ }
+}
+
class DumpstateBinderTest : public Test {
protected:
void SetUp() override {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 1327cfd..a017246 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -427,7 +427,7 @@
<< strerror(errno) << std::endl;
status = -errno;
break;
- } else if (rc == 0) {
+ } else if (rc == 0 || time_left_ms() == 0) {
status = TIMED_OUT;
break;
}
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 64bfdf9..643b3ca 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -15,7 +15,7 @@
cc_defaults {
name: "idlcli-defaults",
shared_libs: [
- "android.hardware.vibrator-ndk_platform",
+ "android.hardware.vibrator-unstable-ndk_platform",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/data/etc/android.software.translation.xml b/data/etc/android.software.translation.xml
new file mode 100644
index 0000000..3b361e5
--- /dev/null
+++ b/data/etc/android.software.translation.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <feature name="android.software.translation" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index dc6963f..2c34047 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,7 @@
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
+ <feature name="android.software.translation" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index e878f86..873b5b7 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -52,6 +52,7 @@
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
+ <feature name="android.software.translation" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index f195399..d7f25e1 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -28,8 +28,23 @@
#include <stdbool.h>
#include <stdint.h>
+#include <stddef.h>
#include <jni.h>
+#ifndef __ANDROID__
+ // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
+ // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
+ #ifndef __ANDROID_API__
+ #define __ANDROID_API__ 10000
+ #endif
+
+ // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
+ // non Android systems
+ #ifndef __INTRODUCED_IN
+ #define __INTRODUCED_IN(api_level)
+ #endif
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -152,8 +167,6 @@
*/
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
-#if __ANDROID_API__ >= 30
-
// Note: these values match android.graphics.Bitmap#compressFormat.
/**
@@ -203,6 +216,8 @@
const void* data,
size_t size) __INTRODUCED_IN(30);
+#if __ANDROID_API__ >= 30
+
/**
* Compress |pixels| as described by |info|.
*
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 49a616f..c7a8939 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -51,6 +51,20 @@
#include <android/rect.h>
#include <stdint.h>
+#ifndef __ANDROID__
+ // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
+ // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
+ #ifndef __ANDROID_API__
+ #define __ANDROID_API__ 10000
+ #endif
+
+ // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
+ // non Android systems
+ #ifndef __INTRODUCED_IN
+ #define __INTRODUCED_IN(api_level)
+ #endif
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -65,7 +79,8 @@
* Many functions will return this to indicate success
* ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason for the failure. On
* failure, any out-parameters should be considered uninitialized, except where
- * specified.
+ * specified. Use {@link AImageDecoder_resultToString} for a readable
+ * version of the result code.
*/
enum {
/**
@@ -109,9 +124,39 @@
/**
* AImageDecoder did not recognize the format.
*/
- ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9
+ ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9,
+ /**
+ * The animation has reached the end.
+ */
+ ANDROID_IMAGE_DECODER_FINISHED = -10,
+ /**
+ * This method cannot be called while the AImageDecoder is in its current
+ * state. For example, various setters (like {@link AImageDecoder_setTargetSize})
+ * can only be called while the AImageDecoder is set to decode the first
+ * frame of an animation. This ensures that any blending and/or restoring
+ * prior frames works correctly.
+ */
+ ANDROID_IMAGE_DECODER_INVALID_STATE = -11,
};
+#if __ANDROID_API__ >= 31
+
+/**
+ * Return a constant string value representing the error code.
+ *
+ * Introduced in API 31.
+ *
+ * Pass the return value from an {@link AImageDecoder} method (e.g.
+ * {@link AImageDecoder_decodeImage}) for a text string representing the error
+ * code.
+ *
+ * Errors:
+ * - Returns null for a value out of range.
+ */
+const char* _Nullable AImageDecoder_resultToString(int)__INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
struct AImageDecoder;
/**
@@ -127,7 +172,7 @@
* After creation, {@link AImageDecoder_getHeaderInfo} can be used to retrieve
* information about the encoded image. Other functions, like
* {@link AImageDecoder_setTargetSize}, can be used to specify how to decode, and
- * {@link AImageDecoder_decode} will decode into client provided memory.
+ * {@link AImageDecoder_decodeImage} will decode into client provided memory.
*
* {@link AImageDecoder} objects are NOT thread-safe, and should not be shared across
* threads.
@@ -165,7 +210,7 @@
* supported.
*/
int AImageDecoder_createFromAAsset(struct AAsset* _Nonnull asset,
- AImageDecoder* _Nonnull * _Nonnull outDecoder)
+ AImageDecoder* _Nullable * _Nonnull outDecoder)
__INTRODUCED_IN(30);
/**
@@ -196,7 +241,7 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nonnull * _Nonnull outDecoder)
+int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nullable * _Nonnull outDecoder)
__INTRODUCED_IN(30);
/**
@@ -227,7 +272,7 @@
* supported.
*/
int AImageDecoder_createFromBuffer(const void* _Nonnull buffer, size_t length,
- AImageDecoder* _Nonnull * _Nonnull outDecoder)
+ AImageDecoder* _Nullable * _Nonnull outDecoder)
__INTRODUCED_IN(30);
/**
@@ -235,11 +280,15 @@
*
* Available since API level 30.
*/
-void AImageDecoder_delete(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
+void AImageDecoder_delete(AImageDecoder* _Nullable decoder) __INTRODUCED_IN(30);
/**
* Choose the desired output format.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param format {@link AndroidBitmapFormat} to use for the output.
@@ -255,6 +304,8 @@
* {@link AndroidBitmapFormat}.
* - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
* {@link AndroidBitmapFormat} is incompatible with the image.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* _Nonnull decoder,
int32_t format) __INTRODUCED_IN(30);
@@ -266,6 +317,10 @@
* Pass true to this method to leave them unpremultiplied. This has no effect on an
* opaque image.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
@@ -278,6 +333,8 @@
* {@link AImageDecoder_setTargetSize}.
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* _Nonnull decoder,
bool unpremultipliedRequired) __INTRODUCED_IN(30);
@@ -288,6 +345,10 @@
* Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
* an {@link ADataSpace}.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
@@ -303,6 +364,8 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null or |dataspace| does not correspond to an
* {@link ADataSpace} value.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
int AImageDecoder_setDataSpace(AImageDecoder* _Nonnull decoder, int32_t dataspace)
__INTRODUCED_IN(30);
@@ -316,6 +379,10 @@
* specified by width and height, and the output image will be the size of the
* crop rect.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param width Width of the output (prior to cropping).
@@ -330,9 +397,11 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null.
* - {@link ANDROID_IMAGE_DECODER_INVALID_SCALE}: |width| or |height| is <= 0,
- * the size is too big, any existing crop is not contained by the new image dimensions,
- * or the scale is incompatible with a previous call to
+ * the size is too big, any existing crop is not contained by the new image
+ * dimensions, or the scale is incompatible with a previous call to
* {@link AImageDecoder_setUnpremultipliedRequired}(true).
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width,
int32_t height) __INTRODUCED_IN(30);
@@ -374,6 +443,10 @@
* the specified {@link ARect}. Clients will only need to allocate enough memory
* for the cropped ARect.
*
+ * If the encoded image represents an animation, this must be called while on
+ * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
+ * after calling {@link AImageDecoder_rewind}).
+ *
* Available since API level 30.
*
* @param crop Rectangle describing a crop of the decode. It must be contained inside of
@@ -389,8 +462,10 @@
*
* Errors:
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
- * {@link AImageDecoder} is null or the crop is not contained by the
+ * {@link AImageDecoder} is null, or the crop is not contained by the
* (possibly scaled) image dimensions.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE}: The animation is not on
+ * the first frame.
*/
int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30);
@@ -472,6 +547,10 @@
* {@link AImageDecoder_decodeImage} will premultiply pixels by default.
*
* Available since API level 30.
+ *
+ * Starting in API level 31, an AImageDecoder may contain multiple frames of an
+ * animation, but this method still only reports whether the first frame has
+ * alpha.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
@@ -518,8 +597,45 @@
* Available since API level 30.
*
* Starting in API level 31, it can be used to decode all of the frames of an
- * animated image (i.e. GIF, WebP, HEIF) using new APIs (TODO (scroggo): list
- * and describe here).
+ * animated image (i.e. GIF, WebP) using new APIs. Internally,
+ * AImageDecoder keeps track of its "current frame" - that is, the frame that
+ * will be decoded by a call to AImageDecoder_decodeImage. At creation time, the
+ * current frame is always the first frame, and multiple calls to this method
+ * will each decode the first frame. {@link AImageDecoder_advanceFrame} advances
+ * the current frame to the following frame, so that future calls to this method
+ * will decode that frame. Some frames may update only part of the image. They
+ * may only update a sub-rectangle (see {@link
+ * AImageDecoderFrameInfo_getFrameRect}), or they may have alpha (see
+ * {@link AImageDecoderFrameInfo_hasAlphaWithinBounds}). In these cases, this
+ * method assumes that the prior frame is still residing in the |pixels| buffer,
+ * decodes only the new portion, and blends it with the buffer. Frames that change
+ * the entire |pixels| buffer are "independent", and do not require the prior
+ * frame to remain in the buffer. The first frame is always independent. A
+ * sophisticated client can use information from the {@link AImageDecoderFrameInfo}
+ * to determine whether other frames are independent, or what frames they rely on.
+ *
+ * If the current frame is marked {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS},
+ * AImageDecoder_decodeImage will store the |pixels| buffer prior to decoding
+ * (note: this only happens for the first in a string of consecutive
+ * ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS frames). After advancing to the
+ * following frame, AImageDecoder_decodeImage will restore that buffer prior to
+ * decoding that frame. This is the default behavior, but it can be disabled
+ * by passing false to {@link AImageDecoder_setInternallyHandleDisposePrevious}.
+ *
+ * Ignoring timing information, display, etc, a client wishing to decode all
+ * frames of an animated image may conceptually use code like the following:
+ *
+ * while (true) {
+ * int result = AImageDecoder_decodeImage(decoder, pixels, stride, size);
+ * if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+ *
+ * // Display or save the image in |pixels|, keeping the buffer intact for
+ * // AImageDecoder to decode the next frame correctly.
+ * Application_viewImage(pixels);
+ *
+ * result = AImageDecoder_advanceFrame(decoder);
+ * if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
+ * }
*
* @param decoder Opaque object representing the decoder.
* @param pixels On success, will be filled with the result
@@ -547,6 +663,10 @@
* failed to seek.
* - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
* failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. No decoding occurred. The client must call
+ * {@link AImageDecoder_rewind} before calling
+ * {@link AImageDecoder_decodeImage} again.
*/
int AImageDecoder_decodeImage(AImageDecoder* _Nonnull decoder,
void* _Nonnull pixels, size_t stride,
@@ -561,8 +681,8 @@
*
* Introduced in API 31.
*
- * This may require seeking past the first frame to verify whether
- * there is a following frame (e.g. for GIF).
+ * A single frame GIF is considered to *not* be animated. This may require
+ * seeking past the first frame to verify whether there is a following frame.
*
* Errors:
* - returns false if |decoder| is null.
@@ -607,9 +727,326 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
* is null.
*/
-int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder);
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder)
__INTRODUCED_IN(31);
+/**
+ * Advance to the next frame in the animation.
+ *
+ * Introduced in API 31.
+ *
+ * The AImageDecoder keeps track internally which frame it is ready to decode
+ * (the "current frame"). Initially it is set to decode the first frame, and
+ * each call to {@link AImageDecoder_decodeImage} will continue to decode
+ * the same frame until this method (or {@link AImageDecoder_rewind})
+ * is called.
+ *
+ * Note that this can be used to skip a frame without decoding it. But
+ * some frames depend on (i.e. blend with) prior frames, and
+ * AImageDecoder_decodeImage assumes that the prior frame is in the
+ * |pixels| buffer. In addition, AImageDecoder_decodeImage handles caching and
+ * restoring frames (see {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}), so
+ * skipping frames in an image with such frames may not produce the correct
+ * results.
+ *
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * represents an image that is not animated (see
+ * {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
+ * to be truncated. The client must call {@link AImageDecoder_rewind}
+ * before calling {@link AImageDecoder_decodeImage} again.
+ * - {@link ANDROID_IMAGE_DECODER_ERROR}: The input contains an error.
+ * The client must call {@link AImageDecoder_rewind} before
+ * calling {@link AImageDecoder_decodeImage} again.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. The client must call {@link AImageDecoder_rewind}
+ * before calling {@link AImageDecoder_decodeImage} again.
+ */
+int AImageDecoder_advanceFrame(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+/**
+ * Return to the beginning of the animation.
+ *
+ * Introduced in API 31.
+ *
+ * After this call, the AImageDecoder will be ready to decode the
+ * first frame of the animation. This can be called after reaching
+ * the end of the animation or an error or in the middle of the
+ * animation.
+ *
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * represents an image that is not animated (see
+ * {@link AImageDecoder_isAnimated}) or the AImageDecoder is
+ * null.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset or file
+ * descriptor failed to seek.
+ */
+int AImageDecoder_rewind(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+struct AImageDecoderFrameInfo;
+
+/**
+ * Opaque handle to animation information about a single frame.
+ *
+ * Introduced in API 31
+ *
+ * The duration (retrieved with {@link AImageDecoderFrameInfo_getDuration}) is
+ * necessary for clients to display the animation at the proper speed. The other
+ * information is helpful for a client that wants to determine what frames are
+ * independent (or what frames they depend on), but is unnecessary for
+ * a simple client that wants to sequentially display all frames.
+ */
+typedef struct AImageDecoderFrameInfo AImageDecoderFrameInfo;
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Create an uninitialized AImageDecoderFrameInfo.
+ *
+ * Introduced in API 31.
+ *
+ * This can be passed to {@link AImageDecoder_getFrameInfo} to fill
+ * in information about the current frame. It may be reused.
+ *
+ * Must be deleted with {@link AImageDecoderFrameInfo_delete}.
+ */
+AImageDecoderFrameInfo* _Nullable AImageDecoderFrameInfo_create()
+ __INTRODUCED_IN(31);
+
+/**
+ * Delete an AImageDecoderFrameInfo.
+ *
+ * Introduced in API 31.
+ */
+void AImageDecoderFrameInfo_delete(
+ AImageDecoderFrameInfo* _Nullable info) __INTRODUCED_IN(31);
+
+/**
+ * Fill |info| with information about the current frame.
+ *
+ * Introduced in API 31.
+ *
+ * Initially, this will return information about the first frame.
+ * {@link AImageDecoder_advanceFrame} and
+ * {@link AImageDecoder_rewind} can be used to change which frame
+ * is the current frame.
+ *
+ * If the image only has one frame, this will fill the {@link
+ * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable
+ * defaults.
+ *
+ * @param decoder Opaque object representing the decoder.
+ * @param info Opaque object to hold frame information. On success, will be
+ * filled with information regarding the current frame.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is null.
+ * - {@link ANDROID_IMAGE_DECODER_FINISHED}: The input contains no
+ * more frames. The client must call {@link AImageDecoder_rewind} to reset the
+ * current frame to a valid frame (0).
+ */
+int AImageDecoder_getFrameInfo(AImageDecoder* _Nonnull decoder,
+ AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * Report the number of nanoseconds to show the current frame.
+ *
+ * Introduced in API 31.
+ *
+ * Errors:
+ * - returns 0 if |info| is null.
+ */
+int64_t AImageDecoderFrameInfo_getDuration(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * The rectangle of the image (within 0, 0,
+ * {@link AImageDecoder_getWidth}, {@link AImageDecoder_getHeight})
+ * updated by this frame.
+ *
+ * Introduced in API 31.
+ *
+ * Note that this is unaffected by calls to
+ * {@link AImageDecoder_setTargetSize} or
+ * {@link AImageDecoder_setCrop}.
+ *
+ * A frame may update only part of the image. This will always be
+ * contained by the image’s dimensions.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * Errors:
+ * - returns an empty ARect if |info| is null.
+ */
+ARect AImageDecoderFrameInfo_getFrameRect(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+/**
+ * Whether the new portion of this frame may contain alpha.
+ *
+ * Introduced in API 31.
+ *
+ * Note that this may differ from whether the composed frame has
+ * alpha. If this frame does not fill the entire image dimensions
+ * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends
+ * with an opaque frame, for example, the composed frame’s alpha
+ * may not match. It is also conservative; for example, if a color
+ * index-based frame has a color with alpha but does not use it,
+ * this will still return true.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * Errors:
+ * - returns false if |info| is null.
+ */
+bool AImageDecoderFrameInfo_hasAlphaWithinBounds(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+/**
+ * How a frame is “disposed” before showing the next one.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles disposing of frames, so a simple
+ * sequential client does not need this.
+ */
+enum {
+ // No disposal. The following frame will be drawn directly
+ // on top of this one.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE = 1,
+ // The frame’s rectangle is cleared to transparent (by AImageDecoder)
+ // before decoding the next frame.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND = 2,
+ // The frame’s rectangle is reverted to the prior frame before decoding
+ // the next frame. This is handled by AImageDecoder, unless
+ // {@link AImageDecoder_setInternallyHandleDisposePrevious} is set to false.
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS = 3,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Return how this frame is “disposed” before showing the next one.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles disposing of frames, so a simple
+ * sequential client does not need this.
+ *
+ * @return one of:
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE}
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND}
+ * - {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
+ */
+int32_t AImageDecoderFrameInfo_getDisposeOp(
+ const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+/**
+ * How a frame is blended with the previous frame.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ */
+enum {
+ // This frame replaces existing content. This corresponds
+ // to webp’s “do not blend”.
+ ANDROID_IMAGE_DECODER_BLEND_OP_SRC = 1,
+ // This frame blends with the previous frame.
+ ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER = 2,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Return how this frame is blended with the previous frame.
+ *
+ * Introduced in API 31.
+ *
+ * This, along with other information in AImageDecoderFrameInfo,
+ * can be useful for determining whether a frame is independent, but
+ * the decoder handles blending frames, so a simple
+ * sequential client does not need this.
+ *
+ * @return one of:
+ * - {@link ANDROID_IMAGE_DECODER_BLEND_OP_SRC}
+ * - {@link ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER}
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
+ */
+int32_t AImageDecoderFrameInfo_getBlendOp(
+ const AImageDecoderFrameInfo* _Nonnull info)
+ __INTRODUCED_IN(31);
+
+/**
+ * Whether to have AImageDecoder store the frame prior to a
+ * frame marked {@link ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS}.
+ *
+ * Introduced in API 31.
+ *
+ * The default is true. Many images will not have such a frame (it
+ * is not supported by WebP, and only some GIFs use it). But
+ * if frame i is ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS, then i+1
+ * may depend on i-1. When this setting is true, AImageDecoder will
+ * defensively copy frame i-1 (i.e. the contents of |pixels| in
+ * {@link AImageDecoder_decodeImage}) into an internal buffer so that
+ * it can be used to decode i+1.
+ *
+ * AImageDecoder will only store a single frame, at the size specified
+ * by {@link AImageDecoder_setTargetSize} (or the original dimensions
+ * if that method has not been called), and will discard it when it is
+ * no longer necessary.
+ *
+ * A client that desires to manually store such frames may set this to
+ * false, so that AImageDecoder does not need to store this extra
+ * frame. Instead, when decoding the same
+ * ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS frame i, AImageDecoder
+ * will decode directly into |pixels|, assuming the client stored i-1.
+ * When asked to decode frame i+1, AImageDecoder will now assume that
+ * the client provided i-1 in |pixels|.
+ *
+ * @param handleInternally Whether AImageDecoder will internally
+ * handle ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS
+ * frames.
+ */
+void AImageDecoder_setInternallyHandleDisposePrevious(
+ AImageDecoder* _Nonnull decoder, bool handleInternally)
+ __INTRODUCED_IN(31);
+
+
#endif // __ANDROID_API__ >= 31
#ifdef __cplusplus
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
index 7817126..2d5d060 100644
--- a/include/android/permission_manager.h
+++ b/include/android/permission_manager.h
@@ -64,8 +64,6 @@
PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
};
-#if __ANDROID_API__ >= 31
-
/**
* Checks whether the package with the given pid/uid has been granted a permission.
*
@@ -84,8 +82,6 @@
uid_t uid,
int32_t* outResult) __INTRODUCED_IN(31);
-#endif // __ANDROID_API__ >= 31
-
__END_DECLS
#endif // ANDROID_PERMISSION_MANAGER_H
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 83582d6..eb81534 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -53,7 +53,7 @@
#include <sys/types.h>
#if !defined(__INTRODUCED_IN)
-#define __INTRODUCED_IN(30) /* Introduced in API level 30 */
+#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
#ifdef __cplusplus
@@ -181,6 +181,51 @@
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Provides an estimate of how much thermal headroom the device currently has before
+ * hitting severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors, such as
+ * the skin temperature sensor. This means that there is no benefit to calling this function
+ * more frequently than about once per second, and attempted to call significantly
+ * more frequently may result in the function returning {@code NaN}.
+ *
+ * In addition, in order to be able to provide an accurate forecast, the system does
+ * not attempt to forecast until it has multiple temperature samples from which to
+ * extrapolate. This should only take a few seconds from the time of the first call,
+ * but during this time, no forecasting will occur, and the current headroom will be
+ * returned regardless of the value of {@code forecastSeconds}.
+ *
+ * The value returned is a non-negative float that represents how much of the thermal envelope
+ * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is
+ * (or will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the
+ * CPU, GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping
+ * to specific thermal levels beyond that point. This means that values greater than 1.0
+ * may correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+ *
+ * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+ * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+ * linearly with temperature, though temperature changes over time are typically not linear.
+ * Negative values will be clamped to 0.0 before returning.
+ *
+ * Available since API level 31.
+ *
+ * @param manager The manager instance to use.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param forecastSeconds how many seconds into the future to forecast. Given that device
+ * conditions may change at any time, forecasts from further in the
+ * future will likely be less accurate than forecasts in the near future.
+ * @return a value greater than equal to 0.0, where 1.0 indicates the SEVERE throttling threshold,
+ * as described above. Returns NaN if the device does not support this functionality or
+ * if this function is called significantly faster than once per second.
+ */
+float AThermal_getThermalHeadroom(AThermalManager *manager,
+ int forecastSeconds) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
#ifdef __cplusplus
}
#endif
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 639df7a..4aa2f60 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -37,6 +37,7 @@
PLAYER_STATE_STARTED = 2,
PLAYER_STATE_PAUSED = 3,
PLAYER_STATE_STOPPED = 4,
+ PLAYER_UPDATE_DEVICE_ID = 5,
} player_state_t;
// must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 2f5ccb8..7d1f38f 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -49,7 +49,8 @@
audio_content_type_t content, const sp<IBinder>& player) = 0;
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
- /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+ /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+ audio_port_handle_t deviceId) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index feaea63..9ea9732 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -179,15 +179,26 @@
"--header-filter=^.*frameworks/native/libs/binder/.*.h$",
],
tidy_checks_as_errors: [
- "*",
+ // Explicitly list the checks that should not occur in this module.
+ "abseil-*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-*",
"-clang-analyzer-core.CallAndMessage",
"-clang-analyzer-core.uninitialized.Assign",
- "-clang-analyzer-unix.Malloc,",
+ "-clang-analyzer-unix.Malloc",
"-clang-analyzer-deadcode.DeadStores",
"-clang-analyzer-optin.cplusplus.UninitializedObject",
+ "google-*",
+ "-google-readability-*",
+ "-google-runtime-references",
+ "misc-*",
"-misc-no-recursion",
"-misc-redundant-expression",
"-misc-unused-using-decls",
+ "performance*",
+ "portability*",
],
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 87eab52..440c98c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1477,6 +1477,31 @@
goto data_sorted;
}
+status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const {
+ int32_t requestedSize;
+ const status_t status = readInt32(&requestedSize);
+ if (status != NO_ERROR) return status;
+
+ // We permit negative sizes, which indicate presence of a nullable vector,
+ // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr.
+ if (requestedSize > 0) {
+ // Check if there are fewer bytes than vector elements.
+ // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t.
+ const size_t availableBytes = dataAvail();
+ if (static_cast<size_t>(requestedSize) > availableBytes) {
+ // We have a size that is greater than the number of bytes available.
+ // On bounds failure we do not 'rewind' position by 4 bytes of the size already read.
+ ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d "
+ "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}",
+ __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity);
+ return BAD_VALUE;
+ }
+ }
+
+ *size = requestedSize;
+ return NO_ERROR;
+}
+
status_t Parcel::read(void* outData, size_t len) const
{
if (len > INT32_MAX) {
@@ -1699,7 +1724,7 @@
status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1721,7 +1746,7 @@
status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1742,7 +1767,7 @@
status_t Parcel::readBoolVector(std::vector<bool>* val) const {
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
if (status != OK) {
return status;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index b2b8671..2e86b74 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -37,7 +37,7 @@
size_t sizePos = p->dataPosition();
RETURN_ON_FAILURE(p->writeInt32(0));
size_t dataStartPos = p->dataPosition();
- RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+ RETURN_ON_FAILURE(p->writeString16(this->mParcelableName));
this->mParcelable->writeToParcel(p);
size_t dataSize = p->dataPosition() - dataStartPos;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 97e282e..00a14f4 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -10,6 +10,9 @@
"name": "binderAllocationLimits"
},
{
+ "name": "binderClearBufTest"
+ },
+ {
"name": "binderDriverInterfaceTest"
},
{
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 988508e..e2a0f87 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -232,7 +232,6 @@
"android.gui.IGraphicBufferConsumer",
"android.gui.IRegionSamplingListener",
"android.gui.ITransactionComposerListener",
- "android.gui.IScreenCaptureListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
"android.hardware.ICamera",
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b49951b..54c49e4 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -517,6 +517,11 @@
void initState();
void scanForFds() const;
status_t validateReadData(size_t len) const;
+
+ // Reads an int32 size and does a coarse bounds check against the number
+ // of available bytes in the Parcel.
+ status_t readVectorSizeWithCoarseBoundCheck(int32_t *size) const;
+
void updateWorkSourceRequestHeaderPosition() const;
status_t finishFlattenBinder(const sp<IBinder>& binder);
@@ -787,6 +792,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::vector<T>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -802,6 +808,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -818,6 +825,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -834,7 +842,7 @@
template<typename T>
status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -850,7 +858,7 @@
template<typename T>
status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -870,7 +878,7 @@
status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -923,7 +931,7 @@
std::vector<T>* val,
status_t(Parcel::*read_func)(U*) const) const {
int32_t size;
- status_t status = this->readInt32(&size);
+ status_t status = this->readVectorSizeWithCoarseBoundCheck(&size);
if (status != OK) {
return status;
@@ -965,7 +973,7 @@
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -989,7 +997,7 @@
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1093,7 +1101,7 @@
status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1117,7 +1125,7 @@
status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index ff0a686..9e4475c 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -18,6 +18,7 @@
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <utils/String16.h>
#include <mutex>
#include <optional>
#include <tuple>
@@ -72,7 +73,7 @@
template <typename T>
status_t getParcelable(std::shared_ptr<T>* ret) const {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
- const std::string& parcelableDesc = T::getParcelableDescriptor();
+ const String16& parcelableDesc = T::getParcelableDescriptor();
if (!this->mParcelPtr) {
if (!this->mParcelable || !this->mParcelableName) {
ALOGD("empty ParcelableHolder");
@@ -80,7 +81,7 @@
return android::OK;
} else if (parcelableDesc != *mParcelableName) {
ALOGD("extension class name mismatch expected:%s actual:%s",
- mParcelableName->c_str(), parcelableDesc.c_str());
+ String8(*mParcelableName).c_str(), String8(parcelableDesc).c_str());
*ret = nullptr;
return android::BAD_VALUE;
}
@@ -88,7 +89,7 @@
return android::OK;
}
this->mParcelPtr->setDataPosition(0);
- status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+ status_t status = this->mParcelPtr->readString16(&this->mParcelableName);
if (status != android::OK || parcelableDesc != this->mParcelableName) {
this->mParcelableName = std::nullopt;
*ret = nullptr;
@@ -130,7 +131,7 @@
private:
mutable std::shared_ptr<Parcelable> mParcelable;
- mutable std::optional<std::string> mParcelableName;
+ mutable std::optional<String16> mParcelableName;
mutable std::unique_ptr<Parcel> mParcelPtr;
Stability mStability;
};
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index bdb74dc..82f3882 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -104,15 +104,28 @@
"--header-filter=^.*frameworks/native/libs/binder/.*.h$",
],
tidy_checks_as_errors: [
- "*",
+ // Explicitly list the checks that should not occur in this module.
+ "abseil-*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-*",
"-clang-analyzer-core.CallAndMessage",
"-clang-analyzer-core.uninitialized.Assign",
- "-clang-analyzer-unix.Malloc,",
+ "-clang-analyzer-unix.Malloc",
"-clang-analyzer-deadcode.DeadStores",
"-clang-analyzer-optin.cplusplus.UninitializedObject",
+ "google-*",
+ "-google-readability-*",
+ "-google-runtime-references",
+ "misc-*",
"-misc-no-recursion",
+ "-misc-non-private-member-variables-in-classes",
"-misc-redundant-expression",
+ "-misc-unused-parameters",
"-misc-unused-using-decls",
+ "performance*",
+ "portability*",
],
}
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 350c658..454fbd0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -301,6 +301,26 @@
return binder.get();
}
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak) {
+ if (weak == nullptr) {
+ return nullptr;
+ }
+
+ return new AIBinder_Weak{weak->binder};
+}
+
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs) {
+ if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+ return const_cast<AIBinder*>(lhs)->getBinder() < const_cast<AIBinder*>(rhs)->getBinder();
+}
+
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) {
+ if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+ return lhs->binder < rhs->binder;
+}
+
AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
AIBinder_Class_onDestroy onDestroy,
AIBinder_Class_onTransact onTransact)
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index a4f4441..a1102e2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -83,7 +83,8 @@
template <class T, class... Args>
static std::shared_ptr<T> make(Args&&... args) {
T* t = new T(std::forward<Args>(args)...);
- return t->template ref<T>();
+ // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
+ return t->template ref<T>(); // NOLINT(clang-analyzer-unix.Malloc)
}
static void operator delete(void* p) { std::free(p); }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 5e1ed46..0ca3a07 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -657,6 +657,68 @@
*/
const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
+/**
+ * Whether AIBinder is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders determined by
+ * an underlying allocation address where a null AIBinder* is considered to be
+ * ordered before all other binders.
+ *
+ * AIBinder* pointers themselves actually also create a per-process-unique total
+ * ordering. However, this ordering is inconsistent with AIBinder_Weak_lt for
+ * remote binders.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs);
+
+/**
+ * Clone an AIBinder_Weak. Useful because even if a weak binder promotes to a
+ * null value, after further binder transactions, it may no longer promote to a
+ * null value.
+ *
+ * Available since API level 31.
+ *
+ * \param weak Object to clone
+ *
+ * \return clone of the input parameter. This must be deleted with
+ * AIBinder_Weak_delete. Null if weak input parameter is also null.
+ */
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak);
+
+/**
+ * Whether AIBinder_Weak is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders which is exactly
+ * the same as AIBinder_lt. Similarly, a null AIBinder_Weak* is considered to be
+ * ordered before all other weak references.
+ *
+ * If you have many AIBinder_Weak* objects which are all references to distinct
+ * binder objects which happen to have the same underlying address (as ordered
+ * by AIBinder_lt), these AIBinder_Weak* objects will retain the same order with
+ * respect to all other AIBinder_Weak* pointers with different underlying
+ * addresses and are also guaranteed to have a per-process-unique ordering. That
+ * is, even though multiple AIBinder* instances may happen to be allocated at
+ * the same underlying address, this function will still correctly distinguish
+ * that these are weak pointers to different binder objects.
+ *
+ * Unlike AIBinder*, the AIBinder_Weak* addresses themselves have nothing to do
+ * with the underlying binder.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs);
+
#endif //__ANDROID_API__ >= 31
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index e233ffd..9a93bf3 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -122,6 +122,9 @@
AServiceManager_waitForService; # apex llndk
AIBinder_Class_getDescriptor;
+ AIBinder_Weak_clone;
+ AIBinder_Weak_lt;
+ AIBinder_lt;
AParcel_appendFrom;
AParcel_create;
AParcel_getDataSize;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 87f1d45..988f7f3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -157,6 +157,24 @@
require_root: true,
}
+cc_test {
+ name: "binderClearBufTest",
+ defaults: ["binder_test_defaults"],
+ srcs: [
+ "binderClearBufTest.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
aidl_interface {
name: "binderStabilityTestIface",
unstable: true,
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
new file mode 100644
index 0000000..a565e72
--- /dev/null
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <gtest/gtest.h>
+
+#include <sys/prctl.h>
+#include <thread>
+
+using namespace android;
+
+const String16 kServerName = String16("binderClearBuf");
+
+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;
+}
+
+class FooBar : public BBinder {
+ public:
+ enum {
+ TRANSACTION_REPEAT_STRING = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+ std::mutex foo;
+ std::string last;
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ // not checking data, since there is no hook at the time this test is
+ // written to check values there are set to zero. Instead, we only check
+ // the reply parcel.
+
+ switch (code) {
+ case TRANSACTION_REPEAT_STRING: {
+ const char* str = data.readCString();
+ return reply->writeCString(str == nullptr ? "<null>" : str);
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ static std::string RepeatString(const sp<IBinder> binder,
+ const std::string& repeat,
+ std::string* outBuffer) {
+ Parcel data;
+ data.writeCString(repeat.c_str());
+ std::string result;
+ const uint8_t* lastReply;
+ size_t lastReplySize;
+ {
+ Parcel reply;
+ binder->transact(TRANSACTION_REPEAT_STRING, data, &reply, FLAG_CLEAR_BUF);
+ result = reply.readCString();
+ lastReply = reply.data();
+ lastReplySize = reply.dataSize();
+ }
+ IPCThreadState::self()->flushCommands();
+ *outBuffer = hexString(lastReply, lastReplySize);
+ return result;
+ }
+};
+
+TEST(BinderClearBuf, ClearKernelBuffer) {
+ sp<IBinder> binder = defaultServiceManager()->getService(kServerName);
+ ASSERT_NE(nullptr, binder);
+
+ std::string replyBuffer;
+ std::string result = FooBar::RepeatString(binder, "foo", &replyBuffer);
+ EXPECT_EQ("foo", result);
+
+ // the buffer must have at least some length for the string, but we will
+ // just check it has some length, to avoid assuming anything about the
+ // format
+ EXPECT_GT(replyBuffer.size(), 0);
+
+ for (size_t i = 0; i < replyBuffer.size(); i++) {
+ EXPECT_EQ(replyBuffer[i], '0') << "reply buffer at " << i;
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+ sp<IBinder> server = new FooBar;
+ android::defaultServiceManager()->addService(kServerName, server);
+
+ IPCThreadState::self()->joinThreadPool(true);
+ exit(1); // should not reach
+ }
+
+ // This is not racey. Just giving these services some time to register before we call
+ // getService which sleeps for much longer. One alternative would be to
+ // start a threadpool + use waitForService, but we want to have as few
+ // binder things going on in this test as possible, since we are checking
+ // memory is zero'd which the kernel has a right to change.
+ usleep(100000);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
index c465bed..5531296 100644
--- a/libs/binder/tests/fuzzers/Android.bp
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -69,3 +69,18 @@
defaults: ["binder_fuzz_defaults"],
srcs: ["TextOutputFuzz.cpp"],
}
+
+cc_fuzz {
+ name: "binder_bufferedTextOutputFuzz",
+ include_dirs: [
+ "frameworks/native/libs/binder",
+ ],
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["BufferedTextOutputFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_memoryDealerFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["MemoryDealerFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
new file mode 100644
index 0000000..09cb216
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <vector>
+#include "BufferedTextOutput.h"
+
+namespace android {
+
+class FuzzBufferedTextOutput : public BufferedTextOutput {
+public:
+ FuzzBufferedTextOutput(uint32_t flags) : BufferedTextOutput(flags) {}
+ virtual status_t writeLines(const struct iovec& buf, size_t) {
+ size_t len = buf.iov_len;
+ void* tmp_buf = malloc(len);
+
+ if (tmp_buf == NULL) {
+ return status_t();
+ }
+
+ // This will attempt to read data from iov_base to ensure valid params were passed.
+ memcpy(tmp_buf, buf.iov_base, len);
+ free(tmp_buf);
+ return status_t();
+ }
+};
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+ size_t push_count = 0;
+ std::shared_ptr<BufferedTextOutput> bTextOutput(new FuzzBufferedTextOutput(flags));
+
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ bTextOutput->pushBundle();
+ push_count++;
+ },
+ [&]() -> void {
+ std::string txt = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ size_t len = fdp.ConsumeIntegralInRange<size_t>(0, txt.length());
+ bTextOutput->print(txt.c_str(), len);
+ },
+ [&]() -> void {
+ if (push_count == 0) return;
+
+ bTextOutput->popBundle();
+ push_count--;
+ },
+ })();
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
new file mode 100644
index 0000000..f9dda8c
--- /dev/null
+++ b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/MemoryDealer.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <unordered_set>
+
+namespace android {
+
+static constexpr size_t kMaxBufferSize = 10000;
+static constexpr size_t kMaxDealerSize = 1024 * 512;
+static constexpr size_t kMaxAllocSize = 1024;
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > kMaxBufferSize) {
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ size_t dSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxDealerSize);
+ std::string name = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+ sp<MemoryDealer> dealer = new MemoryDealer(dSize, name.c_str(), flags);
+
+ // This is used to track offsets that have been freed already to avoid an expected fatal log.
+ std::unordered_set<size_t> free_list;
+
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void { dealer->getAllocationAlignment(); },
+ [&]() -> void { dealer->getMemoryHeap(); },
+ [&]() -> void {
+ size_t offset = fdp.ConsumeIntegral<size_t>();
+
+ // Offset has already been freed, so return instead.
+ if (free_list.find(offset) != free_list.end()) return;
+
+ dealer->deallocate(offset);
+ free_list.insert(offset);
+ },
+ [&]() -> void {
+ std::string randString = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ dealer->dump(randString.c_str());
+ },
+ [&]() -> void {
+ size_t allocSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxAllocSize);
+ sp<IMemory> allocated = dealer->allocate(allocSize);
+ // If the allocation was successful, try to write to it
+ if (allocated != nullptr && allocated->unsecurePointer() != nullptr) {
+ memset(allocated->unsecurePointer(), 1, allocated->size());
+
+ // Clear the address from freelist since it has been allocated over again.
+ free_list.erase(allocated->offset());
+ }
+ },
+ })();
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 4209dc5..2e72cc4 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -56,6 +56,7 @@
static std::vector<std::vector<uint32_t>> gPolicyFreqs;
static std::vector<std::vector<uint32_t>> gPolicyCpus;
static std::set<uint32_t> gAllFreqs;
+static unique_fd gTisTotalMapFd;
static unique_fd gTisMapFd;
static unique_fd gConcurrentMapFd;
static unique_fd gUidLastUpdateMapFd;
@@ -129,6 +130,10 @@
gPolicyCpus.emplace_back(*cpus);
}
+ gTisTotalMapFd =
+ unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")};
+ if (gTisTotalMapFd < 0) return false;
+
gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
if (gTisMapFd < 0) return false;
@@ -239,6 +244,31 @@
return gPolicyFreqs;
}
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() {
+ if (!gInitialized && !initGlobals()) return {};
+
+ std::vector<std::vector<uint64_t>> out;
+ uint32_t maxFreqCount = 0;
+ for (const auto &freqList : gPolicyFreqs) {
+ if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+ out.emplace_back(freqList.size(), 0);
+ }
+
+ std::vector<uint64_t> vals(gNCpus);
+ const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount :
+ MAX_FREQS_FOR_TOTAL;
+ for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) {
+ if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {};
+ for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) {
+ if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue;
+ for (const auto &cpu : gPolicyCpus[policyIdx]) {
+ out[policyIdx][freqIdx] += vals[cpu];
+ }
+ }
+ }
+
+ return out;
+}
// Retrieve the times in ns that uid spent running at each CPU frequency.
// Return contains no value on error, otherwise it contains a vector of vectors using the format:
// [[t0_0, t0_1, ...],
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 87a328a..46de669 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -23,6 +23,7 @@
namespace bpf {
bool startTrackingUidTimes();
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes();
std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
getUidsCpuFreqTimes();
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 519689b..d25b2e9 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -40,6 +40,12 @@
using std::vector;
+TEST(TimeInStateTest, TotalTimeInState) {
+ auto times = getTotalCpuFreqTimes();
+ ASSERT_TRUE(times.has_value());
+ EXPECT_FALSE(times->empty());
+}
+
TEST(TimeInStateTest, SingleUidTimeInState) {
auto times = getUidCpuFreqTimes(0);
ASSERT_TRUE(times.has_value());
@@ -186,6 +192,31 @@
}
}
+TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
+ auto allUid = getUidsCpuFreqTimes();
+ auto total = getTotalCpuFreqTimes();
+
+ ASSERT_TRUE(allUid.has_value() && total.has_value());
+
+ // Check the number of policies.
+ ASSERT_EQ(allUid->at(0).size(), total->size());
+
+ for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) {
+ std::vector<uint64_t> totalTimes = total->at(policyIdx);
+ uint32_t totalFreqsCount = totalTimes.size();
+ std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
+ for (auto const &[uid, uidTimes]: *allUid) {
+ for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
+ allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
+ }
+ }
+
+ for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) {
+ ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]);
+ }
+ }
+}
+
TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
uint64_t zero = 0;
auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
@@ -292,6 +323,22 @@
ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
}
+TEST(TimeInStateTest, TotalTimeInStateMonotonic) {
+ auto before = getTotalCpuFreqTimes();
+ ASSERT_TRUE(before.has_value());
+ sleep(1);
+ auto after = getTotalCpuFreqTimes();
+ ASSERT_TRUE(after.has_value());
+
+ for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) {
+ auto timesBefore = before->at(policyIdx);
+ auto timesAfter = after->at(policyIdx);
+ for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) {
+ ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx]));
+ }
+ }
+}
+
TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
auto map1 = getUidsCpuFreqTimes();
ASSERT_TRUE(map1.has_value());
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7dd584d..38ae353 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -69,7 +69,6 @@
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
"IRegionSamplingListener.cpp",
- "IScreenCaptureListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
@@ -78,6 +77,7 @@
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
+ "ScreenCaptureResults.cpp",
"Surface.cpp",
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
@@ -162,6 +162,7 @@
filegroup {
name: "libgui_bufferqueue_sources",
srcs: [
+ "BatchBufferOps.cpp",
"BufferItem.cpp",
"BufferQueue.cpp",
"BufferQueueConsumer.cpp",
@@ -172,7 +173,7 @@
"FrameTimestamps.cpp",
"GLConsumerUtils.cpp",
"HdrMetadata.cpp",
- "QueueBufferInputOutput.cpp",
+ "IGraphicBufferProducerFlattenables.cpp",
"bufferqueue/1.0/Conversion.cpp",
"bufferqueue/1.0/H2BProducerListener.cpp",
"bufferqueue/1.0/WProducerListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 89de629..f4b5a26 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -354,6 +354,16 @@
t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
mAutoRefresh = bufferItem.mAutoRefresh;
}
+ {
+ std::unique_lock _lock{mTimestampMutex};
+ auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
+ if (dequeueTime != mDequeueTimestamps.end()) {
+ Parcel p;
+ p.writeInt64(dequeueTime->second);
+ t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p);
+ mDequeueTimestamps.erase(dequeueTime);
+ }
+ }
auto mergeTransaction =
[&t, currentFrameNumber = bufferItem.mFrameNumber](
@@ -371,7 +381,7 @@
mPendingTransactions.end());
if (applyTransaction) {
- t->apply();
+ t->setApplyToken(mApplyToken).apply();
}
BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -412,6 +422,16 @@
// Do nothing since we are not storing unacquired buffer items locally.
}
+void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) {
+ std::unique_lock _lock{mTimestampMutex};
+ mDequeueTimestamps[bufferId] = systemTime();
+};
+
+void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
+ std::unique_lock _lock{mTimestampMutex};
+ mDequeueTimestamps.erase(bufferId);
+};
+
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
std::lock_guard _lock{mMutex};
mNextTransaction = t;
diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp
new file mode 100644
index 0000000..60aceb1
--- /dev/null
+++ b/libs/gui/BatchBufferOps.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2021 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 <inttypes.h>
+
+#define LOG_TAG "IGBPBatchOps"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+/**
+ * Default implementation of batched buffer operations. These default
+ * implementations call into the non-batched version of the same operation.
+ */
+
+status_t IGraphicBufferProducer::requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(slots.size());
+ for (int32_t slot : slots) {
+ RequestBufferOutput& output = outputs->emplace_back();
+ output.result = requestBuffer(static_cast<int>(slot),
+ &output.buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const DequeueBufferInput& input : inputs) {
+ DequeueBufferOutput& output = outputs->emplace_back();
+ output.result = dequeueBuffer(
+ &output.slot,
+ &output.fence,
+ input.width,
+ input.height,
+ input.format,
+ input.usage,
+ &output.bufferAge,
+ input.getTimestamps ? &output.timestamps.emplace() : nullptr);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::detachBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(slots.size());
+ for (int32_t slot : slots) {
+ results->emplace_back(detachBuffer(slot));
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(buffers.size());
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ AttachBufferOutput& output = outputs->emplace_back();
+ output.result = attachBuffer(&output.slot, buffer);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::queueBuffers(
+ const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (const QueueBufferInput& input : inputs) {
+ QueueBufferOutput& output = outputs->emplace_back();
+ output.result = queueBuffer(input.slot, input, &output);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ results->clear();
+ results->reserve(inputs.size());
+ for (const CancelBufferInput& input : inputs) {
+ results->emplace_back() = cancelBuffer(input.slot, input.fence);
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ outputs->clear();
+ outputs->reserve(inputs.size());
+ for (int32_t input : inputs) {
+ QueryOutput& output = outputs->emplace_back();
+ int value{};
+ output.result = static_cast<status_t>(
+ query(static_cast<int>(input), &value));
+ output.value = static_cast<int64_t>(value);
+ }
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..c1f9b85 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,13 @@
GET_CONSUMER_USAGE,
SET_LEGACY_BUFFER_DROP,
SET_AUTO_PREROTATION,
+ REQUEST_BUFFERS,
+ DEQUEUE_BUFFERS,
+ DETACH_BUFFERS,
+ ATTACH_BUFFERS,
+ QUEUE_BUFFERS,
+ CANCEL_BUFFERS,
+ QUERY_MULTIPLE,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -90,7 +97,7 @@
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
- status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
+ status_t result = remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
@@ -107,6 +114,27 @@
return result;
}
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (RequestBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+
+ return result;
+ }
+
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -183,6 +211,29 @@
return result;
}
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const auto& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (auto& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t detachBuffer(int slot) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -195,6 +246,19 @@
return result;
}
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(slots);
+ status_t result = remote()->transact(DETACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence) {
if (outBuffer == nullptr) {
@@ -256,6 +320,39 @@
return result;
}
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(buffers);
+ for (const sp<GraphicBuffer>& buffer : buffers) {
+ data.write(*buffer.get());
+ }
+ status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (AttachBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ if (result == NO_ERROR) {
+ for (AttachBufferOutput& output : *outputs) {
+ if (output.result == NO_ERROR && output.slot < 0) {
+ ALOGE("attachBuffers returned invalid slot %d",
+ output.slot);
+ android_errorWriteLog(0x534e4554, "37478824");
+ output.result = UNKNOWN_ERROR;
+ }
+ }
+ }
+ return result;
+ }
+
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
@@ -278,6 +375,28 @@
return result;
}
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const QueueBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueueBufferOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -291,6 +410,23 @@
return result;
}
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeVectorSize(inputs);
+ for (const CancelBufferInput& input : inputs) {
+ data.write(input);
+ }
+ status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32Vector(results);
+ return result;
+ }
+
virtual int query(int what, int* value) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -304,6 +440,25 @@
return result;
}
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32Vector(inputs);
+ status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.resizeOutVector(outputs);
+ for (QueryOutput& output : *outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(output);
+ }
+ return result;
+ }
+
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
@@ -576,6 +731,12 @@
return mBase->requestBuffer(slot, buf);
}
+ status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs) override {
+ return mBase->requestBuffers(slots, outputs);
+ }
+
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
}
@@ -590,10 +751,21 @@
return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
}
+ status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs) override {
+ return mBase->dequeueBuffers(inputs, outputs);
+ }
+
status_t detachBuffer(int slot) override {
return mBase->detachBuffer(slot);
}
+ status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results) override {
+ return mBase->detachBuffers(slots, results);
+ }
+
status_t detachNextBuffer(
sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
return mBase->detachNextBuffer(outBuffer, outFence);
@@ -604,6 +776,12 @@
return mBase->attachBuffer(outSlot, buffer);
}
+ status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs) override {
+ return mBase->attachBuffers(buffers, outputs);
+ }
+
status_t queueBuffer(
int slot,
const QueueBufferInput& input,
@@ -611,14 +789,30 @@
return mBase->queueBuffer(slot, input, output);
}
+ status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs) override {
+ return mBase->queueBuffers(inputs, outputs);
+ }
+
status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
return mBase->cancelBuffer(slot, fence);
}
+ status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results) override {
+ return mBase->cancelBuffers(inputs, results);
+ }
+
int query(int what, int* value) override {
return mBase->query(what, value);
}
+ status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs) override {
+ return mBase->query(inputs, outputs);
+ }
+
status_t connect(
const sp<IProducerListener>& listener,
int api, bool producerControlledByApp,
@@ -789,7 +983,7 @@
switch(code) {
case REQUEST_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- int bufferIdx = data.readInt32();
+ int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != nullptr);
@@ -799,6 +993,24 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case REQUEST_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<RequestBufferOutput> outputs;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)requestBuffers(slots, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const RequestBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case SET_MAX_DEQUEUED_BUFFER_COUNT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int maxDequeuedBuffers = data.readInt32();
@@ -841,6 +1053,30 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DEQUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<DequeueBufferInput> inputs;
+ std::vector<DequeueBufferOutput> outputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (DequeueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ (void)dequeueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const DequeueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case DETACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int slot = data.readInt32();
@@ -848,6 +1084,17 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case DETACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> slots;
+ std::vector<status_t> results;
+ status_t result = data.readInt32Vector(&slots);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ (void)detachBuffers(slots, &results);
+ return reply->writeInt32Vector(results);
+ }
case DETACH_NEXT_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer;
@@ -878,6 +1125,31 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case ATTACH_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<sp<GraphicBuffer>> buffers;
+ status_t result = data.resizeOutVector(&buffers);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (sp<GraphicBuffer>& buffer : buffers) {
+ buffer = new GraphicBuffer();
+ result = data.read(*buffer.get());
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<AttachBufferOutput> outputs;
+ (void)attachBuffers(buffers, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const AttachBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
@@ -890,6 +1162,30 @@
return NO_ERROR;
}
+ case QUEUE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<QueueBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ for (QueueBufferInput& input : inputs) {
+ result = data.read(input);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ std::vector<QueueBufferOutput> outputs;
+ (void)queueBuffers(inputs, &outputs);
+ result = reply->writeVectorSize(outputs);
+ for (const QueueBufferOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CANCEL_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
@@ -901,6 +1197,26 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case CANCEL_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<CancelBufferInput> inputs;
+ status_t result = data.resizeOutVector(&inputs);
+ for (CancelBufferInput& input : inputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = data.read(input);
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<status_t> results;
+ result = cancelBuffers(inputs, &results);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply->writeInt32Vector(results);
+ }
case QUERY: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int value = 0;
@@ -910,6 +1226,27 @@
reply->writeInt32(res);
return NO_ERROR;
}
+ case QUERY_MULTIPLE: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ std::vector<int32_t> inputs;
+ status_t result = data.readInt32Vector(&inputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ std::vector<QueryOutput> outputs;
+ result = query(inputs, &outputs);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeVectorSize(outputs);
+ for (const QueryOutput& output : outputs) {
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->write(output);
+ }
+ return result;
+ }
case CONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<IProducerListener> listener;
@@ -1083,11 +1420,4 @@
return BBinder::onTransact(code, data, reply, flags);
}
-// ----------------------------------------------------------------------------
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-
}; // namespace android
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
new file mode 100644
index 0000000..c8b9b67
--- /dev/null
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2021 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 <inttypes.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) +
+ sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize() +
+ hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, timestamp);
+ FlattenableUtils::write(buffer, size, isAutoTimestamp);
+ FlattenableUtils::write(buffer, size, dataSpace);
+ FlattenableUtils::write(buffer, size, crop);
+ FlattenableUtils::write(buffer, size, scalingMode);
+ FlattenableUtils::write(buffer, size, transform);
+ FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+ status_t result = fence->flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.flatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::write(buffer, size, slot);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, timestamp);
+ FlattenableUtils::read(buffer, size, isAutoTimestamp);
+ FlattenableUtils::read(buffer, size, dataSpace);
+ FlattenableUtils::read(buffer, size, crop);
+ FlattenableUtils::read(buffer, size, scalingMode);
+ FlattenableUtils::read(buffer, size, transform);
+ FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+ fence = new Fence();
+ status_t result = fence->unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = surfaceDamage.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+ result = hdrMetadata.unflatten(buffer, size);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+ FlattenableUtils::read(buffer, size, slot);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
+ sizeof(result);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+ FlattenableUtils::write(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.flatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+ FlattenableUtils::read(buffer, size, maxBufferCount);
+
+ status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() {
+ return sizeof(result) +
+ sizeof(int32_t); // IsBufferNull
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize());
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const {
+ return (buffer == nullptr ? 0 : buffer->getFdCount());
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::flatten(
+ void*& fBuffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(fBuffer, size, result);
+ const int32_t isBufferNull = (buffer == nullptr ? 1 : 0);
+ FlattenableUtils::write(fBuffer, size, isBufferNull);
+
+ if (!isBufferNull) {
+ status_t status = buffer->flatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::unflatten(
+ void const*& fBuffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(fBuffer, size, result);
+ int32_t isBufferNull = 0;
+ FlattenableUtils::read(fBuffer, size, isBufferNull);
+ buffer = new GraphicBuffer();
+ if (!isBufferNull) {
+ status_t status = buffer->unflatten(fBuffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const {
+ return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) +
+ sizeof(int32_t/*getTimestamps*/);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, format);
+ FlattenableUtils::write(buffer, size, usage);
+ const int32_t getTimestampsInt = (getTimestamps ? 1 : 0);
+ FlattenableUtils::write(buffer, size, getTimestampsInt);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, format);
+ FlattenableUtils::read(buffer, size, usage);
+ int32_t getTimestampsInt = 0;
+ FlattenableUtils::read(buffer, size, getTimestampsInt);
+ getTimestamps = (getTimestampsInt == 1);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() {
+ return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ (timestamps.has_value() ? timestamps->getFlattenedSize() : 0);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const {
+ return fence->getFdCount() +
+ (timestamps.has_value() ? timestamps->getFdCount() : 0);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+ FlattenableUtils::write(buffer, size, bufferAge);
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return result;
+ }
+ const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0;
+ FlattenableUtils::write(buffer, size, hasTimestamps);
+ if (timestamps.has_value()) {
+ status = timestamps->flatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+ FlattenableUtils::read(buffer, size, bufferAge);
+
+ fence = new Fence();
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ int32_t hasTimestamps = 0;
+ FlattenableUtils::read(buffer, size, hasTimestamps);
+ if (hasTimestamps) {
+ timestamps.emplace();
+ status = timestamps->unflatten(buffer, size, fds, count);
+ }
+ return status;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(slot);
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, slot);
+
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() {
+ return sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const {
+ return minFlattenedSize() + fence->getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const {
+ return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, slot);
+ return fence->flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, slot);
+
+ fence = new Fence();
+ return fence->unflatten(buffer, size, fds, count);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const {
+ return sizeof(result) + sizeof(value);
+}
+
+status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::write(buffer, size, result);
+ FlattenableUtils::write(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, result);
+ FlattenableUtils::read(buffer, size, value);
+
+ return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp
deleted file mode 100644
index 0635e9c..0000000
--- a/libs/gui/IScreenCaptureListener.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/IScreenCaptureListener.h>
-#include <gui/LayerState.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION,
- LAST = ON_SCREEN_CAPTURE_COMPLETE,
-};
-
-} // Anonymous namespace
-
-class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> {
-public:
- explicit BpScreenCaptureListener(const sp<IBinder>& impl)
- : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {}
-
- ~BpScreenCaptureListener() override;
-
- status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
- Parcel data, reply;
- data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor());
-
- SAFE_PARCEL(captureResults.write, data);
- return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data,
- &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpScreenCaptureListener::~BpScreenCaptureListener() = default;
-
-IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener");
-
-status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_SCREEN_CAPTURE_COMPLETE: {
- CHECK_INTERFACE(IScreenCaptureListener, data, reply);
- ScreenCaptureResults captureResults;
- SAFE_PARCEL(captureResults.read, data);
- return onScreenCaptureComplete(captureResults);
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 30c9b37..634d8b7 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -17,6 +17,7 @@
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <gui/LayerMetadata.h>
+#include <inttypes.h>
#include "android/view/LayerMetadataKey.h"
@@ -113,6 +114,15 @@
memcpy(data.data(), p.data(), p.dataSize());
}
+std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const {
+ if (!has(key)) return std::nullopt;
+ const std::vector<uint8_t>& data = mMap.at(key);
+ if (data.size() < sizeof(int64_t)) return std::nullopt;
+ Parcel p;
+ p.setData(data.data(), data.size());
+ return p.readInt64();
+}
+
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
if (!has(key)) return std::string();
switch (static_cast<view::LayerMetadataKey>(key)) {
@@ -124,6 +134,8 @@
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
case view::LayerMetadataKey::METADATA_OWNER_PID:
return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
+ case view::LayerMetadataKey::METADATA_DEQUEUE_TIME:
+ return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key));
default:
return StringPrintf("%d%s%dbytes", key, separator,
static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 7d2c7b8..63be3ed 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -714,33 +714,4 @@
return NO_ERROR;
}
-status_t ScreenCaptureResults::write(Parcel& output) const {
- if (buffer != nullptr) {
- SAFE_PARCEL(output.writeBool, true);
- SAFE_PARCEL(output.write, *buffer);
- } else {
- SAFE_PARCEL(output.writeBool, false);
- }
- SAFE_PARCEL(output.writeBool, capturedSecureLayers);
- SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace));
- SAFE_PARCEL(output.writeInt32, result);
- return NO_ERROR;
-}
-
-status_t ScreenCaptureResults::read(const Parcel& input) {
- bool hasGraphicBuffer;
- SAFE_PARCEL(input.readBool, &hasGraphicBuffer);
- if (hasGraphicBuffer) {
- buffer = new GraphicBuffer();
- SAFE_PARCEL(input.read, *buffer);
- }
-
- SAFE_PARCEL(input.readBool, &capturedSecureLayers);
- uint32_t dataspace = 0;
- SAFE_PARCEL(input.readUint32, &dataspace);
- capturedDataspace = static_cast<ui::Dataspace>(dataspace);
- SAFE_PARCEL(input.readInt32, &result);
- return NO_ERROR;
-}
-
}; // namespace android
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
deleted file mode 100644
index 30f0ef6..0000000
--- a/libs/gui/QueueBufferInputOutput.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2010 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 <inttypes.h>
-
-#define LOG_TAG "QueueBufferInputOutput"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps);
-}
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
- parcel.read(*this);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
- hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
- return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, timestamp);
- FlattenableUtils::write(buffer, size, isAutoTimestamp);
- FlattenableUtils::write(buffer, size, dataSpace);
- FlattenableUtils::write(buffer, size, crop);
- FlattenableUtils::write(buffer, size, scalingMode);
- FlattenableUtils::write(buffer, size, transform);
- FlattenableUtils::write(buffer, size, stickyTransform);
- FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
- status_t result = fence->flatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.flatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, timestamp);
- FlattenableUtils::read(buffer, size, isAutoTimestamp);
- FlattenableUtils::read(buffer, size, dataSpace);
- FlattenableUtils::read(buffer, size, crop);
- FlattenableUtils::read(buffer, size, scalingMode);
- FlattenableUtils::read(buffer, size, transform);
- FlattenableUtils::read(buffer, size, stickyTransform);
- FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
- fence = new Fence();
- status_t result = fence->unflatten(buffer, size, fds, count);
- if (result != NO_ERROR) {
- return result;
- }
- result = surfaceDamage.unflatten(buffer, size);
- if (result != NO_ERROR) {
- return result;
- }
- FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
- return hdrMetadata.unflatten(buffer, size);
-}
-
-////////////////////////////////////////////////////////////////////////
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
- return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
- sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
-}
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
- return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
- return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
- void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
- if (size < getFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::write(buffer, size, width);
- FlattenableUtils::write(buffer, size, height);
- FlattenableUtils::write(buffer, size, transformHint);
- FlattenableUtils::write(buffer, size, numPendingBuffers);
- FlattenableUtils::write(buffer, size, nextFrameNumber);
- FlattenableUtils::write(buffer, size, bufferReplaced);
- FlattenableUtils::write(buffer, size, maxBufferCount);
-
- return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
- if (size < minFlattenedSize()) {
- return NO_MEMORY;
- }
-
- FlattenableUtils::read(buffer, size, width);
- FlattenableUtils::read(buffer, size, height);
- FlattenableUtils::read(buffer, size, transformHint);
- FlattenableUtils::read(buffer, size, numPendingBuffers);
- FlattenableUtils::read(buffer, size, nextFrameNumber);
- FlattenableUtils::read(buffer, size, bufferReplaced);
- FlattenableUtils::read(buffer, size, maxBufferCount);
-
- return frameTimestamps.unflatten(buffer, size, fds, count);
-}
-
-} // namespace android
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
new file mode 100644
index 0000000..2b29487
--- /dev/null
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 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/ScreenCaptureResults.h>
+
+namespace android::gui {
+
+status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const {
+ if (buffer != nullptr) {
+ SAFE_PARCEL(parcel->writeBool, true);
+ SAFE_PARCEL(parcel->write, *buffer);
+ } else {
+ SAFE_PARCEL(parcel->writeBool, false);
+ }
+ SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
+ SAFE_PARCEL(parcel->writeInt32, result);
+ return NO_ERROR;
+}
+
+status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) {
+ bool hasGraphicBuffer;
+ SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
+ if (hasGraphicBuffer) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(parcel->read, *buffer);
+ }
+
+ SAFE_PARCEL(parcel->readBool, &capturedSecureLayers);
+ uint32_t dataspace = 0;
+ SAFE_PARCEL(parcel->readUint32, &dataspace);
+ capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+ SAFE_PARCEL(parcel->readInt32, &result);
+ return NO_ERROR;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1bba5e4..e82f0cc 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -618,29 +618,31 @@
std::mutex mMutex;
};
+void Surface::getDequeueBufferInputLocked(
+ IGraphicBufferProducer::DequeueBufferInput* dequeueInput) {
+ LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null");
+
+ dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth;
+ dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight;
+
+ dequeueInput->format = mReqFormat;
+ dequeueInput->usage = mReqUsage;
+
+ dequeueInput->getTimestamps = mEnableFrameTimestamps;
+}
+
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
- uint32_t reqWidth;
- uint32_t reqHeight;
- PixelFormat reqFormat;
- uint64_t reqUsage;
- bool enableFrameTimestamps;
-
+ IGraphicBufferProducer::DequeueBufferInput dqInput;
{
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
- reqWidth = mReqWidth ? mReqWidth : mUserWidth;
- reqHeight = mReqHeight ? mReqHeight : mUserHeight;
-
- reqFormat = mReqFormat;
- reqUsage = mReqUsage;
-
- enableFrameTimestamps = mEnableFrameTimestamps;
+ getDequeueBufferInputLocked(&dqInput);
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
@@ -658,16 +660,17 @@
nsecs_t startTime = systemTime();
FrameEventHistoryDelta frameTimestamps;
- status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
- reqFormat, reqUsage, &mBufferAge,
- enableFrameTimestamps ? &frameTimestamps
- : nullptr);
+ status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
+ dqInput.height, dqInput.format,
+ dqInput.usage, &mBufferAge,
+ dqInput.getTimestamps ?
+ &frameTimestamps : nullptr);
mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
"(%d, %d, %d, %#" PRIx64 ") failed: %d",
- reqWidth, reqHeight, reqFormat, reqUsage, result);
+ dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result);
return result;
}
@@ -696,7 +699,7 @@
freeAllBuffers();
}
- if (enableFrameTimestamps) {
+ if (dqInput.getTimestamps) {
mFrameEventHistory->applyDelta(frameTimestamps);
}
@@ -739,6 +742,176 @@
return OK;
}
+int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) {
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+
+ ATRACE_CALL();
+ ALOGV("Surface::dequeueBuffers");
+
+ if (buffers->size() == 0) {
+ ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batch operation is not supported in shared buffer mode!",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBufferRequested = buffers->size();
+ DequeueBufferInput input;
+
+ {
+ Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
+
+ getDequeueBufferInputLocked(&input);
+ } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers
+
+ std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input);
+ std::vector<DequeueBufferOutput> dequeueOutput;
+
+ nsecs_t startTime = systemTime();
+
+ status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput);
+
+ mLastDequeueDuration = systemTime() - startTime;
+
+ if (result < 0) {
+ ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+ "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+ __FUNCTION__, input.width, input.height, input.format, input.usage, result);
+ return result;
+ }
+
+ std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested);
+ std::vector<status_t> cancelBufferOutputs;
+ for (size_t i = 0; i < numBufferRequested; i++) {
+ cancelBufferInputs[i].slot = dequeueOutput[i].slot;
+ cancelBufferInputs[i].fence = dequeueOutput[i].fence;
+ }
+
+ for (const auto& output : dequeueOutput) {
+ if (output.result < 0) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+ "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+ __FUNCTION__, input.width, input.height, input.format, input.usage,
+ output.result);
+ return output.result;
+ }
+
+ if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
+ __FUNCTION__, output.slot);
+ android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
+ return FAILED_TRANSACTION;
+ }
+
+ if (input.getTimestamps && !output.timestamps.has_value()) {
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ ALOGE("%s: no frame timestamp returns!", __FUNCTION__);
+ return FAILED_TRANSACTION;
+ }
+
+ // this should never happen
+ ALOGE_IF(output.fence == nullptr,
+ "%s: received null Fence! slot=%d", __FUNCTION__, output.slot);
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ // Write this while holding the mutex
+ mLastDequeueStartTime = startTime;
+
+ std::vector<int32_t> requestBufferSlots;
+ requestBufferSlots.reserve(numBufferRequested);
+ // handle release all buffers and request buffers
+ for (const auto& output : dequeueOutput) {
+ if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__);
+ freeAllBuffers();
+ break;
+ }
+ }
+
+ for (const auto& output : dequeueOutput) {
+ // Collect slots that needs requesting buffer
+ sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer);
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+ if (mReportRemovedBuffers && (gbuf != nullptr)) {
+ mRemovedBuffers.push_back(gbuf);
+ }
+ requestBufferSlots.push_back(output.slot);
+ }
+ }
+
+ // Batch request Buffer
+ std::vector<RequestBufferOutput> reqBufferOutput;
+ if (requestBufferSlots.size() > 0) {
+ result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput);
+ if (result != NO_ERROR) {
+ ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d",
+ __FUNCTION__, result);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ return result;
+ }
+
+ // Check if we have any single failure
+ for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+ if (reqBufferOutput[i].result != OK) {
+ ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d",
+ __FUNCTION__, i, requestBufferSlots[i]);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+ return reqBufferOutput[i].result;
+ }
+ }
+
+ // Fill request buffer results to mSlots
+ for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+ mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer;
+ }
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) {
+ const auto& output = dequeueOutput[batchIdx];
+ int slot = output.slot;
+ sp<GraphicBuffer>& gbuf(mSlots[slot].buffer);
+
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor hwcReleaseThread("HWC release");
+ hwcReleaseThread.queueFence(output.fence);
+ }
+
+ if (input.getTimestamps) {
+ mFrameEventHistory->applyDelta(output.timestamps.value());
+ }
+
+ if (output.fence->isValid()) {
+ buffers->at(batchIdx).fenceFd = output.fence->dup();
+ if (buffers->at(batchIdx).fenceFd == -1) {
+ ALOGE("%s: error duping fence: %d", __FUNCTION__, errno);
+ // dup() should never fail; something is badly wrong. Soldier on
+ // and hope for the best; the worst that should happen is some
+ // visible corruption that lasts until the next frame.
+ }
+ } else {
+ buffers->at(batchIdx).fenceFd = -1;
+ }
+
+ buffers->at(batchIdx).buffer = gbuf.get();
+ mDequeuedSlots.insert(slot);
+ }
+ return OK;
+}
+
int Surface::cancelBuffer(android_native_buffer_t* buffer,
int fenceFd) {
ATRACE_CALL();
@@ -769,15 +942,65 @@
return OK;
}
+int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) {
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ ATRACE_CALL();
+ ALOGV("Surface::cancelBuffers");
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batch operation is not supported in shared buffer mode!",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBuffers = buffers.size();
+ std::vector<CancelBufferInput> cancelBufferInputs(numBuffers);
+ std::vector<status_t> cancelBufferOutputs;
+ size_t numBuffersCancelled = 0;
+ int badSlotResult = 0;
+ for (size_t i = 0; i < numBuffers; i++) {
+ int slot = getSlotFromBufferLocked(buffers[i].buffer);
+ int fenceFd = buffers[i].fenceFd;
+ if (slot < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
+ badSlotResult = slot;
+ } else {
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ cancelBufferInputs[numBuffersCancelled].slot = slot;
+ cancelBufferInputs[numBuffersCancelled++].fence = fence;
+ }
+ }
+ cancelBufferInputs.resize(numBuffersCancelled);
+ mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+
+
+ for (size_t i = 0; i < numBuffersCancelled; i++) {
+ mDequeuedSlots.erase(cancelBufferInputs[i].slot);
+ }
+
+ if (badSlotResult != 0) {
+ return badSlotResult;
+ }
+ return OK;
+}
+
int Surface::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
+ if (buffer == nullptr) {
+ ALOGE("%s: input buffer is null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
}
- ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle);
return BAD_VALUE;
}
@@ -787,42 +1010,22 @@
return OK;
}
-int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
- ATRACE_CALL();
- ALOGV("Surface::queueBuffer");
- Mutex::Autolock lock(mMutex);
- int64_t timestamp;
+void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd,
+ nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) {
bool isAutoTimestamp = false;
- if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+ if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.0);
- } else {
- timestamp = mTimestamp;
}
- int i = getSlotFromBufferLocked(buffer);
- if (i < 0) {
- if (fenceFd >= 0) {
- close(fenceFd);
- }
- return i;
- }
- if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
- if (fenceFd >= 0) {
- close(fenceFd);
- }
- return OK;
- }
-
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop(Rect::EMPTY_RECT);
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
- IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -893,15 +1096,12 @@
input.setSurfaceDamage(flippedRegion);
}
+ *out = input;
+}
- nsecs_t now = systemTime();
- status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
- mLastQueueDuration = systemTime() - now;
- if (err != OK) {
- ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
- }
-
- mDequeuedSlots.erase(i);
+void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
+ const IGraphicBufferProducer::QueueBufferOutput& output) {
+ mDequeuedSlots.erase(slot);
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
@@ -935,7 +1135,7 @@
mDirtyRegion = Region::INVALID_REGION;
}
- if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+ if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) {
mSharedBufferHasBeenQueued = true;
}
@@ -945,6 +1145,89 @@
static FenceMonitor gpuCompletionThread("GPU completion");
gpuCompletionThread.queueFence(fence);
}
+}
+
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ int i = getSlotFromBufferLocked(buffer);
+ if (i < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return i;
+ }
+ if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
+ sp<Fence> fence = input.fence;
+
+ nsecs_t now = systemTime();
+ status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("queueBuffer: error queuing buffer, %d", err);
+ }
+
+ onBufferQueuedLocked(i, fence, output);
+ return err;
+}
+
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffers");
+ Mutex::Autolock lock(mMutex);
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ size_t numBuffers = buffers.size();
+ std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers);
+ std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs;
+ std::vector<int> bufferSlots(numBuffers, -1);
+ std::vector<sp<Fence>> bufferFences(numBuffers);
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ int i = getSlotFromBufferLocked(buffers[batchIdx].buffer);
+ if (i < 0) {
+ if (buffers[batchIdx].fenceFd >= 0) {
+ close(buffers[batchIdx].fenceFd);
+ }
+ return i;
+ }
+ bufferSlots[batchIdx] = i;
+
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(
+ buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp,
+ &input);
+ bufferFences[batchIdx] = input.fence;
+ queueBufferInputs[batchIdx] = input;
+ }
+
+ nsecs_t now = systemTime();
+ status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err);
+ }
+
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
+ queueBufferOutputs[batchIdx]);
+ }
return err;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5570d99..97c2693 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -396,7 +396,8 @@
mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime),
mIsAutoTimestamp(other.mIsAutoTimestamp),
- mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
+ mFrameTimelineVsyncId(other.mFrameTimelineVsyncId),
+ mApplyToken(other.mApplyToken) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
@@ -427,7 +428,8 @@
const int64_t desiredPresentTime = parcel->readInt64();
const bool isAutoTimestamp = parcel->readBool();
const int64_t frameTimelineVsyncId = parcel->readInt64();
-
+ sp<IBinder> applyToken;
+ parcel->readNullableStrongBinder(&applyToken);
size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
return BAD_VALUE;
@@ -505,6 +507,7 @@
mListenerCallbacks = listenerCallbacks;
mComposerStates = composerStates;
mInputWindowCommands = inputWindowCommands;
+ mApplyToken = applyToken;
return NO_ERROR;
}
@@ -532,6 +535,7 @@
parcel->writeInt64(mDesiredPresentTime);
parcel->writeBool(mIsAutoTimestamp);
parcel->writeInt64(mFrameTimelineVsyncId);
+ parcel->writeStrongBinder(mApplyToken);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
for (auto const& displayState : mDisplayStates) {
displayState.write(*parcel);
@@ -607,6 +611,7 @@
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+ mApplyToken = other.mApplyToken;
// When merging vsync Ids we take the oldest one
if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
@@ -635,6 +640,7 @@
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+ mApplyToken = nullptr;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -763,7 +769,10 @@
flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
}
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ sp<IBinder> applyToken = mApplyToken
+ ? mApplyToken
+ : IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
@@ -1579,6 +1588,12 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
+ const sp<IBinder>& applyToken) {
+ mApplyToken = applyToken;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
copy to libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
index c9788af..f490118 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplay.h"
+package android.gui;
-namespace android::Hwc2::mock {
+import android.gui.ScreenCaptureResults;
-// Explicit default instantiation is recommended.
-Display::Display() = default;
-Display::~Display() = default;
-
-} // namespace android::Hwc2::mock
\ No newline at end of file
+/** @hide */
+oneway interface IScreenCaptureListener {
+ void onScreenCaptureComplete(in ScreenCaptureResults captureResults);
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
similarity index 66%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
rename to libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
index c9788af..9908edd 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplay.h"
+package android.gui;
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-Display::Display() = default;
-Display::~Display() = default;
-
-} // namespace android::Hwc2::mock
\ No newline at end of file
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index c4cdb65..0fbcbdc 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -78,6 +78,8 @@
void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
void onFrameReplaced(const BufferItem& item) override;
void onFrameAvailable(const BufferItem& item) override;
+ void onFrameDequeued(const uint64_t) override;
+ void onFrameCancelled(const uint64_t) override;
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
@@ -165,6 +167,18 @@
std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
+
+ // Queues up transactions using this token in SurfaceFlinger. This prevents queued up
+ // transactions from other parts of the client from blocking this transaction.
+ const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
+
+ // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or
+ // we will deadlock.
+ std::mutex mTimestampMutex;
+ // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses
+ // it for debugging purposes.
+ std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps
+ GUARDED_BY(mTimestampMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..c3b9262 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -38,6 +38,9 @@
#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <optional>
+#include <vector>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -289,8 +292,9 @@
const sp<GraphicBuffer>& buffer) = 0;
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
- friend class Flattenable<QueueBufferInput>;
- explicit inline QueueBufferInput(const Parcel& parcel);
+ explicit inline QueueBufferInput(const Parcel& parcel) {
+ parcel.read(*this);
+ }
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
@@ -304,21 +308,29 @@
// camera mode).
// getFrameTimestamps - whether or not the latest frame timestamps
// should be retrieved from the consumer.
+ // slot - the slot index to queue. This is used only by queueBuffers().
+ // queueBuffer() ignores this value and uses the argument `slot`
+ // instead.
inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
android_dataspace _dataSpace, const Rect& _crop,
int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
- uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false,
+ int _slot = -1)
: timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
- transform(_transform), stickyTransform(_sticky), fence(_fence),
- surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+ transform(_transform), stickyTransform(_sticky),
+ fence(_fence), surfaceDamage(),
+ getFrameTimestamps(_getFrameTimestamps), slot(_slot) { }
+
+ QueueBufferInput() = default;
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
uint32_t* outStickyTransform = nullptr,
- bool* outGetFrameTimestamps = nullptr) const {
+ bool* outGetFrameTimestamps = nullptr,
+ int* outSlot = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -332,6 +344,9 @@
if (outGetFrameTimestamps) {
*outGetFrameTimestamps = getFrameTimestamps;
}
+ if (outSlot) {
+ *outSlot = slot;
+ }
}
// Flattenable protocol
@@ -357,6 +372,7 @@
sp<Fence> fence;
Region surfaceDamage;
bool getFrameTimestamps{false};
+ int slot{-1};
HdrMetadata hdrMetadata;
};
@@ -385,6 +401,7 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{0};
+ status_t result{NO_ERROR};
};
// queueBuffer indicates that the client has finished filling in the
@@ -404,6 +421,10 @@
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
//
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by
+ // queueBuffers(), the batched version of queueBuffer(). The non-batched
+ // method (queueBuffer()) uses `slot` and ignores `input.slot`.
+ //
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
// connected.
@@ -639,6 +660,147 @@
// the width and height used for dequeueBuffer will be additionally swapped.
virtual status_t setAutoPrerotation(bool autoPrerotation);
+ struct RequestBufferOutput : public Flattenable<RequestBufferOutput> {
+ RequestBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ sp<GraphicBuffer> buffer;
+ };
+
+ // Batched version of requestBuffer().
+ // This method behaves like a sequence of requestBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t requestBuffers(
+ const std::vector<int32_t>& slots,
+ std::vector<RequestBufferOutput>* outputs);
+
+ struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> {
+ DequeueBufferInput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint64_t usage;
+ bool getTimestamps;
+ };
+
+ struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> {
+ DequeueBufferOutput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ status_t result;
+ int slot = -1;
+ sp<Fence> fence = Fence::NO_FENCE;
+ uint64_t bufferAge;
+ std::optional<FrameEventHistoryDelta> timestamps;
+ };
+
+ // Batched version of dequeueBuffer().
+ // This method behaves like a sequence of dequeueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t dequeueBuffers(
+ const std::vector<DequeueBufferInput>& inputs,
+ std::vector<DequeueBufferOutput>* outputs);
+
+ // Batched version of detachBuffer().
+ // This method behaves like a sequence of detachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+ std::vector<status_t>* results);
+
+
+ struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> {
+ AttachBufferOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int slot;
+ };
+ // Batched version of attachBuffer().
+ // This method behaves like a sequence of attachBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t attachBuffers(
+ const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<AttachBufferOutput>* outputs);
+
+ // Batched version of queueBuffer().
+ // This method behaves like a sequence of queueBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ //
+ // Note: QueueBufferInput::slot was added to QueueBufferInput to include the
+ // `slot` input argument of the non-batched method queueBuffer().
+ virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+ std::vector<QueueBufferOutput>* outputs);
+
+ struct CancelBufferInput : public Flattenable<CancelBufferInput> {
+ CancelBufferInput() = default;
+
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ int slot;
+ sp<Fence> fence;
+ };
+ // Batched version of cancelBuffer().
+ // This method behaves like a sequence of cancelBuffer() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t cancelBuffers(
+ const std::vector<CancelBufferInput>& inputs,
+ std::vector<status_t>* results);
+
+ struct QueryOutput : public LightFlattenable<QueryOutput> {
+ QueryOutput() = default;
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ status_t result;
+ int64_t value;
+ };
+ // Batched version of query().
+ // This method behaves like a sequence of query() calls.
+ // The return value of the batched method will only be about the
+ // transaction. For a local call, the return value will always be NO_ERROR.
+ virtual status_t query(const std::vector<int32_t> inputs,
+ std::vector<QueryOutput>* outputs);
+
#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h
deleted file mode 100644
index a2ddc9f..0000000
--- a/libs/gui/include/gui/IScreenCaptureListener.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/Binder.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-struct ScreenCaptureResults;
-
-// TODO(b/166271443): Convert to AIDL
-class IScreenCaptureListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(ScreenCaptureListener)
-
- virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0;
-};
-
-class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> {
-public:
- BnScreenCaptureListener()
- : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 40316db..86f3c60 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,8 +22,8 @@
#include <binder/IBinder.h>
#include <binder/IInterface.h>
+#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
-#include <gui/IScreenCaptureListener.h>
#include <gui/ITransactionCompletedListener.h>
#include <input/Flags.h>
@@ -59,7 +59,6 @@
struct DisplayState;
struct InputWindowCommands;
struct LayerCaptureArgs;
-struct ScreenCaptureResults;
class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
@@ -69,6 +68,8 @@
class Rect;
enum class FrameEvent;
+using gui::IScreenCaptureListener;
+
namespace ui {
struct DisplayState;
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index ac48aef..41982c2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -29,6 +29,7 @@
METADATA_MOUSE_CURSOR = 4,
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
+ METADATA_DEQUEUE_TIME = 7
};
struct LayerMetadata : public Parcelable {
@@ -51,6 +52,8 @@
bool has(uint32_t key) const;
int32_t getInt32(uint32_t key, int32_t fallback) const;
void setInt32(uint32_t key, int32_t value);
+ std::optional<int64_t> getInt64(uint32_t key) const;
+ void setInt64(uint32_t key, int64_t value);
std::string itemToString(uint32_t key, const char* separator) const;
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f1c5d67..4a291ae 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -362,16 +362,6 @@
status_t read(const Parcel& input) override;
};
-struct ScreenCaptureResults {
- sp<GraphicBuffer> buffer;
- bool capturedSecureLayers{false};
- ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
- status_t result = OK;
-
- status_t write(Parcel& output) const;
- status_t read(const Parcel& input);
-};
-
}; // namespace android
#endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
new file mode 100644
index 0000000..fdb4b69
--- /dev/null
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 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
+
+#define SAFE_PARCEL(FUNC, ...) \
+ { \
+ status_t error = FUNC(__VA_ARGS__); \
+ if (error) { \
+ ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
+ return error; \
+ } \
+ }
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::gui {
+
+struct ScreenCaptureResults : public Parcelable {
+public:
+ ScreenCaptureResults() = default;
+ virtual ~ScreenCaptureResults() = default;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ sp<GraphicBuffer> buffer;
+ bool capturedSecureLayers{false};
+ ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+ status_t result = OK;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 82bc5c9..43b5dcd 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -344,6 +344,23 @@
static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
ui::Dataspace dataspace);
+ // Batch version of dequeueBuffer, cancelBuffer and queueBuffer
+ // Note that these batched operations are not supported when shared buffer mode is being used.
+ struct BatchBuffer {
+ ANativeWindowBuffer* buffer = nullptr;
+ int fenceFd = -1;
+ };
+ virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers);
+ virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers);
+
+ struct BatchQueuedBuffer {
+ ANativeWindowBuffer* buffer = nullptr;
+ int fenceFd = -1;
+ nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ };
+ virtual int queueBuffers(
+ const std::vector<BatchQueuedBuffer>& buffers);
+
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
@@ -373,6 +390,14 @@
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
+ void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput);
+
+ void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp,
+ IGraphicBufferProducer::QueueBufferInput* out);
+
+ void onBufferQueuedLocked(int slot, sp<Fence> fence,
+ const IGraphicBufferProducer::QueueBufferOutput& output);
+
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0abe72c..48bc5d5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -388,6 +388,10 @@
// The vsync Id provided by Choreographer.getVsyncId
int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+ // If not null, transactions will be queued up using this token otherwise a common token
+ // per process will be used.
+ sp<IBinder> mApplyToken = nullptr;
+
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
@@ -555,6 +559,11 @@
// in shared buffer mode.
Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
+ // Queues up transactions using this token in SurfaceFlinger. By default, all transactions
+ // from a client are placed on the same queue. This can be used to prevent multiple
+ // transactions from blocking each other.
+ Transaction& setApplyToken(const sp<IBinder>& token);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
index 2857996..8620b77 100644
--- a/libs/gui/include/gui/SyncScreenCaptureListener.h
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -16,16 +16,19 @@
#pragma once
+#include <android/gui/BnScreenCaptureListener.h>
#include <gui/SurfaceComposerClient.h>
#include <future>
namespace android {
-class SyncScreenCaptureListener : public BnScreenCaptureListener {
+using gui::ScreenCaptureResults;
+
+struct SyncScreenCaptureListener : gui::BnScreenCaptureListener {
public:
- status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+ binder::Status onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
resultsPromise.set_value(captureResults);
- return NO_ERROR;
+ return binder::Status::ok();
}
ScreenCaptureResults waitForResults() {
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 170ad87..d69b7c3 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -221,6 +221,32 @@
return captureResults.result;
}
+ void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
+ nsecs_t presentTimeDelay) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbp->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, igbp->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ nsecs_t timestampNanos = systemTime() + presentTimeDelay;
+ IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight / 2),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbp->queueBuffer(slot, input, &qbOutput);
+ }
+
sp<SurfaceComposerClient> mClient;
sp<ISurfaceComposer> mComposer;
@@ -528,6 +554,41 @@
ASSERT_EQ(queuesToNativeWindow, 1);
}
+TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
+ sp<SurfaceControl> bgSurface =
+ mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ ASSERT_NE(nullptr, bgSurface.get());
+ Transaction t;
+ t.setLayerStack(bgSurface, 0)
+ .show(bgSurface)
+ .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
+ .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
+ .apply();
+
+ BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> slowIgbProducer;
+ setUpProducer(slowAdapter, slowIgbProducer);
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+
+ BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> fastIgbProducer;
+ setUpProducer(fastAdapter, fastIgbProducer);
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+ queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
+ fastAdapter.waitForCallbacks();
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
public:
void test(uint32_t tr) {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 15bd32d..2af2fe1 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -70,6 +70,9 @@
const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
+ const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0;
+ const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0;
+ const int QUEUE_BUFFER_INPUT_SLOT = -1;
// Enums to control which IGraphicBufferProducer backend to test.
enum IGraphicBufferProducerTestCode {
@@ -156,6 +159,9 @@
scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE;
transform = QUEUE_BUFFER_INPUT_TRANSFORM;
fence = QUEUE_BUFFER_INPUT_FENCE;
+ stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM;
+ getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS;
+ slot = QUEUE_BUFFER_INPUT_SLOT;
}
IGraphicBufferProducer::QueueBufferInput build() {
@@ -166,7 +172,10 @@
crop,
scalingMode,
transform,
- fence);
+ fence,
+ stickyTransform,
+ getTimestamps,
+ slot);
}
QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
@@ -204,6 +213,21 @@
return *this;
}
+ QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) {
+ this->stickyTransform = stickyTransform;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) {
+ this->getTimestamps = getTimestamps;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setSlot(int slot) {
+ this->slot = slot;
+ return *this;
+ }
+
private:
int64_t timestamp;
bool isAutoTimestamp;
@@ -212,17 +236,17 @@
int scalingMode;
uint32_t transform;
sp<Fence> fence;
+ uint32_t stickyTransform;
+ bool getTimestamps;
+ int slot;
}; // struct QueueBufferInputBuilder
- // To easily store dequeueBuffer results into containers
- struct DequeueBufferResult {
- int slot;
- sp<Fence> fence;
- };
-
- status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
- nullptr, nullptr);
+ status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage,
+ IGraphicBufferProducer::DequeueBufferOutput* result) {
+ result->result =
+ mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
+ &result->bufferAge, nullptr);
+ return result->result;
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -336,6 +360,27 @@
EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value);
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ NATIVE_WINDOW_WIDTH,
+ NATIVE_WINDOW_HEIGHT,
+ NATIVE_WINDOW_FORMAT,
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND,
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS };
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value));
+ EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value));
+ EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value);
+ EXPECT_LE(0, outputs[3].value);
+ EXPECT_FALSE(outputs[4].value);
+ EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value);
+ for (const QueryOutput& output : outputs) {
+ EXPECT_OK(output.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) {
@@ -358,6 +403,24 @@
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
// TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
+ { // Test the batched version
+ std::vector<int32_t> inputs = {
+ -1,
+ static_cast<int32_t>(0xDEADBEEF),
+ NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE,
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+ NATIVE_WINDOW_CONCRETE_TYPE,
+ NATIVE_WINDOW_DEFAULT_WIDTH,
+ NATIVE_WINDOW_DEFAULT_HEIGHT,
+ NATIVE_WINDOW_TRANSFORM_HINT};
+ using QueryOutput = IGraphicBufferProducer::QueryOutput;
+ std::vector<QueryOutput> outputs;
+ EXPECT_OK(mProducer->query(inputs, &outputs));
+ for (const QueryOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
// Value was NULL
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
@@ -416,29 +479,113 @@
// Buffer was not in the dequeued state
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Queue
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
+ std::vector<QueueBufferInput> queueInputs;
+ queueInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ queueInputs.emplace_back(CreateBufferInput()).slot =
+ dequeueOutput.slot;
+ }
+ std::vector<QueueBufferOutput> queueOutputs;
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_OK(queueOutput.result);
+ }
+
+ // Re-queue
+ EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+ ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+ for (const QueueBufferOutput& queueOutput : queueOutputs) {
+ EXPECT_EQ(BAD_VALUE, queueOutput.result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+ using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
// Invalid slot number
{
// A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS,
input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = -1;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Slot was not in the dequeued state (all slots start out in Free state)
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = 0;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Put the slot into the "dequeued" state for the rest of the test
@@ -453,10 +600,22 @@
// Slot was enqueued without requesting a buffer
{
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferInput input = CreateBufferInput();
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Request the buffer so that the rest of the tests don't fail on earlier checks.
@@ -467,11 +626,23 @@
{
sp<Fence> nullFence = nullptr;
- IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInput input =
QueueBufferInputBuilder().setFence(nullFence).build();
- IGraphicBufferProducer::QueueBufferOutput output;
+ QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Scaling mode was unknown
@@ -482,9 +653,33 @@
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
+
input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Crop rect is out of bounds of the buffer dimensions
@@ -495,6 +690,18 @@
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(BAD_VALUE, output.result);
+ }
+ }
}
// Abandon the buffer queue so that the last test fails
@@ -507,6 +714,18 @@
// TODO(b/73267953): Make BufferHub honor producer and consumer connection.
EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+ { // Test with the batched version
+ constexpr size_t BATCH_SIZE = 16;
+ input.slot = dequeuedSlot;
+ std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+ std::vector<QueueBufferOutput> outputs;
+ EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+ ASSERT_EQ(inputs.size(), outputs.size());
+ for (const QueueBufferOutput& output : outputs) {
+ EXPECT_EQ(NO_INIT, output.result);
+ }
+ }
}
}
@@ -525,6 +744,44 @@
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
mProducer->cancelBuffer(dequeuedSlot, dequeuedFence);
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Cancel
+ using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+ std::vector<CancelBufferInput> cancelInputs;
+ cancelInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK,
+ ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ CancelBufferInput& cancelInput = cancelInputs.emplace_back();
+ cancelInput.slot = dequeueOutput.slot;
+ cancelInput.fence = dequeueOutput.fence;
+ }
+ std::vector<status_t> cancelOutputs;
+ EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs));
+ ASSERT_EQ(cancelInputs.size(), cancelOutputs.size());
+ for (status_t result : cancelOutputs) {
+ EXPECT_OK(result);
+ }
+ }
}
TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -541,11 +798,11 @@
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
- DequeueBufferResult result;
+ IGraphicBufferProducer::DequeueBufferOutput 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)))
+ TEST_PRODUCER_USAGE_BITS, &result)))
<< "iteration: " << i << ", slot: " << result.slot;
}
@@ -558,7 +815,6 @@
ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer));
ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output));
-
// Should now be able to dequeue up to maxBuffers times
int dequeuedSlot = -1;
sp<Fence> dequeuedFence;
@@ -794,6 +1050,71 @@
EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
EXPECT_OK(buffer->initCheck());
+
+ ASSERT_OK(mProducer->detachBuffer(slot));
+
+ { // Test batched methods
+ constexpr size_t BATCH_SIZE = 4;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+ // Dequeue
+ using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+ using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+ DequeueBufferInput dequeueInput;
+ dequeueInput.width = DEFAULT_WIDTH;
+ dequeueInput.height = DEFAULT_HEIGHT;
+ dequeueInput.format = DEFAULT_FORMAT;
+ dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+ dequeueInput.getTimestamps = false;
+ std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+ std::vector<DequeueBufferOutput> dequeueOutputs;
+ EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+ ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+ // Request
+ std::vector<int32_t> requestInputs;
+ requestInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ dequeueOutput.result);
+ requestInputs.emplace_back(dequeueOutput.slot);
+ }
+ using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+ std::vector<RequestBufferOutput> requestOutputs;
+ EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+ ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ EXPECT_OK(requestOutput.result);
+ }
+
+ // Detach
+ std::vector<int32_t> detachInputs;
+ detachInputs.reserve(BATCH_SIZE);
+ for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+ detachInputs.emplace_back(dequeueOutput.slot);
+ }
+ std::vector<status_t> detachOutputs;
+ EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs));
+ ASSERT_EQ(detachInputs.size(), detachOutputs.size());
+ for (status_t result : detachOutputs) {
+ EXPECT_OK(result);
+ }
+
+ // Attach
+ using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput;
+ std::vector<sp<GraphicBuffer>> attachInputs;
+ attachInputs.reserve(BATCH_SIZE);
+ for (const RequestBufferOutput& requestOutput : requestOutputs) {
+ attachInputs.emplace_back(requestOutput.buffer);
+ }
+ std::vector<AttachBufferOutput> attachOutputs;
+ EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs));
+ ASSERT_EQ(attachInputs.size(), attachOutputs.size());
+ for (const AttachBufferOutput& attachOutput : attachOutputs) {
+ EXPECT_OK(attachOutput.result);
+ EXPECT_NE(-1, attachOutput.slot);
+ }
+ }
}
#if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index fa98cd4c..63db9a7 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -2024,4 +2024,86 @@
EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
}
+TEST_F(SurfaceTest, BatchOperations) {
+ const int BUFFER_COUNT = 16;
+ const int BATCH_SIZE = 8;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<StubProducerListener> listener = new StubProducerListener();
+
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+ /*reportBufferRemoval*/false));
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+
+ // Batch dequeued buffers can be queued individually
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ ANativeWindowBuffer* buffer = buffers[i].buffer;
+ int fence = buffers[i].fenceFd;
+ ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+ }
+
+ // Batch dequeued buffers can be canceled individually
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ ANativeWindowBuffer* buffer = buffers[i].buffer;
+ int fence = buffers[i].fenceFd;
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+ }
+
+ // Batch dequeued buffers can be batch cancelled
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers));
+
+ // Batch dequeued buffers can be batch queued
+ ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+ for (size_t i = 0; i < BATCH_SIZE; i++) {
+ queuedBuffers[i].buffer = buffers[i].buffer;
+ queuedBuffers[i].fenceFd = buffers[i].fenceFd;
+ queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ }
+ ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers));
+
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
+TEST_F(SurfaceTest, BatchIllegalOperations) {
+ const int BUFFER_COUNT = 16;
+ const int BATCH_SIZE = 8;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<StubProducerListener> listener = new StubProducerListener();
+
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+ /*reportBufferRemoval*/false));
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+
+ // Batch operations are invalid in shared buffer mode
+ surface->setSharedBufferMode(true);
+ ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers));
+ ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers));
+ ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers));
+ surface->setSharedBufferMode(false);
+
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
} // namespace android
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 07e5d86..3011dcc 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -28,6 +28,7 @@
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
min_sdk_version: "29",
+ host_supported: true,
}
ndk_library {
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 0e74c63..d69c7ae 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -79,6 +79,7 @@
name: "librenderengine_skia_sources",
srcs: [
"skia/AutoBackendTexture.cpp",
+ "skia/ColorSpaces.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
"skia/debug/CaptureTimer.cpp",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 3e65d9a..45db31c 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -41,6 +41,9 @@
if (strcmp(prop, "skiagl") == 0) {
renderEngineType = RenderEngineType::SKIA_GL;
}
+ if (strcmp(prop, "skiaglthreaded") == 0) {
+ renderEngineType = RenderEngineType::SKIA_GL_THREADED;
+ }
switch (renderEngineType) {
case RenderEngineType::THREADED:
@@ -49,6 +52,10 @@
[args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
case RenderEngineType::SKIA_GL:
return renderengine::skia::SkiaGLRenderEngine::create(args);
+ case RenderEngineType::SKIA_GL_THREADED:
+ return renderengine::threaded::RenderEngineThreaded::create([args]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(args);
+ });
case RenderEngineType::GLES:
default:
ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 9157066..506f81e 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -82,6 +82,7 @@
GLES = 1,
THREADED = 2,
SKIA_GL = 3,
+ SKIA_GL_THREADED = 4,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index d126c27..c535597 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,8 +20,7 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <utils/Trace.h>
-
+#include "ColorSpaces.h"
#include "log/log_main.h"
#include "utils/Trace.h"
@@ -29,49 +28,9 @@
namespace renderengine {
namespace skia {
-// Converts an android dataspace to a supported SkColorSpace
-// Supported dataspaces are
-// 1. sRGB
-// 2. Display P3
-// 3. BT2020 PQ
-// 4. BT2020 HLG
-// Unknown primaries are mapped to BT709, and unknown transfer functions
-// are mapped to sRGB.
-static sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
- skcms_Matrix3x3 gamut;
- switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- gamut = SkNamedGamut::kSRGB;
- break;
- case HAL_DATASPACE_STANDARD_BT2020:
- gamut = SkNamedGamut::kRec2020;
- break;
- case HAL_DATASPACE_STANDARD_DCI_P3:
- gamut = SkNamedGamut::kDisplayP3;
- break;
- default:
- ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
- gamut = SkNamedGamut::kSRGB;
- break;
- }
-
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_LINEAR:
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
- case HAL_DATASPACE_TRANSFER_SRGB:
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
- case HAL_DATASPACE_TRANSFER_ST2084:
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
- case HAL_DATASPACE_TRANSFER_HLG:
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
- default:
- ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
- }
-}
-
AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
bool isRender) {
+ ATRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
new file mode 100644
index 0000000..ff4d348
--- /dev/null
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 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 "ColorSpaces.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
+ skcms_Matrix3x3 gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkNamedGamut::kRec2020;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkNamedGamut::kDisplayP3;
+ break;
+ default:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ default:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/ColorSpaces.h b/libs/renderengine/skia/ColorSpaces.h
new file mode 100644
index 0000000..2cbdeb8
--- /dev/null
+++ b/libs/renderengine/skia/ColorSpaces.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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 "SkColorSpace.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace);
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 46ae18c..dc04f69 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -19,25 +19,10 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <cstdint>
-#include <memory>
-
-#include "SkImageInfo.h"
-#include "log/log_main.h"
-#include "system/graphics-base-v1.0.h"
+#include "SkiaGLRenderEngine.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <sync/sync.h>
-#include <ui/BlurRegion.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Trace.h>
-#include "../gl/GLExtensions.h"
-#include "SkiaGLRenderEngine.h"
-#include "filters/BlurFilter.h"
-#include "filters/LinearEffect.h"
-#include "skia/debug/SkiaCapture.h"
-
#include <GrContextOptions.h>
#include <SkCanvas.h>
#include <SkColorFilter.h>
@@ -45,11 +30,28 @@
#include <SkColorSpace.h>
#include <SkImage.h>
#include <SkImageFilters.h>
+#include <SkRegion.h>
#include <SkShadowUtils.h>
#include <SkSurface.h>
#include <gl/GrGLInterface.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
#include <cmath>
+#include <cstdint>
+#include <memory>
+
+#include "../gl/GLExtensions.h"
+#include "ColorSpaces.h"
+#include "SkBlendMode.h"
+#include "SkImageInfo.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+#include "log/log_main.h"
+#include "skia/debug/SkiaCapture.h"
+#include "system/graphics-base-v1.0.h"
bool checkGlError(const char* op, int lineNumber);
@@ -281,6 +283,44 @@
if (args.supportsBackgroundBlur) {
mBlurFilter = new BlurFilter();
}
+ mCapture = std::make_unique<SkiaCapture>();
+}
+
+SkiaGLRenderEngine::~SkiaGLRenderEngine() {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mRuntimeEffects.clear();
+ mProtectedTextureCache.clear();
+ mTextureCache.clear();
+
+ if (mBlurFilter) {
+ delete mBlurFilter;
+ }
+
+ mCapture = nullptr;
+
+ mGrContext->flushAndSubmit(true);
+ mGrContext->abandonContext();
+
+ if (mProtectedGrContext) {
+ mProtectedGrContext->flushAndSubmit(true);
+ mProtectedGrContext->abandonContext();
+ }
+
+ if (mPlaceholderSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
+ }
+ if (mProtectedPlaceholderSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mProtectedPlaceholderSurface);
+ }
+ if (mEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mEGLContext);
+ }
+ if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
+ }
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEGLDisplay);
+ eglReleaseThread();
}
bool SkiaGLRenderEngine::supportsProtectedContent() const {
@@ -298,6 +338,7 @@
useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+
if (success) {
mInProtectedContext = useProtectedContext;
}
@@ -418,12 +459,39 @@
mProtectedTextureCache.erase(bufferId);
}
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
+ const LayerSettings* layer,
+ const DisplaySettings& display,
+ bool undoPremultipliedAlpha) {
+ if (mUseColorManagement &&
+ needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
+ LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
+ .outputDataspace = display.outputDataspace,
+ .undoPremultipliedAlpha = undoPremultipliedAlpha};
+
+ auto effectIter = mRuntimeEffects.find(effect);
+ sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
+ if (effectIter == mRuntimeEffects.end()) {
+ runtimeEffect = buildRuntimeEffect(effect);
+ mRuntimeEffects.insert({effect, runtimeEffect});
+ } else {
+ runtimeEffect = effectIter->second;
+ }
+ return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
+ display.maxLuminance,
+ layer->source.buffer.maxMasteringLuminance,
+ layer->source.buffer.maxContentLuminance);
+ }
+ return shader;
+}
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer,
const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
ATRACE_NAME("SkiaGL::drawLayers");
+
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
@@ -462,7 +530,7 @@
if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) {
surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
surfaceTextureRef->setTexture(
- new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), true));
+ new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true));
if (useFramebufferCache) {
ALOGD("Adding to cache");
cache.insert({buffer->getId(), surfaceTextureRef});
@@ -472,10 +540,10 @@
sk_sp<SkSurface> surface =
surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
? display.outputDataspace
- : ui::Dataspace::SRGB,
- mGrContext.get());
+ : ui::Dataspace::UNKNOWN,
+ grContext.get());
- SkCanvas* canvas = mCapture.tryCapture(surface.get());
+ SkCanvas* canvas = mCapture->tryCapture(surface.get());
if (canvas == nullptr) {
ALOGE("Cannot acquire canvas from Skia.");
return BAD_VALUE;
@@ -484,7 +552,7 @@
canvas->clear(SK_ColorTRANSPARENT);
canvas->save();
- if (mCapture.isCaptureRunning()) {
+ if (mCapture->isCaptureRunning()) {
// Record display settings when capture is running.
std::stringstream displaySettings;
PrintTo(display, &displaySettings);
@@ -520,10 +588,33 @@
canvas->rotate(toDegrees(display.orientation));
canvas->translate(-clipWidth / 2, -clipHeight / 2);
canvas->translate(-display.clip.left, -display.clip.top);
+
+ // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
+ // view is still on-screen. The clear region could be re-specified as a black color layer,
+ // however.
+ if (!display.clearRegion.isEmpty()) {
+ size_t numRects = 0;
+ Rect const* rects = display.clearRegion.getArray(&numRects);
+ SkIRect skRects[numRects];
+ for (int i = 0; i < numRects; ++i) {
+ skRects[i] =
+ SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
+ }
+ SkRegion clearRegion;
+ SkPaint paint;
+ sk_sp<SkShader> shader =
+ SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
+ toSkColorSpace(mUseColorManagement ? display.outputDataspace
+ : ui::Dataspace::UNKNOWN));
+ paint.setShader(shader);
+ clearRegion.setRects(skRects, numRects);
+ canvas->drawRegion(clearRegion, paint);
+ }
+
for (const auto& layer : layers) {
canvas->save();
- if (mCapture.isCaptureRunning()) {
+ if (mCapture->isCaptureRunning()) {
// Record the name of the layer if the capture is running.
std::stringstream layerSettings;
PrintTo(*layer, &layerSettings);
@@ -562,6 +653,16 @@
}
}
+ const ui::Dataspace targetDataspace = mUseColorManagement
+ ? (needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
+ display.outputDataspace)
+ // If we need to map to linear space, then mark the source image with the
+ // same colorspace as the destination surface so that Skia's color
+ // management is a no-op.
+ ? display.outputDataspace
+ : layer->sourceDataspace)
+ : ui::Dataspace::UNKNOWN;
+
if (layer->source.buffer.buffer) {
ATRACE_NAME("DrawImage");
const auto& item = layer->source.buffer;
@@ -571,31 +672,18 @@
imageTextureRef = iter->second;
} else {
imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
- imageTextureRef->setTexture(new AutoBackendTexture(mGrContext.get(),
+ imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(),
item.buffer->toAHardwareBuffer(),
false));
- mTextureCache.insert({buffer->getId(), imageTextureRef});
+ mTextureCache.insert({item.buffer->getId(), imageTextureRef});
}
sk_sp<SkImage> image =
- imageTextureRef->getTexture()
- ->makeImage(mUseColorManagement
- ? (needsLinearEffect(layer->colorTransform,
- layer->sourceDataspace,
- display.outputDataspace)
- // If we need to map to linear space,
- // then mark the source image with the
- // same colorspace as the destination
- // surface so that Skia's color
- // management is a no-op.
- ? display.outputDataspace
- : layer->sourceDataspace)
- : ui::Dataspace::SRGB,
- item.isOpaque ? kOpaque_SkAlphaType
- : (item.usePremultipliedAlpha
- ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType),
- mGrContext.get());
+ imageTextureRef->getTexture()->makeImage(targetDataspace,
+ item.usePremultipliedAlpha
+ ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType,
+ grContext.get());
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -624,44 +712,52 @@
shader = image->makeShader(SkSamplingOptions(), matrix);
}
- if (mUseColorManagement &&
- needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
- display.outputDataspace)) {
- LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
- .outputDataspace = display.outputDataspace,
- .undoPremultipliedAlpha = !item.isOpaque &&
- item.usePremultipliedAlpha};
-
- auto effectIter = mRuntimeEffects.find(effect);
- sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
- if (effectIter == mRuntimeEffects.end()) {
- runtimeEffect = buildRuntimeEffect(effect);
- mRuntimeEffects.insert({effect, runtimeEffect});
- } else {
- runtimeEffect = effectIter->second;
- }
-
- paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
- layer->colorTransform,
- display.maxLuminance,
- layer->source.buffer.maxMasteringLuminance,
- layer->source.buffer.maxContentLuminance));
- } else {
- paint.setShader(shader);
+ // Handle opaque images - it's a little nonstandard how we do this.
+ // Fundamentally we need to support SurfaceControl.Builder#setOpaque:
+ // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+ // The important language is that when isOpaque is set, opacity is not sampled from the
+ // alpha channel, but blending may still be supported on a transaction via setAlpha. So,
+ // here's the conundrum:
+ // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated
+ // as an internal hint - composition is undefined when there are alpha bits present.
+ // 2. We can try to lie about the pixel layout, but that only works for RGBA8888
+ // buffers, i.e., treating them as RGBx8888 instead. But we can't do the same for
+ // RGBA1010102 because RGBx1010102 is not supported as a pixel layout for SkImages. It's
+ // also not clear what to use for F16 either, and lying about the pixel layout is a bit
+ // of a hack anyways.
+ // 3. We can't change the blendmode to src, because while this satisfies the requirement
+ // for ignoring the alpha channel, it doesn't quite satisfy the blending requirement
+ // because src always clobbers the destination content.
+ //
+ // So, what we do here instead is an additive blend mode where we compose the input
+ // image with a solid black. This might need to be reassess if this does not support
+ // FP16 incredibly well, but FP16 end-to-end isn't well supported anyway at the moment.
+ if (item.isOpaque) {
+ shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
+ SkShaders::Color(SkColors::kBlack,
+ toSkColorSpace(targetDataspace)));
}
- // Make sure to take into the account the alpha set on the layer.
+
+ paint.setShader(
+ createRuntimeEffectShader(shader, layer, display,
+ !item.isOpaque && item.usePremultipliedAlpha));
paint.setAlphaf(layer->alpha);
} else {
ATRACE_NAME("DrawColor");
const auto color = layer->source.solidColor;
- paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
- .fG = color.g,
- .fB = color.b,
- layer->alpha},
- nullptr));
+ sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
+ .fG = color.g,
+ .fB = color.b,
+ .fA = layer->alpha},
+ toSkColorSpace(targetDataspace));
+ paint.setShader(createRuntimeEffectShader(shader, layer, display,
+ /* undoPremultipliedAlpha */ false));
}
- paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+ sk_sp<SkColorFilter> filter =
+ SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+
+ paint.setColorFilter(filter);
for (const auto effectRegion : layer->blurRegions) {
drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]);
@@ -686,7 +782,7 @@
canvas->restore();
}
canvas->restore();
- mCapture.endCapture();
+ mCapture->endCapture();
{
ATRACE_NAME("flush surface");
surface->flush();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 3d8c693..ed62a2a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -34,9 +34,9 @@
#include "SkImageInfo.h"
#include "SkiaRenderEngine.h"
#include "android-base/macros.h"
+#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
-#include "skia/debug/SkiaCapture.h"
-#include "skia/filters/LinearEffect.h"
+#include "filters/LinearEffect.h"
namespace android {
namespace renderengine {
@@ -48,7 +48,7 @@
SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
- ~SkiaGLRenderEngine() override{};
+ ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
void unbindExternalTextureBuffer(uint64_t bufferId) override;
status_t drawLayers(const DisplaySettings& display,
@@ -91,6 +91,11 @@
void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect,
sk_sp<SkSurface> blurredSurface);
SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect);
+ // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
+ // shader. Otherwise it returns the input shader.
+ sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+ const DisplaySettings& display,
+ bool undoPremultipliedAlpha);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
@@ -122,7 +127,7 @@
bool mInProtectedContext = false;
// Object to capture commands send to Skia.
- SkiaCapture mCapture;
+ std::unique_ptr<SkiaCapture> mCapture;
};
} // namespace skia
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index 8006a11..e9cfead 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -129,8 +129,14 @@
// SkDocuments don't take ownership of the streams they write.
// we need to keep it until after mMultiPic.close()
// procs is passed as a pointer, but just as a method of having an optional default.
- // procs doesn't need to outlive this Make call.
- mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs);
+ // procs doesn't need to outlive this Make call
+ // The last argument is a callback for the endPage behavior.
+ // See SkSharingProc.h for more explanation of this callback.
+ mMultiPic = SkMakeMultiPictureDocument(
+ mOpenMultiPicStream.get(), &procs,
+ [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
+ SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
+ });
mCaptureRunning = true;
return true;
} else {
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 7680649..84af016 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -454,11 +454,18 @@
effectBuilder.child("input") = shader;
- ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
- ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+ effectBuilder.uniform("in_rgbToXyz") = mat4();
+ effectBuilder.uniform("in_xyzToRgb") = colorTransform;
+ } else {
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
- effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
- effectBuilder.uniform("in_xyzToRgb") = colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
+ effectBuilder.uniform("in_xyzToRgb") =
+ colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ }
+
effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
effectBuilder.uniform("in_inputMaxLuminance") =
std::min(maxMasteringLuminance, maxContentLuminance);
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index bcf389b..51c7028 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -14,17 +14,21 @@
cc_test {
name: "librenderengine_test",
- defaults: ["surfaceflinger_defaults"],
+ defaults: ["skia_deps", "surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
"RenderEngineThreadedTest.cpp",
],
+ include_dirs: [
+ "external/skia/src/gpu",
+ ],
static_libs: [
"libgmock",
"librenderengine",
"librenderengine_mocks",
],
+
shared_libs: [
"libbase",
"libcutils",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 15fb1b8..58afe6e 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,16 +22,18 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
-#include <chrono>
-#include <condition_variable>
-#include <fstream>
-
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <fstream>
+
#include "../gl/GLESRenderEngine.h"
+#include "../skia/SkiaGLRenderEngine.h"
#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
@@ -46,14 +48,26 @@
virtual ~RenderEngineFactory() = default;
virtual std::string name() = 0;
- virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() = 0;
+ virtual renderengine::RenderEngine::RenderEngineType type() = 0;
+ virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
+ virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
+ return nullptr;
+ }
};
class GLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "GLESRenderEngineFactory"; }
- std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::GLES;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ return createGLESRenderEngine();
+ }
+
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -63,7 +77,7 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .setRenderEngineType(type())
.build();
return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
@@ -73,7 +87,15 @@
public:
std::string name() override { return "GLESCMRenderEngineFactory"; }
- std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::GLES;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ return createGLESRenderEngine();
+ }
+
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() override {
renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -82,7 +104,7 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .setRenderEngineType(type())
.setUseColorManagerment(true)
.build();
return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
@@ -91,9 +113,13 @@
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
- std::string name() override { return "SkiaGLESRenderEngineFactory"; }
+ std::string name() override { return "SkiaGLRenderEngineFactory"; }
- std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -102,17 +128,21 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::SKIA_GL)
+ .setRenderEngineType(type())
.build();
- return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+ return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
};
class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
public:
- std::string name() override { return "SkiaGLESCMRenderEngineFactory"; }
+ std::string name() override { return "SkiaGLCMRenderEngineFactory"; }
- std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -121,10 +151,10 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::SKIA_GL)
+ .setRenderEngineType(type())
.setUseColorManagerment(true)
.build();
- return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+ return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
};
@@ -134,7 +164,7 @@
return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER,
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
"output");
}
@@ -159,7 +189,9 @@
}
for (uint32_t texName : mTexNames) {
mRE->deleteTextures(1, &texName);
- EXPECT_FALSE(mRE->isTextureNameKnownForTesting(texName));
+ if (mGLESRE != nullptr) {
+ EXPECT_FALSE(mGLESRE->isTextureNameKnownForTesting(texName));
+ }
}
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
@@ -351,12 +383,11 @@
}
void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer) {
+ std::vector<const renderengine::LayerSettings*> layers) {
base::unique_fd fence;
status_t status =
- mRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
- mCurrentBuffer = buffer;
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+ mCurrentBuffer = mBuffer;
int fd = fence.release();
if (fd >= 0) {
@@ -365,17 +396,15 @@
}
ASSERT_EQ(NO_ERROR, status);
- if (layers.size() > 0) {
- ASSERT_TRUE(mRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ if (layers.size() > 0 && mGLESRE != nullptr) {
+ ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
}
}
void drawEmptyLayers() {
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
- // Meaningless buffer since we don't do any drawing
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- invokeDraw(settings, layers, buffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -471,7 +500,12 @@
const renderengine::ShadowSettings& shadow,
const ubyte4& backgroundColor);
- std::unique_ptr<renderengine::gl::GLESRenderEngine> mRE;
+ void initializeRenderEngine();
+
+ std::unique_ptr<renderengine::RenderEngine> mRE;
+ // GLESRenderEngine for testing GLES-specific behavior.
+ // Owened by mRE, but this is downcasted.
+ renderengine::gl::GLESRenderEngine* mGLESRE = nullptr;
// Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
// be freed *after* RenderEngine is destroyed, so that the EGL image is
@@ -483,6 +517,21 @@
std::vector<uint32_t> mTexNames;
};
+void RenderEngineTest::initializeRenderEngine() {
+ const auto& renderEngineFactory = GetParam();
+ if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ // Only GLESRenderEngine exposes test-only methods. Provide a pointer to the
+ // GLESRenderEngine if we're using it so that we don't need to dynamic_cast
+ // every time.
+ std::unique_ptr<renderengine::gl::GLESRenderEngine> renderEngine =
+ renderEngineFactory->createGLESRenderEngine();
+ mGLESRE = renderEngine.get();
+ mRE = std::move(renderEngine);
+ } else {
+ mRE = renderEngineFactory->createRenderEngine();
+ }
+}
+
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
RenderEngineTest* /*fixture*/) {
@@ -509,7 +558,7 @@
static uint8_t getAlphaChannel() {
// The isOpaque bit will override the alpha channel, so this should be
// arbitrary.
- return 10;
+ return 50;
}
};
@@ -563,7 +612,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -606,7 +655,7 @@
layer.alpha = 1.0f;
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -659,7 +708,7 @@
layers.push_back(&layerTwo);
layers.push_back(&layerThree);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -747,7 +796,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -786,7 +835,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -816,7 +865,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -844,7 +893,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
template <typename SourceVariant>
@@ -904,7 +953,7 @@
blurLayer.alpha = 0;
layers.push_back(&blurLayer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
50 /* tolerance */);
@@ -929,7 +978,7 @@
layerOne.alpha = 0.2;
layersFirst.push_back(&layerOne);
- invokeDraw(settings, layersFirst, mBuffer);
+ invokeDraw(settings, layersFirst);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -945,7 +994,7 @@
layerTwo.alpha = 1.0f;
layersSecond.push_back(&layerTwo);
- invokeDraw(settings, layersSecond, mBuffer);
+ invokeDraw(settings, layersSecond);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
@@ -997,7 +1046,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferTextureTransform() {
@@ -1036,7 +1085,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
@@ -1075,7 +1124,7 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
@@ -1093,7 +1142,7 @@
// fake layer, without bounds should not render anything
renderengine::LayerSettings layer;
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::clearRegion() {
@@ -1140,7 +1189,7 @@
casterColor.b / 255.0f, this);
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
@@ -1171,7 +1220,7 @@
shadowLayer.shadow = shadow;
layers.push_back(&shadowLayer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
}
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
@@ -1181,17 +1230,46 @@
std::make_shared<SkiaGLESCMRenderEngineFactory>()));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
drawEmptyLayers();
}
-TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
+TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
const auto& renderEngineFactory = GetParam();
mRE = renderEngineFactory->createRenderEngine();
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ // 255, 255, 255, 255 is full opaque white.
+ const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+ // Create layer with given color.
+ renderengine::LayerSettings bgLayer;
+ bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ bgLayer.source.solidColor = half3(backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ // Transform the red color.
+ bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ layers.push_back(&bgLayer);
+
+ invokeDraw(settings, layers);
+
+ // Expect to see full opaque pixel (with inverted red from the transform).
+ expectBufferColor(Rect(0, 0, 10, 10), 0.f, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
+TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
std::vector<const renderengine::LayerSettings*> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
@@ -1204,8 +1282,7 @@
}
TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1227,7 +1304,13 @@
TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1244,355 +1327,264 @@
status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
mCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
- ASSERT_FALSE(mRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+ ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillGreenBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBlueBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedTransparentBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferPhysicalOffset<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate0<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate90<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate180<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate270<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferLayerTransform<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferColorTransform<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferAndBlurBackground<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
overlayCorners<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
-TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
+ initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferTextureTransform();
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferWithPremultiplyAlpha();
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
fillBufferWithoutPremultiplyAlpha();
}
TEST_P(RenderEngineTest, drawLayers_clearRegion) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
-
+ initializeRenderEngine();
clearRegion();
}
TEST_P(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1606,26 +1598,32 @@
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
uint64_t bufferId = layer.source.buffer.buffer->getId();
- EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
+ EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- mRE->unbindExternalTextureBufferForTesting(bufferId);
+ mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
std::lock_guard<std::mutex> lock(barrier->mutex);
ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
[&]() REQUIRES(barrier->mutex) {
return barrier->isOpen;
}));
- EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
+ EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
EXPECT_EQ(NO_ERROR, barrier->result);
}
TEST_P(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- mRE->cacheExternalTextureBufferForTesting(nullptr);
+ mGLESRE->cacheExternalTextureBufferForTesting(nullptr);
std::lock_guard<std::mutex> lock(barrier->mutex);
ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
[&]() REQUIRES(barrier->mutex) {
@@ -1637,12 +1635,18 @@
TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
uint64_t bufferId = buf->getId();
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- mRE->cacheExternalTextureBufferForTesting(buf);
+ mGLESRE->cacheExternalTextureBufferForTesting(buf);
{
std::lock_guard<std::mutex> lock(barrier->mutex);
ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
@@ -1651,8 +1655,8 @@
}));
EXPECT_EQ(NO_ERROR, barrier->result);
}
- EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
- barrier = mRE->unbindExternalTextureBufferForTesting(bufferId);
+ EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
+ barrier = mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
{
std::lock_guard<std::mutex> lock(barrier->mutex);
ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
@@ -1661,12 +1665,11 @@
}));
EXPECT_EQ(NO_ERROR, barrier->result);
}
- EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
+ EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 backgroundColor(255, 255, 255, 255);
const float shadowLength = 5.0f;
@@ -1681,8 +1684,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
@@ -1701,8 +1703,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
@@ -1722,8 +1723,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
@@ -1744,8 +1744,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
@@ -1767,8 +1766,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
const ubyte4 backgroundColor(255, 255, 255, 255);
@@ -1796,7 +1794,13 @@
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1828,7 +1832,13 @@
TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ // GLES-specific test
+ return;
+ }
+
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1852,20 +1862,19 @@
uint64_t bufferId = layer.source.buffer.buffer->getId();
uint32_t texName = layer.source.buffer.textureName;
- EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(bufferId, mRE->getBufferIdForTextureNameForTesting(texName));
+ EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
// Now check that our view of memory is good.
- EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(std::nullopt, mRE->getBufferIdForTextureNameForTesting(bufferId));
- EXPECT_TRUE(mRE->isTextureNameKnownForTesting(texName));
+ EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName));
}
TEST_P(RenderEngineTest, testRoundedCornersCrop) {
- const auto& renderEngineFactory = GetParam();
- mRE = renderEngineFactory->createRenderEngine();
+ initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1900,7 +1909,7 @@
layers.push_back(&greenLayer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers);
// Corners should be ignored...
// Screen size: width is 128, height is 256.
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index f973cba..7bddee6 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -64,6 +64,15 @@
}
}
+void SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ service->addIndividualSensorPrivacyListener(userId, sensor, listener);
+ }
+}
+
void SensorPrivacyManager::removeSensorPrivacyListener(
const sp<hardware::ISensorPrivacyListener>& listener)
{
@@ -85,6 +94,18 @@
return false;
}
+bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ bool result;
+ service->isIndividualSensorPrivacyEnabled(userId, sensor, &result);
+ return result;
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+ return false;
+}
+
status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient)
{
sp<hardware::ISensorPrivacyManager> service = getService();
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 4c2d5db..629b8c2 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -22,9 +22,17 @@
interface ISensorPrivacyManager {
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+ void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
+
void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
boolean isSensorPrivacyEnabled();
+ boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+
void setSensorPrivacy(boolean enable);
+
+ void setIndividualSensorPrivacy(int userId, int sensor, boolean enable);
+
+ void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable);
}
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 2546a68..bd7c726 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -28,11 +28,19 @@
class SensorPrivacyManager
{
public:
+ enum {
+ INDIVIDUAL_SENSOR_MICROPHONE = 1,
+ INDIVIDUAL_SENSOR_CAMERA = 2
+ };
+
SensorPrivacyManager();
void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+ void addIndividualSensorPrivacyListener(int userId, int sensor,
+ const sp<hardware::ISensorPrivacyListener>& listener);
void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
bool isSensorPrivacyEnabled();
+ bool isIndividualSensorPrivacyEnabled(int userId, int sensor);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 48abdce..8b94f61 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -54,6 +54,7 @@
cc_library_headers {
name: "gl_headers",
+ host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 398efc0..f576660 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -128,6 +128,7 @@
"EGL_KHR_stream_producer_eglsurface "
"EGL_KHR_surfaceless_context "
"EGL_KHR_wait_sync " // strongly recommended
+ "EGL_NV_context_priority_realtime "
"EGL_NV_system_time "
;
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 6235f06..0d17265 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -84,11 +84,13 @@
return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
+ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+ audio_port_handle_t deviceId) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
+ data.writeInt32((int32_t) deviceId);
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index c5f8859..e916221 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -59,7 +59,6 @@
}
void SetUp() override {
- SKIP_IF_BPF_NOT_SUPPORTED;
bpf::setrlimitForTest();
mGpuMem = std::make_unique<GpuMem>();
@@ -87,8 +86,6 @@
};
TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
@@ -97,20 +94,16 @@
}
TEST_F(GpuMemTest, bpfInitializationFailed) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
}
TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
- SKIP_IF_BPF_NOT_SUPPORTED;
mTestableGpuMem.setGpuMemTotalMap(mTestMap);
EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
}
TEST_F(GpuMemTest, globalMemTotal) {
- SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
mTestableGpuMem.setGpuMemTotalMap(mTestMap);
@@ -118,7 +111,6 @@
}
TEST_F(GpuMemTest, missingGlobalMemTotal) {
- SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
mTestableGpuMem.setGpuMemTotalMap(mTestMap);
@@ -126,7 +118,6 @@
}
TEST_F(GpuMemTest, procMemTotal) {
- SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
mTestableGpuMem.setGpuMemTotalMap(mTestMap);
@@ -146,7 +137,6 @@
}
TEST_F(GpuMemTest, traverseGpuMemTotals) {
- SKIP_IF_BPF_NOT_SUPPORTED;
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 4e55872..887bdd4 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -55,12 +55,18 @@
ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
- void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
- const std::string& reason) override {
- ALOGE("Connection is not responding: %s", reason.c_str());
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+ const std::string& reason) override {
+ ALOGE("Window is not responding: %s", reason.c_str());
}
- void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {}
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {}
+
+ void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override {
+ ALOGE("Monitor is not responding: %s", reason.c_str());
+ }
+
+ void notifyMonitorResponsive(int32_t pid) override {}
void notifyInputChannelBroken(const sp<IBinder>&) override {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 3a860f0..26b641d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -271,6 +271,7 @@
sp<IBinder> newToken;
std::string obscuringPackage;
bool enabled;
+ int32_t pid;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f3d969e..cdc74f3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -453,6 +453,20 @@
return event;
}
+static std::optional<int32_t> findMonitorPidByToken(
+ const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
+ const sp<IBinder>& token) {
+ for (const auto& it : monitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ return monitor.pid;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -615,7 +629,7 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(*connection);
+ onAnrLocked(connection);
return LONG_LONG_MIN;
}
@@ -1266,14 +1280,20 @@
// Disable Pointer Capture
token = mWindowTokenWithPointerCapture;
mWindowTokenWithPointerCapture = nullptr;
- mFocusedWindowRequestedPointerCapture = false;
+ if (mFocusedWindowRequestedPointerCapture) {
+ mFocusedWindowRequestedPointerCapture = false;
+ setPointerCaptureLocked(false);
+ }
}
auto channel = getInputChannelLocked(token);
if (channel == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
- mFocusedWindowRequestedPointerCapture = false;
+ if (mFocusedWindowRequestedPointerCapture) {
+ mFocusedWindowRequestedPointerCapture = false;
+ setPointerCaptureLocked(false);
+ }
return;
}
InputTarget target;
@@ -5148,6 +5168,14 @@
return std::nullopt;
}
+std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+ std::optional<int32_t> gesturePid = findMonitorPidByToken(mGestureMonitorsByDisplay, token);
+ if (gesturePid.has_value()) {
+ return gesturePid;
+ }
+ return findMonitorPidByToken(mGlobalMonitorsByDisplay, token);
+}
+
sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
@@ -5208,12 +5236,15 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const Connection& connection) {
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+ if (connection == nullptr) {
+ LOG_ALWAYS_FATAL("Caller must check for nullness");
+ }
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection.waitQueue.empty()) {
+ if (connection->waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection.inputChannel->getName().c_str());
+ connection->inputChannel->getName().c_str());
return;
}
/**
@@ -5224,21 +5255,20 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection.waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection->waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection.inputChannel->getName().c_str(),
+ connection->inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
- sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
+ sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();
updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible);
- commandEntry->connectionToken = connectionToken;
- commandEntry->reason = std::move(reason);
- postCommandLocked(std::move(commandEntry));
+ processConnectionUnresponsiveLocked(*connection, std::move(reason));
+
+ // Stop waking up for events on this connection, it is already unresponsive
+ cancelEventsForAnrLocked(connection);
}
void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
@@ -5323,26 +5353,34 @@
mLock.lock();
}
-void InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible(
- CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->notifyConnectionUnresponsive(commandEntry->connectionToken, commandEntry->reason);
+ mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
mLock.lock();
-
- // stop waking up for events in this connection, it is already not responding
- sp<Connection> connection = getConnectionLocked(commandEntry->connectionToken);
- if (connection == nullptr) {
- return;
- }
- cancelEventsForAnrLocked(connection);
}
-void InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->notifyConnectionResponsive(commandEntry->connectionToken);
+ mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyMonitorResponsive(commandEntry->pid);
mLock.lock();
}
@@ -5447,13 +5485,8 @@
if (!connection->responsive) {
connection->responsive = isConnectionResponsive(*connection);
if (connection->responsive) {
- // The connection was unresponsive, and now it's responsive. Tell the policy
- // about it so that it can stop ANR.
- std::unique_ptr<CommandEntry> connectionResponsiveCommand =
- std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible);
- connectionResponsiveCommand->connectionToken = connectionToken;
- postCommandLocked(std::move(connectionResponsiveCommand));
+ // The connection was unresponsive, and now it's responsive.
+ processConnectionResponsiveLocked(*connection);
}
}
traceWaitQueueLength(connection);
@@ -5469,6 +5502,82 @@
startDispatchCycleLocked(now(), connection);
}
+void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
+ std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
+ monitorUnresponsiveCommand->pid = pid;
+ monitorUnresponsiveCommand->reason = std::move(reason);
+ postCommandLocked(std::move(monitorUnresponsiveCommand));
+}
+
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+ std::string reason) {
+ std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
+ windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
+ windowUnresponsiveCommand->reason = std::move(reason);
+ postCommandLocked(std::move(windowUnresponsiveCommand));
+}
+
+void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
+ std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
+ monitorResponsiveCommand->pid = pid;
+ postCommandLocked(std::move(monitorResponsiveCommand));
+}
+
+void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
+ std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
+ windowResponsiveCommand->connectionToken = std::move(connectionToken);
+ postCommandLocked(std::move(windowResponsiveCommand));
+}
+
+/**
+ * Tell the policy that a connection has become unresponsive so that it can start ANR.
+ * Check whether the connection of interest is a monitor or a window, and add the corresponding
+ * command entry to the command queue.
+ */
+void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
+ std::string reason) {
+ const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ if (connection.monitor) {
+ ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+ reason.c_str());
+ std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+ if (!pid.has_value()) {
+ ALOGE("Could not find unresponsive monitor for connection %s",
+ connection.inputChannel->getName().c_str());
+ return;
+ }
+ sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
+ return;
+ }
+ // If not a monitor, must be a window
+ ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+ reason.c_str());
+ sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
+}
+
+/**
+ * Tell the policy that a connection has become responsive so that it can stop ANR.
+ */
+void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
+ const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+ if (connection.monitor) {
+ std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+ if (!pid.has_value()) {
+ ALOGE("Could not find responsive monitor for connection %s",
+ connection.inputChannel->getName().c_str());
+ return;
+ }
+ sendMonitorResponsiveCommandLocked(pid.value());
+ return;
+ }
+ // If not a monitor, must be a window
+ sendWindowResponsiveCommandLocked(connectionToken);
+}
+
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
KeyEntry& keyEntry, bool handled) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index df0be99..c7299e9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -233,6 +233,8 @@
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
+ // Find a monitor pid by the provided token.
+ std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -433,6 +435,34 @@
void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
/**
+ * Tell policy about a window or a monitor that just became unresponsive. Starts ANR.
+ */
+ void processConnectionUnresponsiveLocked(const Connection& connection, std::string reason)
+ REQUIRES(mLock);
+ /**
+ * Tell policy about a window or a monitor that just became responsive.
+ */
+ void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
+
+ /**
+ * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
+ */
+ void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
+ /**
+ * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
+ */
+ void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+ REQUIRES(mLock);
+ /**
+ * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
+ */
+ void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
+ /**
+ * Post `doNotifyWindowResponsiveLockedInterruptible` command.
+ */
+ void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
+
+ /**
* This map will store the pending focus requests that cannot be currently processed. This can
* happen if the window requested to be focused is not currently visible. Such a window might
* become visible later, and these requests would be processed at that time.
@@ -577,7 +607,7 @@
int32_t displayId, std::string_view reason) REQUIRES(mLock);
void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
REQUIRES(mLock);
- void onAnrLocked(const Connection& connection) REQUIRES(mLock);
+ void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
@@ -592,11 +622,13 @@
REQUIRES(mLock);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ // ANR-related callbacks - start
void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyConnectionUnresponsiveLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
- void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
+ void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ // ANR-related callbacks - end
void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index d9ec020..b976129 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -50,20 +50,31 @@
virtual void notifyNoFocusedWindowAnr(
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
- /* Notifies the system that a connection just became unresponsive. This indicates that ANR
- * should be raised for this connection. The connection is identified via token.
+ /* Notifies the system that a window just became unresponsive. This indicates that ANR
+ * should be raised for this window. The window is identified via token.
* The string reason contains information about the input event that we haven't received
* a response for.
*/
- virtual void notifyConnectionUnresponsive(const sp<IBinder>& token,
- const std::string& reason) = 0;
+ virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0;
+ /* Notifies the system that a monitor just became unresponsive. This indicates that ANR
+ * should be raised for this monitor. The monitor is identified via its pid.
+ * The string reason contains information about the input event that we haven't received
+ * a response for.
+ */
+ virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0;
- /* Notifies the system that a connection just became responsive. This is only called after the
- * connection was first marked "unresponsive". This indicates that ANR dialog (if any) should
- * no longer should be shown to the user. The connection is eligible to cause a new ANR in the
+ /* Notifies the system that a window just became responsive. This is only called after the
+ * window was first marked "unresponsive". This indicates that ANR dialog (if any) should
+ * no longer should be shown to the user. The window is eligible to cause a new ANR in the
* future.
*/
- virtual void notifyConnectionResponsive(const sp<IBinder>& token) = 0;
+ virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0;
+ /* Notifies the system that a monitor just became responsive. This is only called after the
+ * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should
+ * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the
+ * future.
+ */
+ virtual void notifyMonitorResponsive(int32_t pid) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index c7c8e7c..3e6910d 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -505,8 +505,7 @@
}
void InputDevice::notifyReset(nsecs_t when) {
- NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
- mContext->getListener()->notifyDeviceReset(&args);
+ mContext->notifyDeviceReset(when, mId);
}
std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 6c493ff..7c448e4 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -339,8 +339,7 @@
updateGlobalMetaStateLocked();
// Enqueue configuration changed.
- NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
- mQueuedListener->notifyConfigurationChanged(&args);
+ mContext.notifyConfigurationChanged(when);
}
void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -367,9 +366,7 @@
}
if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
- const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
- mConfig.pointerCapture);
- mQueuedListener->notifyPointerCaptureChanged(&args);
+ mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture);
}
}
@@ -868,16 +865,64 @@
return mReader->mPolicy.get();
}
-InputListenerInterface* InputReader::ContextImpl::getListener() {
- return mReader->mQueuedListener.get();
-}
-
EventHubInterface* InputReader::ContextImpl::getEventHub() {
return mReader->mEventHub.get();
}
-int32_t InputReader::ContextImpl::getNextId() {
- return mIdGenerator.nextId();
+void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) {
+ NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when);
+ mReader->mQueuedListener->notifyConfigurationChanged(&args);
+}
+
+void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, nsecs_t downTime) {
+ NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags,
+ action, flags, keyCode, scanCode, metaState, downTime);
+ mReader->mQueuedListener->notifyKey(&args);
+}
+void InputReader::ContextImpl::notifyMotion(
+ nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames) {
+ NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId,
+ policyFlags, action, actionButton, flags, metaState, buttonState,
+ classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
+ xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+ videoFrames);
+ mReader->mQueuedListener->notifyMotion(&args);
+}
+
+void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId,
+ InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy,
+ bool accuracyChanged, nsecs_t timestamp,
+ std::vector<float> values) {
+ NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType,
+ accuracy, accuracyChanged, timestamp, std::move(values));
+ mReader->mQueuedListener->notifySensor(&args);
+}
+
+void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues,
+ uint32_t switchMask) {
+ NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues,
+ switchMask);
+ mReader->mQueuedListener->notifySwitch(&args);
+}
+
+void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) {
+ NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId);
+ mReader->mQueuedListener->notifyDeviceReset(&args);
+}
+
+void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) {
+ const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture);
+ mReader->mQueuedListener->notifyPointerCaptureChanged(&args);
}
} // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 48d4596..7be932a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -127,11 +127,30 @@
void dispatchExternalStylusState(const StylusState& outState)
NO_THREAD_SAFETY_ANALYSIS override;
InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
- InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
- int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
+
+ // Send events to InputListener interface
+ void notifyConfigurationChanged(nsecs_t when) override;
+ void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime) override;
+ void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t metaState, int32_t buttonState,
+ MotionClassification classification, int32_t edgeFlags,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames) override;
+ void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override;
+ void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+ nsecs_t timestamp, std::vector<float> values) override;
+ void notifyDeviceReset(nsecs_t when, int32_t deviceId) override;
+ void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override;
+
} mContext;
friend class ContextImpl;
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index dc807f7..edab312 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,13 +55,33 @@
virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
virtual InputReaderPolicyInterface* getPolicy() = 0;
- virtual InputListenerInterface* getListener() = 0;
virtual EventHubInterface* getEventHub() = 0;
- virtual int32_t getNextId() = 0;
-
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
+
+ // Send events to InputListener interface
+
+ virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+ virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
+ virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames) = 0;
+ virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0;
+ virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+ nsecs_t timestamp, std::vector<float> values) = 0;
+ virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0;
+ virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 254b64b..7f7b33c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -175,8 +175,7 @@
}
bumpGeneration();
if (changes) {
- NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener()->notifyDeviceReset(&args);
+ getContext()->notifyDeviceReset(when, getDeviceId());
}
}
@@ -383,40 +382,35 @@
while (!released.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
- mSource, displayId, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&releaseArgs);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {});
}
}
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ motionEventAction, 0, 0, metaState, currentButtonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {});
if (buttonsPressed) {
BitSet32 pressed(buttonsPressed);
while (!pressed.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
- NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags,
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&pressArgs);
}
}
@@ -424,13 +418,12 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- 0, metaState, currentButtonState, MotionClassification::NONE,
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&hoverArgs);
}
// Send scroll events.
@@ -438,13 +431,12 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
- yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+ yCursorPosition, downTime, /* videoFrames */ {});
}
}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index bd64d8d..6ca6ec9 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -19,7 +19,6 @@
#include "EventHub.h"
#include "InputDevice.h"
-#include "InputListener.h"
#include "InputReaderContext.h"
#include "StylusState.h"
#include "VibrationElement.h"
@@ -48,7 +47,6 @@
inline const std::string getDeviceName() { return mDeviceContext.getName(); }
inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
- inline InputListenerInterface* getListener() { return getContext()->getListener(); }
virtual uint32_t getSources() = 0;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index abd8aa9..ac4669c 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -337,13 +337,12 @@
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
- ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &pointerProperties, &pointerCoords, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE,
+ policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &pointerProperties, &pointerCoords, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
}
void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8b9f235..03d7405 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -350,10 +350,9 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
- policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
- getListener()->notifyKey(&args);
+ getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
}
ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 594ff42..3f8a364 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -121,13 +121,12 @@
int32_t metaState = getContext()->getGlobalMetaState();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
}
mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 7ac2dec..68c1e40 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -405,13 +405,10 @@
// Convert to Android unit
convertFromLinuxToAndroid(values, sensorType);
// Notify dispatcher for sensor event
- NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
- AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
- sensor.accuracy !=
- sensor.sensorInfo.accuracy /* accuracyChanged */,
- timestamp /* hwTimestamp */, values);
-
- getListener()->notifySensor(&args);
+ getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy,
+ sensor.accuracy !=
+ sensor.sensorInfo.accuracy /* accuracyChanged */,
+ timestamp /* hwTimestamp */, std::move(values));
sensor.lastSampleTimeNs = timestamp;
sensor.accuracy = sensor.sensorInfo.accuracy;
}
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4f73681..07de244 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,10 +56,7 @@
void SwitchInputMapper::sync(nsecs_t when) {
if (mUpdatedSwitchMask) {
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
- updatedSwitchValues, mUpdatedSwitchMask);
- getListener()->notifySwitch(&args);
-
+ getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask);
mUpdatedSwitchMask = 0;
}
}
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index a86443d..ff6341f 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -66,9 +66,8 @@
(currentButtonState & buttonState)) ||
(action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
!(currentButtonState & buttonState))) {
- NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, 0, keyCode, 0, context->getGlobalMetaState(), when);
- context->getListener()->notifyKey(&args);
+ context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/,
+ keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when);
}
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ce12c27..fb30b88 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -397,8 +397,7 @@
if (changes && resetNeeded) {
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
- NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener()->notifyDeviceReset(&args);
+ getContext()->notifyDeviceReset(when, getDeviceId());
}
}
@@ -1829,10 +1828,9 @@
int32_t metaState = getContext()->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
- mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
- scanCode, metaState, downTime);
- getListener()->notifyKey(&args);
+ getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId,
+ policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode,
+ metaState, downTime);
}
void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
@@ -2508,12 +2506,11 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
const int32_t displayId = mPointerController->getDisplayId();
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- 1, &pointerProperties, &pointerCoords, 0, 0, x, y,
- mPointerGesture.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &pointerProperties, &pointerCoords, 0, 0, x, y,
+ mPointerGesture.downTime, /* videoFrames */ {});
}
// Update state.
@@ -3428,28 +3425,28 @@
mPointerSimple.down = false;
// Send up.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+ mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime,
+ /* videoFrames */ {});
}
if (mPointerSimple.hovering && !hovering) {
mPointerSimple.hovering = false;
// Send hover exit.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+ mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime,
+ /* videoFrames */ {});
}
if (down) {
@@ -3458,25 +3455,24 @@
mPointerSimple.downTime = when;
// Send down.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {});
}
// Send move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
}
if (hovering) {
@@ -3484,25 +3480,24 @@
mPointerSimple.hovering = true;
// Send hover enter.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {});
}
// Send hover move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3517,14 +3512,14 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &pointerCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {});
}
// Save state.
@@ -3595,12 +3590,11 @@
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
- NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, actionButton, flags, metaState, buttonState,
- MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
- pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
- downTime, std::move(frames));
- getListener()->notifyMotion(&args);
+ getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton,
+ flags, metaState, buttonState, MotionClassification::NONE, edgeFlags,
+ pointerCount, pointerProperties, pointerCoords, xPrecision,
+ yPrecision, xCursorPosition, yCursorPosition, downTime,
+ std::move(frames));
}
bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3644,6 +3638,9 @@
const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
+ const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
+ const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+
// Rotate to surface coordinate.
// 0 - no swap and reverse.
// 90 - swap x/y and reverse y.
@@ -3655,16 +3652,16 @@
y = yScaled + mYTranslate;
break;
case DISPLAY_ORIENTATION_90:
- y = mSurfaceRight - xScaled;
+ y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
x = yScaled + mYTranslate;
break;
case DISPLAY_ORIENTATION_180:
- x = mSurfaceRight - xScaled;
- y = mSurfaceBottom - yScaled;
+ x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
+ y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
break;
case DISPLAY_ORIENTATION_270:
y = xScaled + mXTranslate;
- x = mSurfaceBottom - yScaled;
+ x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
break;
default:
assert(false);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 6dbc88f..c7e05eb 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -140,27 +140,49 @@
ASSERT_EQ(expectedApplication, application);
}
- void assertNotifyConnectionUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
- const sp<IBinder>& expectedConnectionToken) {
- sp<IBinder> connectionToken = getUnresponsiveConnectionToken(timeout);
+ void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout);
ASSERT_EQ(expectedConnectionToken, connectionToken);
}
- void assertNotifyConnectionResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
- sp<IBinder> connectionToken = getResponsiveConnectionToken();
+ void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
+ sp<IBinder> connectionToken = getResponsiveWindowToken();
ASSERT_EQ(expectedConnectionToken, connectionToken);
}
- sp<IBinder> getUnresponsiveConnectionToken(std::chrono::nanoseconds timeout) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(timeout, mAnrConnectionTokens, lock);
+ void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) {
+ int32_t pid = getUnresponsiveMonitorPid(timeout);
+ ASSERT_EQ(MONITOR_PID, pid);
}
- sp<IBinder> getResponsiveConnectionToken() {
+ void assertNotifyMonitorResponsiveWasCalled() {
+ int32_t pid = getResponsiveMonitorPid();
+ ASSERT_EQ(MONITOR_PID, pid);
+ }
+
+ sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
- return getAnrTokenLockedInterruptible(0s, mResponsiveConnectionTokens, lock);
+ return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock);
+ }
+
+ sp<IBinder> getResponsiveWindowToken() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock);
+ }
+
+ int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock);
+ }
+
+ int32_t getResponsiveMonitorPid() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock);
}
// All three ANR-related callbacks behave the same way, so we use this generic function to wait
@@ -182,7 +204,7 @@
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
if (storage.empty()) {
ADD_FAILURE() << "Did not receive the ANR callback";
- return nullptr;
+ return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -201,10 +223,14 @@
void assertNotifyAnrWasNotCalled() {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mAnrApplications.empty());
- ASSERT_TRUE(mAnrConnectionTokens.empty());
- ASSERT_TRUE(mResponsiveConnectionTokens.empty())
+ ASSERT_TRUE(mAnrWindowTokens.empty());
+ ASSERT_TRUE(mAnrMonitorPids.empty());
+ ASSERT_TRUE(mResponsiveWindowTokens.empty())
<< "ANR was not called, but please also consume the 'connection is responsive' "
"signal";
+ ASSERT_TRUE(mResponsiveMonitorPids.empty())
+ << "Monitor ANR was not called, but please also consume the 'monitor is responsive'"
+ " signal";
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -251,8 +277,10 @@
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock);
- std::queue<sp<IBinder>> mResponsiveConnectionTokens GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock);
+ std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
+ std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
void notifyConfigurationChanged(nsecs_t when) override {
@@ -260,16 +288,27 @@
mConfigurationChangedTime = when;
}
- void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
- const std::string&) override {
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override {
std::scoped_lock lock(mLock);
- mAnrConnectionTokens.push(connectionToken);
+ mAnrWindowTokens.push(connectionToken);
mNotifyAnr.notify_all();
}
- void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {
+ void notifyMonitorUnresponsive(int32_t pid, const std::string&) override {
std::scoped_lock lock(mLock);
- mResponsiveConnectionTokens.push(connectionToken);
+ mAnrMonitorPids.push(pid);
+ mNotifyAnr.notify_all();
+ }
+
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {
+ std::scoped_lock lock(mLock);
+ mResponsiveWindowTokens.push(connectionToken);
+ mNotifyAnr.notify_all();
+ }
+
+ void notifyMonitorResponsive(int32_t pid) override {
+ std::scoped_lock lock(mLock);
+ mResponsiveMonitorPids.push(pid);
mNotifyAnr.notify_all();
}
@@ -1962,11 +2001,10 @@
std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(DISPATCHING_TIMEOUT,
- monitor.getToken());
+ mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT);
monitor.finishEvent(*consumeSeq);
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(monitor.getToken());
+ mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
}
TEST_F(InputDispatcherTest, TestMoveEvent) {
@@ -3194,13 +3232,13 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->finishEvent(*sequenceNum);
mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
}
// Send a key to the app and have the app not respond right away.
@@ -3210,7 +3248,7 @@
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -3315,7 +3353,7 @@
// We have now sent down and up. Let's consume first event and then ANR on the second.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
}
// If an app is not responding to a key event, gesture monitors should continue to receive
@@ -3332,7 +3370,7 @@
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -3341,7 +3379,7 @@
mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -3360,7 +3398,7 @@
mWindow->consumeMotionDown();
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
// New tap will go to the gesture monitor, but not to the window
tapOnWindow();
@@ -3369,7 +3407,7 @@
mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
monitor.assertNoEvents();
}
@@ -3386,19 +3424,19 @@
mWindow->consumeMotionDown();
// Block on ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp(); // Now the connection should be healthy again
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mWindow->assertNoEvents();
tapOnWindow();
mWindow->consumeMotionDown();
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
mWindow->consumeMotionUp();
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
mWindow->assertNoEvents();
}
@@ -3411,7 +3449,7 @@
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
std::this_thread::sleep_for(windowTimeout);
// 'notifyConnectionUnresponsive' should only be called once per connection
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -3421,7 +3459,7 @@
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
mWindow->assertNoEvents();
mDispatcher->waitForIdle();
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -3589,7 +3627,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
// Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
// sequence to make it consistent
mFocusedWindow->consumeMotionCancel();
@@ -3600,7 +3638,7 @@
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -3614,8 +3652,8 @@
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveConnectionToken(10ms);
- sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveConnectionToken(0ms);
+ sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms);
+ sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms);
// We don't know which window will ANR first. But both of them should happen eventually.
ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
@@ -3630,8 +3668,8 @@
mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeMotionOutside();
- sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveConnectionToken();
- sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveConnectionToken();
+ sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken();
+ sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken();
// Both applications should be marked as responsive, in any order
ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
@@ -3655,7 +3693,7 @@
ASSERT_TRUE(upEventSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -3676,7 +3714,7 @@
// The second tap did not go to the focused window
mFocusedWindow->assertNoEvents();
// Since all events are finished, connection should be deemed healthy again
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -3788,7 +3826,7 @@
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
mUnfocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionDown();
@@ -3807,7 +3845,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
}
ASSERT_TRUE(mDispatcher->waitForIdle());
- mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+ mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
mUnfocusedWindow->assertNoEvents();
mFocusedWindow->assertNoEvents();
@@ -4172,6 +4210,7 @@
notifyPointerCaptureChanged(true);
// Ensure that Pointer Capture is disabled.
+ mFakePolicy->waitForSetPointerCapture(false);
mWindow->consumeCaptureEvent(false);
mWindow->assertNoEvents();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 6a06c9e..91d5864 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -8045,6 +8045,34 @@
processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
}
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ const int32_t x = 0;
+ const int32_t y = 0;
+
+ const int32_t xExpected = x;
+ const int32_t yExpected = y;
+ processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
+
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+ // expect x/y = swap x/y then reverse y.
+ const int32_t xExpected90 = y;
+ const int32_t yExpected90 = DISPLAY_WIDTH - 1;
+ processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
+
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_270);
+ // expect x/y = swap x/y then reverse x.
+ const int32_t xExpected270 = DISPLAY_HEIGHT - 1;
+ const int32_t yExpected270 = x;
+ processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
+}
+
TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
// we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
std::shared_ptr<FakePointerController> fakePointerController =
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 4a711ca..2f32827 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -114,14 +114,15 @@
HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
std::unique_lock<std::mutex> lock(mBoostMutex);
+ size_t idx = static_cast<size_t>(boost);
+
// Quick return if boost is not supported by HAL
- if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
- mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+ if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
return HalResult::UNSUPPORTED;
}
- if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+ if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
if (!isSupportedRet.isOk()) {
@@ -130,8 +131,7 @@
return HalResult::FAILED;
}
- mBoostSupportedArray[static_cast<int32_t>(boost)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
toString(boost).c_str());
@@ -145,14 +145,15 @@
HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
std::unique_lock<std::mutex> lock(mModeMutex);
+ size_t idx = static_cast<size_t>(mode);
+
// Quick return if mode is not supported by HAL
- if (mode > Mode::DISPLAY_INACTIVE ||
- mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+ if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
return HalResult::UNSUPPORTED;
}
- if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+ if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
if (!isSupportedRet.isOk()) {
@@ -161,8 +162,7 @@
return HalResult::FAILED;
}
- mModeSupportedArray[static_cast<int32_t>(mode)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setMode %s because Power HAL doesn't support it",
toString(mode).c_str());
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 6561707..424a8b3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -389,33 +389,58 @@
mBufferInfo.mFrameLatencyNeeded = true;
}
+bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
+ // If this is not a valid vsync for the layer's uid, return and try again later
+ const bool isVsyncValidForUid =
+ mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+ if (!isVsyncValidForUid) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ return false;
+ }
+
+ // AutoRefresh layers and sideband streams should always be presented
+ if (getSidebandStreamChanged() || getAutoRefresh()) {
+ return true;
+ }
+
+ // If the next planned present time is not current, return and try again later
+ if (frameIsEarly(expectedPresentTime)) {
+ ATRACE_NAME("frameIsEarly()");
+ return false;
+ }
+
+ // If this layer doesn't have a frame is shouldn't be presented
+ if (!hasFrameUpdate()) {
+ return false;
+ }
+
+ // Defer to the derived class to decide whether the next buffer is due for
+ // presentation.
+ return isBufferDue(expectedPresentTime);
+}
+
bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
// TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
// vsync period. We can do this change as soon as ag/13100772 is merged.
constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
const auto presentTime = nextPredictedPresentTime();
- if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+ if (!presentTime.has_value()) {
return false;
}
- return presentTime >= expectedPresentTime &&
- presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
+ if (std::abs(*presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+ return false;
+ }
+
+ return *presentTime >= expectedPresentTime &&
+ *presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
}
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
ATRACE_CALL();
- // If this is not a valid vsync for the layer's uid, return and try again later
- const bool isVsyncValidForUid =
- mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
- if (!isVsyncValidForUid) {
- ATRACE_NAME("!isVsyncValidForUid");
- mFlinger->setTransactionFlags(eTraversalNeeded);
- return false;
- }
-
bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
if (refreshRequired) {
@@ -435,12 +460,6 @@
return false;
}
- if (frameIsEarly(expectedPresentTime)) {
- ATRACE_NAME("frameIsEarly()");
- mFlinger->signalLayerUpdate();
- return false;
- }
-
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
if (!fenceHasSignaled()) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 5cd9a7c..2118f4a 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -175,12 +175,24 @@
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
// Transform hint provided to the producer. This must be accessed holding
- /// the mStateLock.
+ // the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
bool getAutoRefresh() const { return mAutoRefresh; }
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+ // Returns true if the next buffer should be presented at the expected present time
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+
+ // Returns true if the next buffer should be presented at the expected present time,
+ // overridden by BufferStateLayer and BufferQueueLayer for implementation
+ // specific logic
+ virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
+
+ // Returns true if the next frame is considered too early to present
+ // at the given expectedPresentTime
+ bool frameIsEarly(nsecs_t expectedPresentTime) const;
+
std::atomic<bool> mAutoRefresh{false};
std::atomic<bool> mSidebandStreamChanged{false};
@@ -225,13 +237,8 @@
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
- // Returns true if the next frame is considered too early to present
- // at the given expectedPresentTime
- bool frameIsEarly(nsecs_t expectedPresentTime) const;
-
- // Returns the predicted present time of the next frame if available or
- // 0 otherwise.
- virtual nsecs_t nextPredictedPresentTime() const = 0;
+ // Returns the predicted present time of the next frame if available
+ virtual std::optional<nsecs_t> nextPredictedPresentTime() const = 0;
// The amount of time SF can delay a frame if it is considered early based
// on the VsyncModulator::VsyncConfig::appWorkDuration
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 04cec4f..32e6b10 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -100,15 +100,7 @@
return mQueuedFrames;
}
-bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
- if (getSidebandStreamChanged() || getAutoRefresh()) {
- return true;
- }
-
- if (!hasFrameUpdate()) {
- return false;
- }
-
+bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const {
Mutex::Autolock lock(mQueueItemLock);
const int64_t addedTime = mQueueItems[0].item.mTimestamp;
@@ -223,16 +215,16 @@
return mQueuedFrames > 0;
}
-nsecs_t BufferQueueLayer::nextPredictedPresentTime() const {
+std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime() const {
Mutex::Autolock lock(mQueueItemLock);
if (mQueueItems.empty()) {
- return 0;
+ return std::nullopt;
}
const auto& bufferData = mQueueItems[0];
if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
- return 0;
+ return std::nullopt;
}
return bufferData.surfaceFrame->getPredictions().presentTime;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 2347d8c..0e8fdbe 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -53,7 +53,8 @@
int32_t getQueuedFrameCount() const override;
- bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+ // Returns true if the next buffer should be presented at the expected present time
+ bool isBufferDue(nsecs_t expectedPresentTime) const override;
// Implements BufferLayer.
bool fenceHasSignaled() const override;
@@ -116,7 +117,7 @@
// Temporary - Used only for LEGACY camera mode.
uint32_t getProducerStickyTransform() const;
- nsecs_t nextPredictedPresentTime() const override;
+ std::optional<nsecs_t> nextPredictedPresentTime() const override;
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index bca1c69..0cc15c2 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -212,14 +212,6 @@
}
}
-bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
- if (getSidebandStreamChanged() || getAutoRefresh()) {
- return true;
- }
-
- return hasFrameUpdate();
-}
-
bool BufferStateLayer::willPresentCurrentTransaction() const {
// Returns true if the most recent Transaction applied to CurrentState will be presented.
return (getSidebandStreamChanged() || getAutoRefresh() ||
@@ -343,7 +335,8 @@
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& clientCacheId, uint64_t frameNumber) {
+ const client_cache_t& clientCacheId, uint64_t frameNumber,
+ std::optional<nsecs_t> /* dequeueTime */) {
ATRACE_CALL();
if (mCurrentState.buffer) {
@@ -602,9 +595,9 @@
return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-nsecs_t BufferStateLayer::nextPredictedPresentTime() const {
+std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const {
if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) {
- return 0;
+ return std::nullopt;
}
return mSurfaceFrame->getPredictions().presentTime;
@@ -644,7 +637,10 @@
if (s.buffer == nullptr) {
return BAD_VALUE;
}
- decrementPendingBufferCount();
+
+ if (s.buffer != mBufferInfo.mBuffer) {
+ decrementPendingBufferCount();
+ }
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer = s.buffer;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 6414e6b..f638caa 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -45,7 +45,7 @@
void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
const CompositorTiming& compositorTiming) override;
- bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+ bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; }
uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
return flags;
@@ -71,7 +71,8 @@
bool setFrame(const Rect& frame) override;
bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
nsecs_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& clientCacheId, uint64_t frameNumber) override;
+ const client_cache_t& clientCacheId, uint64_t frameNumber,
+ std::optional<nsecs_t> dequeueTime) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -152,7 +153,7 @@
bool bufferNeedsFiltering() const override;
- nsecs_t nextPredictedPresentTime() const override;
+ std::optional<nsecs_t> nextPredictedPresentTime() const override;
static const std::array<float, 16> IDENTITY_MATRIX;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 43792dc..4f99495 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -167,15 +167,10 @@
// Update framebuffer space
const Rect newBounds(size);
- ScaleVector scale;
- scale = getScale(state.framebufferSpace.bounds, newBounds);
state.framebufferSpace.bounds = newBounds;
- state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
// Update display space
- scale = getScale(state.displaySpace.bounds, newBounds);
state.displaySpace.bounds = newBounds;
- state.displaySpace.content.scaleSelf(scale.x, scale.y);
state.transform = state.layerStackSpace.getTransform(state.displaySpace);
// Update oriented display space
@@ -185,9 +180,7 @@
std::swap(orientedSize.width, orientedSize.height);
}
const Rect newOrientedBounds(orientedSize);
- scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
state.orientedDisplaySpace.bounds = newOrientedBounds;
- state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
dirtyEntireOutput();
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index e64a9f1..d0ba8fe 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -87,20 +87,17 @@
MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId));
MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD1(
- getConfigs,
- std::vector<std::shared_ptr<const HWC2::Display::Config>>(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getActiveConfig,
- std::shared_ptr<const HWC2::Display::Config>(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getActiveConfigIndex, int(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getModes, DisplayModes(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getActiveMode, DisplayModePtr(PhysicalDisplayId));
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId));
MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId));
- MOCK_METHOD4(setActiveConfigWithConstraints,
- status_t(PhysicalDisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+ MOCK_METHOD4(setActiveModeWithConstraints,
+ status_t(PhysicalDisplayId, HwcConfigIndexType,
+ const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline*));
MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
MOCK_METHOD2(getSupportedContentTypes,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1452192..376bac8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -338,15 +338,12 @@
EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
- EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index cbc201f..8551365 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -210,7 +210,7 @@
result.append(" ");
StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
- StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+ StringAppendF(&result, "activeConfig=%zu, ", mActiveConfig.value());
StringAppendF(&result, "deviceProductInfo=");
if (mDeviceProductInfo) {
mDeviceProductInfo->dump(result);
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
new file mode 100644
index 0000000..69fd00e
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "DisplayHardware/Hal.h"
+#include "Scheduler/HwcStrongTypes.h"
+
+#include <android/configuration.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+class DisplayMode;
+using DisplayModePtr = std::shared_ptr<const DisplayMode>;
+using DisplayModes = std::vector<DisplayModePtr>;
+
+class DisplayMode {
+public:
+ class Builder {
+ public:
+ explicit Builder(hal::HWConfigId id) : mDisplayMode(new DisplayMode(id)) {}
+
+ DisplayModePtr build() {
+ return std::const_pointer_cast<const DisplayMode>(std::move(mDisplayMode));
+ }
+
+ Builder& setId(HwcConfigIndexType id) {
+ mDisplayMode->mId = id;
+ return *this;
+ }
+
+ Builder& setWidth(int32_t width) {
+ mDisplayMode->mWidth = width;
+ return *this;
+ }
+
+ Builder& setHeight(int32_t height) {
+ mDisplayMode->mHeight = height;
+ return *this;
+ }
+
+ Builder& setVsyncPeriod(int32_t vsyncPeriod) {
+ mDisplayMode->mVsyncPeriod = vsyncPeriod;
+ return *this;
+ }
+
+ Builder& setDpiX(int32_t dpiX) {
+ if (dpiX == -1) {
+ mDisplayMode->mDpiX = getDefaultDensity();
+ } else {
+ mDisplayMode->mDpiX = dpiX / 1000.0f;
+ }
+ return *this;
+ }
+
+ Builder& setDpiY(int32_t dpiY) {
+ if (dpiY == -1) {
+ mDisplayMode->mDpiY = getDefaultDensity();
+ } else {
+ mDisplayMode->mDpiY = dpiY / 1000.0f;
+ }
+ return *this;
+ }
+
+ Builder& setConfigGroup(int32_t configGroup) {
+ mDisplayMode->mConfigGroup = configGroup;
+ return *this;
+ }
+
+ private:
+ float getDefaultDensity() {
+ // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+ // resolution displays get TV density. Maybe eventually we'll need to update
+ // it for 4k displays, though hopefully those will just report accurate DPI
+ // information to begin with. This is also used for virtual displays and
+ // older HWC implementations, so be careful about orientation.
+
+ auto longDimension = std::max(mDisplayMode->mWidth, mDisplayMode->mHeight);
+ if (longDimension >= 1080) {
+ return ACONFIGURATION_DENSITY_XHIGH;
+ } else {
+ return ACONFIGURATION_DENSITY_TV;
+ }
+ }
+ std::shared_ptr<DisplayMode> mDisplayMode;
+ };
+
+ HwcConfigIndexType getId() const { return mId; }
+ hal::HWConfigId getHwcId() const { return mHwcId; }
+
+ int32_t getWidth() const { return mWidth; }
+ int32_t getHeight() const { return mHeight; }
+ nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
+ float getDpiX() const { return mDpiX; }
+ float getDpiY() const { return mDpiY; }
+ int32_t getConfigGroup() const { return mConfigGroup; }
+
+private:
+ explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
+
+ hal::HWConfigId mHwcId;
+ HwcConfigIndexType mId;
+
+ int32_t mWidth = -1;
+ int32_t mHeight = -1;
+ nsecs_t mVsyncPeriod = -1;
+ float mDpiX = -1;
+ float mDpiY = -1;
+ int32_t mConfigGroup = -1;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 14b54cd..3e856bb 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -77,9 +77,8 @@
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
- const auto& activeConfig = mHwc.getActiveConfig(displayId);
- ui::Size limitedSize =
- limitFramebufferSize(activeConfig->getWidth(), activeConfig->getHeight());
+ const auto& activeMode = mHwc.getActiveMode(displayId);
+ ui::Size limitedSize = limitFramebufferSize(activeMode->getWidth(), activeMode->getHeight());
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 426092d..71a3276 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -68,33 +68,6 @@
// Display methods
Display::~Display() = default;
-Display::Config::Config(Display& display, HWConfigId id)
- : mDisplay(display),
- mId(id),
- mWidth(-1),
- mHeight(-1),
- mVsyncPeriod(-1),
- mDpiX(-1),
- mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, HWConfigId id)
- : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
- // Default density is based on TVs: 1080p displays get XHIGH density, lower-
- // resolution displays get TV density. Maybe eventually we'll need to update
- // it for 4k displays, though hopefully those will just report accurate DPI
- // information to begin with. This is also used for virtual displays and
- // older HWC implementations, so be careful about orientation.
-
- auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
- if (longDimension >= 1080) {
- return ACONFIGURATION_DENSITY_XHIGH;
- } else {
- return ACONFIGURATION_DENSITY_TV;
- }
-}
-
namespace impl {
Display::Display(android::Hwc2::Composer& composer,
@@ -162,93 +135,12 @@
return Error::NONE;
}
-Error Display::getActiveConfig(
- std::shared_ptr<const Display::Config>* outConfig) const
-{
- ALOGV("[%" PRIu64 "] getActiveConfig", mId);
- HWConfigId configId = 0;
- auto intError = mComposer.getActiveConfig(mId, &configId);
- auto error = static_cast<Error>(intError);
-
- if (error != Error::NONE) {
- ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
- *outConfig = nullptr;
- return error;
- }
-
- if (mConfigs.count(configId) != 0) {
- *outConfig = mConfigs.at(configId);
- } else {
- ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId,
- configId);
- // Return no error, but the caller needs to check for a null pointer to
- // detect this case
- *outConfig = nullptr;
- }
-
- return Error::NONE;
-}
-
bool Display::isVsyncPeriodSwitchSupported() const {
ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
return mComposer.isVsyncPeriodSwitchSupported();
}
-Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
- ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
-
- Error error;
-
- if (isVsyncPeriodSwitchSupported()) {
- Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
- auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
- error = static_cast<Error>(intError);
- *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
- } else {
- // Get the default vsync period
- std::shared_ptr<const Display::Config> config;
- error = getActiveConfig(&config);
- if (error != Error::NONE) {
- return error;
- }
- if (!config) {
- // HWC has updated the display modes and hasn't notified us yet.
- return Error::BAD_CONFIG;
- }
-
- *outVsyncPeriod = config->getVsyncPeriod();
- }
-
- return error;
-}
-
-Error Display::getActiveConfigIndex(int* outIndex) const {
- ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
- HWConfigId configId = 0;
- auto intError = mComposer.getActiveConfig(mId, &configId);
- auto error = static_cast<Error>(intError);
-
- if (error != Error::NONE) {
- ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
- *outIndex = -1;
- return error;
- }
-
- auto pos = mConfigs.find(configId);
- if (pos != mConfigs.end()) {
- *outIndex = std::distance(mConfigs.begin(), pos);
- ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
- } else {
- ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
- // Return no error, but the caller needs to check for a negative index
- // to detect this case
- *outIndex = -1;
- }
-
- return Error::NONE;
-}
-
Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
std::vector<Hwc2::Layer> layerIds;
std::vector<Hwc2::IComposerClient::Composition> types;
@@ -335,15 +227,6 @@
return static_cast<Error>(intError);
}
-std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const
-{
- std::vector<std::shared_ptr<const Config>> configs;
- for (const auto& element : mConfigs) {
- configs.emplace_back(element.second);
- }
- return configs;
-}
-
Error Display::getName(std::string* outName) const
{
auto intError = mComposer.getDisplayName(mId, outName);
@@ -486,16 +369,10 @@
return Error::NONE;
}
-Error Display::setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
- if (config->getDisplayId() != mId) {
- ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
- " (expected %" PRIu64 ")",
- config->getId(), config->getDisplayId(), mId);
- return Error::BAD_CONFIG;
- }
if (isVsyncPeriodSwitchSupported()) {
Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
@@ -503,9 +380,8 @@
hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
- auto intError =
- mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
- &vsyncPeriodChangeTimeline);
+ auto intError = mComposer.setActiveConfigWithConstraints(mId, configId, hwc2Constraints,
+ &vsyncPeriodChangeTimeline);
outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
@@ -519,25 +395,13 @@
ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
}
- auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+ auto intError_2_4 = mComposer.setActiveConfig(mId, configId);
outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
outTimeline->refreshRequired = true;
outTimeline->refreshTimeNanos = now;
return static_cast<Error>(intError_2_4);
}
-Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
-{
- if (config->getDisplayId() != mId) {
- ALOGE("setActiveConfig received config %u for the wrong display %"
- PRIu64 " (expected %" PRIu64 ")", config->getId(),
- config->getDisplayId(), mId);
- return Error::BAD_CONFIG;
- }
- auto intError = mComposer.setActiveConfig(mId, config->getId());
- return static_cast<Error>(intError);
-}
-
Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target,
const sp<Fence>& acquireFence, Dataspace dataspace)
{
@@ -681,58 +545,10 @@
void Display::setConnected(bool connected) {
if (!mIsConnected && connected) {
mComposer.setClientTargetSlotCount(mId);
- if (mType == DisplayType::PHYSICAL) {
- loadConfigs();
- }
}
mIsConnected = connected;
}
-int32_t Display::getAttribute(HWConfigId configId, Attribute attribute) {
- int32_t value = 0;
- auto intError = mComposer.getDisplayAttribute(mId, configId, attribute, &value);
- auto error = static_cast<Error>(intError);
- if (error != Error::NONE) {
- ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
- configId, to_string(attribute).c_str(),
- to_string(error).c_str(), intError);
- return -1;
- }
- return value;
-}
-
-void Display::loadConfig(HWConfigId configId) {
- ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
-
- auto config = Config::Builder(*this, configId)
- .setWidth(getAttribute(configId, hal::Attribute::WIDTH))
- .setHeight(getAttribute(configId, hal::Attribute::HEIGHT))
- .setVsyncPeriod(getAttribute(configId, hal::Attribute::VSYNC_PERIOD))
- .setDpiX(getAttribute(configId, hal::Attribute::DPI_X))
- .setDpiY(getAttribute(configId, hal::Attribute::DPI_Y))
- .setConfigGroup(getAttribute(configId, hal::Attribute::CONFIG_GROUP))
- .build();
- mConfigs.emplace(configId, std::move(config));
-}
-
-void Display::loadConfigs()
-{
- ALOGV("[%" PRIu64 "] loadConfigs", mId);
-
- std::vector<HWConfigId> configIds;
- auto intError = mComposer.getDisplayConfigs(mId, &configIds);
- auto error = static_cast<Error>(intError);
- if (error != Error::NONE) {
- ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
- to_string(error).c_str(), intError);
- return;
- }
-
- for (auto configId : configIds) {
- loadConfig(configId);
- }
-}
-
// Other Display methods
HWC2::Layer* Display::getLayerById(HWLayerId id) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 89df84b..4c7f284 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -79,80 +79,6 @@
public:
virtual ~Display();
- class Config {
- public:
- class Builder
- {
- public:
- Builder(Display& display, hal::HWConfigId id);
-
- std::shared_ptr<const Config> build() {
- return std::const_pointer_cast<const Config>(
- std::move(mConfig));
- }
-
- Builder& setWidth(int32_t width) {
- mConfig->mWidth = width;
- return *this;
- }
- Builder& setHeight(int32_t height) {
- mConfig->mHeight = height;
- return *this;
- }
- Builder& setVsyncPeriod(int32_t vsyncPeriod) {
- mConfig->mVsyncPeriod = vsyncPeriod;
- return *this;
- }
- Builder& setDpiX(int32_t dpiX) {
- if (dpiX == -1) {
- mConfig->mDpiX = getDefaultDensity();
- } else {
- mConfig->mDpiX = dpiX / 1000.0f;
- }
- return *this;
- }
- Builder& setDpiY(int32_t dpiY) {
- if (dpiY == -1) {
- mConfig->mDpiY = getDefaultDensity();
- } else {
- mConfig->mDpiY = dpiY / 1000.0f;
- }
- return *this;
- }
- Builder& setConfigGroup(int32_t configGroup) {
- mConfig->mConfigGroup = configGroup;
- return *this;
- }
-
- private:
- float getDefaultDensity();
- std::shared_ptr<Config> mConfig;
- };
-
- hal::HWDisplayId getDisplayId() const { return mDisplay.getId(); }
- hal::HWConfigId getId() const { return mId; }
-
- int32_t getWidth() const { return mWidth; }
- int32_t getHeight() const { return mHeight; }
- nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
- float getDpiX() const { return mDpiX; }
- float getDpiY() const { return mDpiY; }
- int32_t getConfigGroup() const { return mConfigGroup; }
-
- private:
- Config(Display& display, hal::HWConfigId id);
-
- Display& mDisplay;
- hal::HWConfigId mId;
-
- int32_t mWidth;
- int32_t mHeight;
- nsecs_t mVsyncPeriod;
- float mDpiX;
- float mDpiY;
- int32_t mConfigGroup;
- };
-
virtual hal::HWDisplayId getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
@@ -162,9 +88,6 @@
[[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0;
[[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0;
[[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0;
- [[clang::warn_unused_result]] virtual hal::Error getActiveConfig(
- std::shared_ptr<const Config>* outConfig) const = 0;
- [[clang::warn_unused_result]] virtual hal::Error getActiveConfigIndex(int* outIndex) const = 0;
[[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
@@ -176,10 +99,6 @@
[[clang::warn_unused_result]] virtual hal::Error getDataspaceSaturationMatrix(
hal::Dataspace dataspace, android::mat4* outMatrix) = 0;
- // Doesn't call into the HWC2 device, so no errors are possible
- [[clang::warn_unused_result]] virtual std::vector<std::shared_ptr<const Config>> getConfigs()
- const = 0;
-
[[clang::warn_unused_result]] virtual hal::Error getName(std::string* outName) const = 0;
[[clang::warn_unused_result]] virtual hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
@@ -201,8 +120,6 @@
std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
[[clang::warn_unused_result]] virtual hal::Error present(
android::sp<android::Fence>* outPresentFence) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setActiveConfig(
- const std::shared_ptr<const Config>& config) = 0;
[[clang::warn_unused_result]] virtual hal::Error setClientTarget(
uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) = 0;
@@ -222,11 +139,8 @@
android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
[[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
float brightness) = 0;
- [[clang::warn_unused_result]] virtual hal::Error getDisplayVsyncPeriod(
- nsecs_t* outVsyncPeriod) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
[[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
[[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
@@ -248,8 +162,6 @@
hal::Error acceptChanges() override;
hal::Error createLayer(Layer** outLayer) override;
hal::Error destroyLayer(Layer*) override;
- hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
- hal::Error getActiveConfigIndex(int* outIndex) const override;
hal::Error getChangedCompositionTypes(
std::unordered_map<Layer*, hal::Composition>* outTypes) override;
hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
@@ -259,9 +171,6 @@
std::vector<hal::RenderIntent>* outRenderIntents) const override;
hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override;
- // Doesn't call into the HWC2 device, so no errors are possible
- std::vector<std::shared_ptr<const Config>> getConfigs() const override;
-
hal::Error getName(std::string* outName) const override;
hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
@@ -279,7 +188,6 @@
hal::Error getReleaseFences(
std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
hal::Error present(android::sp<android::Fence>* outPresentFence) override;
- hal::Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
hal::Dataspace dataspace) override;
@@ -294,11 +202,9 @@
android::sp<android::Fence>* outPresentFence,
uint32_t* state) override;
std::future<hal::Error> setDisplayBrightness(float brightness) override;
- hal::Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
- hal::Error setActiveConfigWithConstraints(
- const std::shared_ptr<const HWC2::Display::Config>& config,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline) override;
+ hal::Error setActiveConfigWithConstraints(hal::HWConfigId configId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline* outTimeline) override;
hal::Error setAutoLowLatencyMode(bool on) override;
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
@@ -315,9 +221,6 @@
virtual bool isVsyncPeriodSwitchSupported() const override;
private:
- int32_t getAttribute(hal::HWConfigId, hal::Attribute);
- void loadConfig(hal::HWConfigId);
- void loadConfigs();
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
@@ -338,7 +241,6 @@
bool mIsConnected = false;
std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers;
- std::unordered_map<hal::HWConfigId, std::shared_ptr<const Config>> mConfigs;
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6f3987f..ca67935 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -281,6 +281,8 @@
void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
PhysicalDisplayId displayId) {
+ mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+
if (!mInternalHwcDisplayId) {
mInternalHwcDisplayId = hwcDisplayId;
} else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
@@ -293,8 +295,41 @@
hal::DisplayType::PHYSICAL);
newDisplay->setConnected(true);
displayData.hwcDisplay = std::move(newDisplay);
- displayData.configs = displayData.hwcDisplay->getConfigs();
- mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+ loadModes(displayData, hwcDisplayId);
+}
+
+int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
+ hal::Attribute attribute) {
+ int32_t value = 0;
+ auto error = static_cast<hal::Error>(
+ mComposer->getDisplayAttribute(hwcDisplayId, configId, attribute, &value));
+
+ RETURN_IF_HWC_ERROR_FOR("getDisplayAttribute", error, *toPhysicalDisplayId(hwcDisplayId), -1);
+ return value;
+}
+
+void HWComposer::loadModes(DisplayData& displayData, hal::HWDisplayId hwcDisplayId) {
+ ALOGV("[HWC display %" PRIu64 "] %s", hwcDisplayId, __FUNCTION__);
+
+ std::vector<hal::HWConfigId> configIds;
+ auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds));
+ RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId));
+
+ displayData.modes.clear();
+ for (auto configId : configIds) {
+ auto mode = DisplayMode::Builder(configId)
+ .setId(HwcConfigIndexType(displayData.modes.size()))
+ .setWidth(getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH))
+ .setHeight(getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT))
+ .setVsyncPeriod(getAttribute(hwcDisplayId, configId,
+ hal::Attribute::VSYNC_PERIOD))
+ .setDpiX(getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X))
+ .setDpiY(getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y))
+ .setConfigGroup(getAttribute(hwcDisplayId, configId,
+ hal::Attribute::CONFIG_GROUP))
+ .build();
+ displayData.modes.push_back(std::move(mode));
+ }
}
HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
@@ -330,34 +365,38 @@
return mDisplayData.at(displayId).hwcDisplay->isConnected();
}
-std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
- PhysicalDisplayId displayId) const {
+DisplayModes HWComposer::getModes(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
- // We cache the configs when the DisplayData is created on hotplug. If the configs need to
+ // We cache the modes when the DisplayData is created on hotplug. If the modes need to
// change HWC will send a hotplug event which will recreate displayData.
- return mDisplayData.at(displayId).configs;
+ return mDisplayData.at(displayId).modes;
}
-std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
- PhysicalDisplayId displayId) const {
+DisplayModePtr HWComposer::getActiveMode(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
- std::shared_ptr<const HWC2::Display::Config> config;
- auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfig(&config);
+ const auto hwcId = *fromPhysicalDisplayId(displayId);
+ ALOGV("[%" PRIu64 "] getActiveMode", hwcId);
+ hal::HWConfigId configId;
+ auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
+
+ const auto& modes = mDisplayData.at(displayId).modes;
if (error == hal::Error::BAD_CONFIG) {
- LOG_DISPLAY_ERROR(displayId, "No active config");
+ LOG_DISPLAY_ERROR(displayId, "No active mode");
return nullptr;
}
RETURN_IF_HWC_ERROR(error, displayId, nullptr);
- if (!config) {
- LOG_DISPLAY_ERROR(displayId, "Unknown config");
+ const auto it = std::find_if(modes.begin(), modes.end(),
+ [configId](auto mode) { return mode->getHwcId() == configId; });
+ if (it == modes.end()) {
+ LOG_DISPLAY_ERROR(displayId, "Unknown mode");
return nullptr;
}
- return config;
+ return *it;
}
// Composer 2.4
@@ -385,30 +424,24 @@
nsecs_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
- nsecs_t vsyncPeriodNanos;
- auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
- RETURN_IF_HWC_ERROR(error, displayId, 0);
- return vsyncPeriodNanos;
-}
-
-int HWComposer::getActiveConfigIndex(PhysicalDisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, -1);
-
- int index;
- auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfigIndex(&index);
- if (error == hal::Error::BAD_CONFIG) {
- LOG_DISPLAY_ERROR(displayId, "No active config");
- return -1;
+ if (isVsyncPeriodSwitchSupported(displayId)) {
+ const auto hwcId = *fromPhysicalDisplayId(displayId);
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+ auto error =
+ static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos));
+ RETURN_IF_HWC_ERROR(error, displayId, 0);
+ return static_cast<nsecs_t>(vsyncPeriodNanos);
}
- RETURN_IF_HWC_ERROR(error, displayId, -1);
+ // Get the default vsync period
+ auto mode = getActiveMode(displayId);
- if (index < 0) {
- LOG_DISPLAY_ERROR(displayId, "Unknown config");
- return -1;
+ if (!mode) {
+ // HWC has updated the display modes and hasn't notified us yet.
+ RETURN_IF_HWC_ERROR(hal::Error::BAD_CONFIG, displayId, 0);
}
- return index;
+ return mode->getVsyncPeriod();
}
std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
@@ -640,21 +673,21 @@
return NO_ERROR;
}
-status_t HWComposer::setActiveConfigWithConstraints(
- PhysicalDisplayId displayId, size_t configId,
+status_t HWComposer::setActiveModeWithConstraints(
+ PhysicalDisplayId displayId, HwcConfigIndexType modeId,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
- if (configId >= displayData.configs.size()) {
- LOG_DISPLAY_ERROR(displayId, ("Invalid config " + std::to_string(configId)).c_str());
+ if (modeId.value() >= displayData.modes.size()) {
+ LOG_DISPLAY_ERROR(displayId, ("Invalid mode " + std::to_string(modeId.value())).c_str());
return BAD_INDEX;
}
- auto error =
- displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configs[configId],
- constraints, outTimeline);
+ const auto hwcConfigId = displayData.modes[modeId.value()]->getHwcId();
+ auto error = displayData.hwcDisplay->setActiveConfigWithConstraints(hwcConfigId, constraints,
+ outTimeline);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7e1da252..2b3d2d4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -41,6 +41,7 @@
#include "DisplayIdGenerator.h"
#include "DisplayIdentification.h"
+#include "DisplayMode.h"
#include "HWC2.h"
#include "Hal.h"
@@ -184,12 +185,9 @@
virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0;
virtual bool isConnected(PhysicalDisplayId) const = 0;
- virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- PhysicalDisplayId) const = 0;
+ virtual DisplayModes getModes(PhysicalDisplayId) const = 0;
- virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
- PhysicalDisplayId) const = 0;
- virtual int getActiveConfigIndex(PhysicalDisplayId) const = 0;
+ virtual DisplayModePtr getActiveMode(PhysicalDisplayId) const = 0;
virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
@@ -200,9 +198,9 @@
virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
- virtual status_t setActiveConfigWithConstraints(
- PhysicalDisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
+ virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, HwcConfigIndexType,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
virtual status_t getSupportedContentTypes(
PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
@@ -319,11 +317,9 @@
nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override;
bool isConnected(PhysicalDisplayId) const override;
- std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- PhysicalDisplayId) const override;
+ DisplayModes getModes(PhysicalDisplayId) const override;
- std::shared_ptr<const HWC2::Display::Config> getActiveConfig(PhysicalDisplayId) const override;
- int getActiveConfigIndex(PhysicalDisplayId) const override;
+ DisplayModePtr getActiveMode(PhysicalDisplayId) const override;
std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
@@ -332,10 +328,10 @@
// Composer 2.4
DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
- nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const override;
- status_t setActiveConfigWithConstraints(PhysicalDisplayId, size_t configId,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline* outTimeline) override;
+ nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId displayId) const override;
+ status_t setActiveModeWithConstraints(PhysicalDisplayId, HwcConfigIndexType,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline* outTimeline) override;
status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
@@ -362,14 +358,6 @@
// For unit tests
friend TestableSurfaceFlinger;
- std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
- std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
- bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
-
- void loadCapabilities();
- void loadLayerMetadataSupport();
- uint32_t getMaxVirtualDisplayCount() const;
-
struct DisplayData {
bool isVirtual = false;
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -377,7 +365,7 @@
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
buffer_handle_t outbufHandle = nullptr;
sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ DisplayModes modes;
bool validateWasSkipped;
hal::Error presentError;
@@ -391,6 +379,18 @@
nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
};
+ std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
+ std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
+ bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+
+ int32_t getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
+ hal::Attribute attribute);
+ void loadModes(DisplayData& displayData, hal::HWDisplayId hwcDisplayId);
+
+ void loadCapabilities();
+ void loadLayerMetadataSupport();
+ uint32_t getMaxVirtualDisplayCount() const;
+
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
std::unique_ptr<android::Hwc2::Composer> mComposer;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index a8fef04..c994434 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -239,11 +239,16 @@
return minTime;
}
+int64_t TraceCookieCounter::getCookieForTracing() {
+ return ++mTraceCookie;
+}
+
SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds)
+ JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter)
: mToken(token),
mOwnerPid(ownerPid),
mOwnerUid(ownerUid),
@@ -254,7 +259,8 @@
mPredictions(predictions),
mActuals({0, 0, 0}),
mTimeStats(timeStats),
- mJankClassificationThresholds(thresholds) {}
+ mJankClassificationThresholds(thresholds),
+ mTraceCookieCounter(*traceCookieCounter) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -443,7 +449,7 @@
void SurfaceFrame::trace(int64_t displayFrameToken) {
using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ {
std::lock_guard<std::mutex> lock(mMutex);
if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str());
@@ -453,36 +459,83 @@
mLayerName.c_str());
return;
}
+ }
+
+ int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ // Expected timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::lock_guard<std::mutex> lock(mMutex);
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
auto* event = packet->set_frame_timeline_event();
- auto* surfaceFrameEvent = event->set_surface_frame();
+ auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
- surfaceFrameEvent->set_token(mToken);
- surfaceFrameEvent->set_display_frame_token(displayFrameToken);
+ expectedSurfaceFrameStartEvent->set_cookie(expectedTimelineCookie);
+
+ expectedSurfaceFrameStartEvent->set_token(mToken);
+ expectedSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+ expectedSurfaceFrameStartEvent->set_pid(mOwnerPid);
+ expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
+ });
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
+
+ expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ // Actual timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ // Actual start time is not yet available, so use expected start instead
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
+
+ actualSurfaceFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualSurfaceFrameStartEvent->set_token(mToken);
+ actualSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+ actualSurfaceFrameStartEvent->set_pid(mOwnerPid);
+ actualSurfaceFrameStartEvent->set_layer_name(mDebugName);
if (mPresentState == PresentState::Dropped) {
- surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+ actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
} else if (mPresentState == PresentState::Unknown) {
- surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
+ actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
} else {
- surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+ actualSurfaceFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
}
- surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
- FrameReadyMetadata::OnTimeFinish);
- surfaceFrameEvent->set_gpu_composition(mGpuComposition);
- surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ actualSurfaceFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
+ actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ });
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime));
- surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
- surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameEndEvent = event->set_frame_end();
- surfaceFrameEvent->set_actual_start_ns(mActuals.startTime);
- surfaceFrameEvent->set_actual_end_ns(mActuals.endTime);
-
- surfaceFrameEvent->set_layer_name(mDebugName);
- surfaceFrameEvent->set_pid(mOwnerPid);
+ actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
});
}
@@ -520,11 +573,13 @@
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
JankClassificationThresholds thresholds)
- : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
- mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+ : mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)),
mSurfaceFlingerPid(surfaceFlingerPid),
- mJankClassificationThresholds(thresholds) {}
+ mJankClassificationThresholds(thresholds) {
+ mCurrentDisplayFrame =
+ std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
+}
void FrameTimeline::onBootFinished() {
perfetto::TracingInitArgs args;
@@ -547,27 +602,29 @@
return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
ownerUid, std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
- mJankClassificationThresholds);
+ mJankClassificationThresholds, &mTraceCookieCounter);
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
std::move(debugName), PredictionState::Valid,
std::move(*predictions), mTimeStats,
- mJankClassificationThresholds);
+ mJankClassificationThresholds, &mTraceCookieCounter);
}
return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
std::move(debugName), PredictionState::Expired,
- TimelineItem(), mTimeStats,
- mJankClassificationThresholds);
+ TimelineItem(), mTimeStats, mJankClassificationThresholds,
+ &mTraceCookieCounter);
}
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds)
+ JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter)
: mSurfaceFlingerPredictions(TimelineItem()),
mSurfaceFlingerActuals(TimelineItem()),
mTimeStats(timeStats),
- mJankClassificationThresholds(thresholds) {
+ mJankClassificationThresholds(thresholds),
+ mTraceCookieCounter(*traceCookieCounter) {
mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
}
@@ -725,32 +782,69 @@
}
void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+ if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace DisplayFrame with invalid token");
+ return;
+ }
+
+ int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ // Expected timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
- ALOGD("Cannot trace DisplayFrame with invalid token");
- return;
- }
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime));
auto* event = packet->set_frame_timeline_event();
- auto* displayFrameEvent = event->set_display_frame();
+ auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
- displayFrameEvent->set_token(mToken);
- displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
- displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
- FrameReadyMetadata::OnTimeFinish);
- displayFrameEvent->set_gpu_composition(mGpuComposition);
- displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ expectedDisplayFrameStartEvent->set_cookie(expectedTimelineCookie);
- displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
- displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+ expectedDisplayFrameStartEvent->set_token(mToken);
+ expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+ });
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime));
- displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
- displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedDisplayFrameEndEvent = event->set_frame_end();
- displayFrameEvent->set_pid(surfaceFlingerPid);
+ expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ // Expected timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
+
+ actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualDisplayFrameStartEvent->set_token(mToken);
+ actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+
+ actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
+ actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
+ actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ });
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.endTime));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
+
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
});
for (auto& surfaceFrame : mSurfaceFrames) {
@@ -786,8 +880,8 @@
}
mDisplayFrames.push_back(mCurrentDisplayFrame);
mCurrentDisplayFrame.reset();
- mCurrentDisplayFrame =
- std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
+ &mTraceCookieCounter);
}
nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index f5239aa..ed38cc6 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -127,6 +127,23 @@
None, // Predictions are either not present or didn't come from TokenManager
};
+/*
+ * Trace cookie is used to send start and end timestamps of <Surface/Display>Frames separately
+ * without needing to resend all the other information. We send all info to perfetto, along with a
+ * new cookie, in the start of a frame. For the corresponding end, we just send the same cookie.
+ * This helps in reducing the amount of data emitted by the producer.
+ */
+class TraceCookieCounter {
+public:
+ int64_t getCookieForTracing();
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ std::atomic<int64_t> mTraceCookie = 0;
+};
+
class SurfaceFrame {
public:
enum class PresentState {
@@ -139,7 +156,8 @@
// TokenManager), Thresholds and TimeStats pointer.
SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
- std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -205,6 +223,9 @@
// for BufferStuffing where the current buffer is expected to be ready but the previous buffer
// was latched instead.
nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
+ // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
+ // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
+ TraceCookieCounter& mTraceCookieCounter;
};
/*
@@ -291,7 +312,8 @@
*/
class DisplayFrame {
public:
- DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+ DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
virtual ~DisplayFrame() = default;
// Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
// SurfaceFrame is janky.
@@ -360,6 +382,10 @@
// The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
// timeline
nsecs_t mVsyncPeriod = 0;
+ // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto.
+ // Using a reference here because the counter is owned by FrameTimeline, which outlives
+ // DisplayFrame.
+ TraceCookieCounter& mTraceCookieCounter;
};
FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
@@ -402,6 +428,7 @@
mPendingPresentFences GUARDED_BY(mMutex);
std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
TokenManager mTokenManager;
+ TraceCookieCounter mTraceCookieCounter;
mutable std::mutex mMutex;
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d6023b6..f78b5f3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -446,7 +446,7 @@
virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
- uint64_t /* frameNumber */) {
+ uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 6a511a8..9230e72 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -233,11 +233,12 @@
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
-void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- mCurrentFps = refreshRate.getFps().getIntValue();
+void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+ mCurrentFps = fps.getIntValue();
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+ std::nullopt /* dequeueTime */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
@@ -249,15 +250,17 @@
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+ std::nullopt /* dequeueTime */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
void RefreshRateOverlay::reset() {
mBufferCache.clear();
- mLowFps = mFlinger.mRefreshRateConfigs->getMinRefreshRate().getFps().getIntValue();
- mHighFps = mFlinger.mRefreshRateConfigs->getMaxRefreshRate().getFps().getIntValue();
+ const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange();
+ mLowFps = range.min.getIntValue();
+ mHighFps = range.max.getIntValue();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 4ca1337..c16cfa0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -23,7 +23,7 @@
#include <ui/Size.h>
#include <utils/StrongPointer.h>
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Fps.h"
namespace android {
@@ -34,14 +34,12 @@
class Layer;
class SurfaceFlinger;
-using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
class RefreshRateOverlay {
public:
RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
void setViewport(ui::Size);
- void changeRefreshRate(const RefreshRate&);
+ void changeRefreshRate(const Fps&);
void onInvalidate();
void reset();
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
index 8ba4f20..b6a33a2 100644
--- a/services/surfaceflinger/Scheduler/HwcStrongTypes.h
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -18,9 +18,11 @@
#include "StrongTyping.h"
+#include <cstddef>
+
namespace android {
// Strong types for the different indexes as they are referring to a different base.
-using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
+using HwcConfigIndexType = StrongTyping<size_t, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 35b382e..b02596a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -26,6 +26,7 @@
#include <utils/Trace.h>
#include <chrono>
#include <cmath>
+#include "../SurfaceFlingerProperties.h"
#undef LOG_TAG
#define LOG_TAG "RefreshRateConfigs"
@@ -38,14 +39,33 @@
toString(layer.seamlessness).c_str(),
to_string(layer.desiredRefreshRate).c_str());
}
+
+std::vector<Fps> constructKnownFrameRates(const DisplayModes& configs) {
+ std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+ knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+ // Add all supported refresh rates to the set
+ for (const auto& config : configs) {
+ const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod());
+ knownFrameRates.emplace_back(refreshRate);
+ }
+
+ // Sort and remove duplicates
+ std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+ knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+ Fps::EqualsWithMargin()),
+ knownFrameRates.end());
+ return knownFrameRates;
+}
+
} // namespace
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
std::string RefreshRate::toString() const {
- return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
- getConfigId().value(), hwcConfig->getId(), getFps().getValue(),
+ return base::StringPrintf("{id=%zu, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
+ getConfigId().value(), hwcConfig->getHwcId(), getFps().getValue(),
hwcConfig->getWidth(), hwcConfig->getHeight(), getConfigGroup());
}
@@ -67,7 +87,7 @@
}
std::string RefreshRateConfigs::Policy::toString() const {
- return base::StringPrintf("default config ID: %d, allowGroupSwitching = %d"
+ return base::StringPrintf("default config ID: %zu, allowGroupSwitching = %d"
", primary range: %s, app request range: %s",
defaultConfig.value(), allowGroupSwitching,
primaryRange.toString().c_str(), appRequestRange.toString().c_str());
@@ -153,9 +173,9 @@
float score;
};
-const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered) const {
+RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -468,9 +488,20 @@
return bestRefreshRate;
}
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
+ std::optional<HwcConfigIndexType> desiredActiveConfigId, bool timerExpired) const {
std::lock_guard lock(mLock);
- return getMinRefreshRateByPolicyLocked();
+
+ const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
+ : *mCurrentRefreshRate;
+ const auto& min = *mMinSupportedRefreshRate;
+
+ if (current != min) {
+ const auto& refreshRate = timerExpired ? min : current;
+ return refreshRate.getFps();
+ }
+
+ return {};
}
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
@@ -486,7 +517,7 @@
return *mPrimaryRefreshRates.front();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getMaxRefreshRateByPolicyLocked();
}
@@ -505,12 +536,12 @@
return *mPrimaryRefreshRates.back();
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
std::lock_guard lock(mLock);
return *mCurrentRefreshRate;
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getCurrentRefreshRateByPolicyLocked();
}
@@ -528,20 +559,24 @@
mCurrentRefreshRate = mRefreshRates.at(configId).get();
}
-RefreshRateConfigs::RefreshRateConfigs(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId)
+RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& configs,
+ HwcConfigIndexType currentConfigId)
: mKnownFrameRates(constructKnownFrameRates(configs)) {
+ updateDisplayConfigs(configs, currentConfigId);
+}
+
+void RefreshRateConfigs::updateDisplayConfigs(const DisplayModes& configs,
+ HwcConfigIndexType currentConfigId) {
+ std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfigId.value() < 0);
LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
+ mRefreshRates.clear();
for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
const auto& config = configs.at(static_cast<size_t>(configId.value()));
+ const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod());
mRefreshRates.emplace(configId,
- std::make_unique<RefreshRate>(configId, config,
- Fps::fromPeriodNsecs(
- config->getVsyncPeriod()),
+ std::make_unique<RefreshRate>(configId, config, fps,
RefreshRate::ConstructorTag(0)));
if (configId == currentConfigId) {
mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -549,24 +584,27 @@
}
std::vector<const RefreshRate*> sortedConfigs;
- getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedConfigs);
mDisplayManagerPolicy.defaultConfig = currentConfigId;
mMinSupportedRefreshRate = sortedConfigs.front();
mMaxSupportedRefreshRate = sortedConfigs.back();
mSupportsFrameRateOverride = false;
- for (const auto& config1 : sortedConfigs) {
- for (const auto& config2 : sortedConfigs) {
- if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
- mSupportsFrameRateOverride = true;
- break;
+ if (android::sysprop::enable_frame_rate_override(true)) {
+ for (const auto& config1 : sortedConfigs) {
+ for (const auto& config2 : sortedConfigs) {
+ if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
+ mSupportsFrameRateOverride = true;
+ break;
+ }
}
}
}
+
constructAvailableRefreshRates();
}
-bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
+bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
// defaultConfig must be a valid config, and within the given refresh rate range.
auto iter = mRefreshRates.find(policy.defaultConfig);
if (iter == mRefreshRates.end()) {
@@ -584,7 +622,7 @@
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
- if (!isPolicyValid(policy)) {
+ if (!isPolicyValidLocked(policy)) {
ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
@@ -599,7 +637,7 @@
status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
std::lock_guard lock(mLock);
- if (policy && !isPolicyValid(*policy)) {
+ if (policy && !isPolicyValidLocked(*policy)) {
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -635,14 +673,14 @@
return false;
}
-void RefreshRateConfigs::getSortedRefreshRateList(
+void RefreshRateConfigs::getSortedRefreshRateListLocked(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) {
outRefreshRates->clear();
outRefreshRates->reserve(mRefreshRates.size());
for (const auto& [type, refreshRate] : mRefreshRates) {
if (shouldAddRefreshRate(*refreshRate)) {
- ALOGV("getSortedRefreshRateList: config %d added to list policy",
+ ALOGV("getSortedRefreshRateListLocked: config %zu added to list policy",
refreshRate->configId.value());
outRefreshRates->push_back(refreshRate.get());
}
@@ -668,8 +706,9 @@
ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
auto filterRefreshRates = [&](Fps min, Fps max, const char* listName,
- std::vector<const RefreshRate*>* outRefreshRates) {
- getSortedRefreshRateList(
+ std::vector<const RefreshRate*>*
+ outRefreshRates) REQUIRES(mLock) {
+ getSortedRefreshRateListLocked(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
const auto& hwcConfig = refreshRate.hwcConfig;
@@ -702,25 +741,6 @@
&mAppRequestRefreshRates);
}
-std::vector<Fps> RefreshRateConfigs::constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
- std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
- knownFrameRates.reserve(knownFrameRates.size() + configs.size());
-
- // Add all supported refresh rates to the set
- for (const auto& config : configs) {
- const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod());
- knownFrameRates.emplace_back(refreshRate);
- }
-
- // Sort and remove duplicates
- std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
- knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
- Fps::EqualsWithMargin()),
- knownFrameRates.end());
- return knownFrameRates;
-}
-
Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
return *mKnownFrameRates.begin();
@@ -740,7 +760,7 @@
RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
std::lock_guard lock(mLock);
- const auto& deviceMin = getMinRefreshRate();
+ const auto& deviceMin = *mMinSupportedRefreshRate;
const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e4bbf7f..a5d37c2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -24,6 +24,7 @@
#include <optional>
#include <type_traits>
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "Fps.h"
#include "HwcStrongTypes.h"
@@ -64,12 +65,9 @@
};
public:
- RefreshRate(HwcConfigIndexType configId,
- std::shared_ptr<const HWC2::Display::Config> config, Fps fps, ConstructorTag)
+ RefreshRate(HwcConfigIndexType configId, DisplayModePtr config, Fps fps, ConstructorTag)
: configId(configId), hwcConfig(config), fps(std::move(fps)) {}
- RefreshRate(const RefreshRate&) = delete;
-
HwcConfigIndexType getConfigId() const { return configId; }
nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
@@ -103,7 +101,7 @@
// on the device.
const HwcConfigIndexType configId;
// The config itself
- std::shared_ptr<const HWC2::Display::Config> hwcConfig;
+ DisplayModePtr hwcConfig;
// Refresh rate in frames per second
const Fps fps{0.0f};
};
@@ -111,27 +109,26 @@
using AllRefreshRatesMapType =
std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
+ struct FpsRange {
+ Fps min{0.0f};
+ Fps max{std::numeric_limits<float>::max()};
+
+ bool operator==(const FpsRange& other) const {
+ return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+ }
+
+ bool operator!=(const FpsRange& other) const { return !(*this == other); }
+
+ std::string toString() const {
+ return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str());
+ }
+ };
+
struct Policy {
private:
static constexpr int kAllowGroupSwitchingDefault = false;
public:
- struct Range {
- Fps min{0.0f};
- Fps max{std::numeric_limits<float>::max()};
-
- bool operator==(const Range& other) const {
- return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
- }
-
- bool operator!=(const Range& other) const { return !(*this == other); }
-
- std::string toString() const {
- return base::StringPrintf("[%s %s]", to_string(min).c_str(),
- to_string(max).c_str());
- }
- };
-
// The default config, used to ensure we only initiate display config switches within the
// same config group as defaultConfigId's group.
HwcConfigIndexType defaultConfig;
@@ -140,29 +137,29 @@
// The primary refresh rate range represents display manager's general guidance on the
// display configs we'll consider when switching refresh rates. Unless we get an explicit
// signal from an app, we should stay within this range.
- Range primaryRange;
+ FpsRange primaryRange;
// The app request refresh rate range allows us to consider more display configs when
// switching refresh rates. Although we should generally stay within the primary range,
// specific considerations, such as layer frame rate settings specified via the
// setFrameRate() api, may cause us to go outside the primary range. We never go outside the
// app request range. The app request range will be greater than or equal to the primary
// refresh rate range, never smaller.
- Range appRequestRange;
+ FpsRange appRequestRange;
Policy() = default;
- Policy(HwcConfigIndexType defaultConfig, const Range& range)
+ Policy(HwcConfigIndexType defaultConfig, const FpsRange& range)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range)
+ Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const FpsRange& range)
: Policy(defaultConfig, allowGroupSwitching, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
- const Range& appRequestRange)
+ Policy(HwcConfigIndexType defaultConfig, const FpsRange& primaryRange,
+ const FpsRange& appRequestRange)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
- const Range& primaryRange, const Range& appRequestRange)
+ const FpsRange& primaryRange, const FpsRange& appRequestRange)
: defaultConfig(defaultConfig),
allowGroupSwitching(allowGroupSwitching),
primaryRange(primaryRange),
@@ -258,35 +255,35 @@
// globalSignals - global state of touch and idle
// outSignalsConsidered - An output param that tells the caller whether the refresh rate was
// chosen based on touch boost and/or idle timer.
- const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered = nullptr) const
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
- // Returns the lowest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
+ FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
+ }
- // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
- // uses the primary range, not the app request range.
- const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
-
- // Returns the highest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
+ std::optional<Fps> onKernelTimerChanged(std::optional<HwcConfigIndexType> desiredActiveConfigId,
+ bool timerExpired) const EXCLUDES(mLock);
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+ RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
// Returns the current refresh rate
- const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+ RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
- const RefreshRate& getCurrentRefreshRateByPolicy() const;
+ RefreshRate getCurrentRefreshRateByPolicy() const;
- // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // Returns the refresh rate that corresponds to a HwcConfigIndexType. This may change at
// runtime.
- const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+ // TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed.
+ RefreshRate getRefreshRateFromConfigId(HwcConfigIndexType configId) const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
return *mRefreshRates.at(configId);
};
@@ -299,13 +296,18 @@
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
- RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId);
+ RefreshRateConfigs(const DisplayModes& configs, HwcConfigIndexType currentConfigId);
+
+ void updateDisplayConfigs(const DisplayModes& configs, HwcConfigIndexType currentConfig)
+ EXCLUDES(mLock);
// Returns whether switching configs (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
// differ in resolution.
- bool canSwitch() const { return mRefreshRates.size() > 1; }
+ bool canSwitch() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return mRefreshRates.size() > 1;
+ }
// Class to enumerate options around toggling the kernel timer on and off. We have an option
// for no change to avoid extra calls to kernel.
@@ -334,12 +336,10 @@
friend class RefreshRateConfigsTest;
void constructAvailableRefreshRates() REQUIRES(mLock);
- static std::vector<Fps> constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
- void getSortedRefreshRateList(
+ void getSortedRefreshRateListLocked(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
- std::vector<const RefreshRate*>* outRefreshRates);
+ std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
// Returns the refresh rate with the highest score in the collection specified from begin
// to end. If there are more than one with the same highest refresh rate, the first one is
@@ -364,7 +364,7 @@
const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
- bool isPolicyValid(const Policy& policy);
+ bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Return the display refresh rate divider to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -376,9 +376,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
- // The list of refresh rates, indexed by display config ID. This must not change after this
+ // The list of refresh rates, indexed by display config ID. This may change after this
// object is initialized.
- AllRefreshRatesMapType mRefreshRates;
+ AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
// The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
// (the first element is the lowest refresh rate).
@@ -398,9 +398,9 @@
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
// The min and max refresh rates supported by the device.
- // This will not change at runtime.
- const RefreshRate* mMinSupportedRefreshRate;
- const RefreshRate* mMaxSupportedRefreshRate;
+ // This may change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock);
+ const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock);
mutable std::mutex mLock;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 18c899b..92786fd 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -564,21 +564,23 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
+ scheduler::LayerHistory::LayerVoteType voteType;
+
if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
- mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::NoVote);
+ voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
} else if (!mOptions.useContentDetection) {
// If the content detection feature is off, all layers are registered at Max. We still keep
// the layer history, since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Max);
+ voteType = scheduler::LayerHistory::LayerVoteType::Max;
+ } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
+ // Running Wallpaper at Min is considered as part of content detection.
+ voteType = scheduler::LayerHistory::LayerVoteType::Min;
} else {
- if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
- // Running Wallpaper at Min is considered as part of content detection.
- mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Min);
- } else {
- mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Heuristic);
- }
+ voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
}
+
+ mLayerHistory->registerLayer(layer, voteType);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
@@ -612,7 +614,7 @@
mFeatures.contentRequirements = summary;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
- auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
frameRateOverridesChanged =
updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
@@ -629,7 +631,7 @@
}
}
if (frameRateChanged) {
- auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate,
consideredSignals.idle ? ConfigEvent::None
: ConfigEvent::Changed);
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index 6a60257..a05c123 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -62,8 +62,8 @@
template <typename T, typename W, template <typename> class... Ability>
struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
- StrongTyping() : mValue(0) {}
- explicit StrongTyping(T const& value) : mValue(value) {}
+ constexpr StrongTyping() = default;
+ constexpr explicit StrongTyping(T const& value) : mValue(value) {}
StrongTyping(StrongTyping const&) = default;
StrongTyping& operator=(StrongTyping const&) = default;
explicit inline operator T() const { return mValue; }
@@ -75,7 +75,7 @@
}
private:
- T mValue;
+ T mValue{0};
};
} // namespace android
diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp
index db82772..f42cd53 100644
--- a/services/surfaceflinger/StartPropertySetThread.cpp
+++ b/services/surfaceflinger/StartPropertySetThread.cpp
@@ -31,6 +31,7 @@
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
property_set("service.bootanim.exit", "0");
+ property_set("service.bootanim.progress", "0");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6967f69..a6531e1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -226,7 +226,7 @@
~UnnecessaryLock() RELEASE() {}
};
-// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
float getDensityFromProperty(const char* property, bool required) {
@@ -904,14 +904,14 @@
configs->clear();
- for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
+ for (const auto& mode : getHwComposer().getModes(*displayId)) {
DisplayConfig config;
- auto width = hwConfig->getWidth();
- auto height = hwConfig->getHeight();
+ auto width = mode->getWidth();
+ auto height = mode->getHeight();
- auto xDpi = hwConfig->getDpiX();
- auto yDpi = hwConfig->getDpiY();
+ auto xDpi = mode->getDpiX();
+ auto yDpi = mode->getDpiY();
if (isInternal &&
(internalDisplayOrientation == ui::ROTATION_90 ||
@@ -930,14 +930,14 @@
config.yDpi = yDpi;
}
- const nsecs_t period = hwConfig->getVsyncPeriod();
+ const nsecs_t period = mode->getVsyncPeriod();
config.refreshRate = Fps::fromPeriodNsecs(period).getValue();
const auto vsyncConfigSet =
mVsyncConfiguration->getConfigsForRefreshRate(Fps(config.refreshRate));
config.appVsyncOffset = vsyncConfigSet.late.appOffset;
config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
- config.configGroup = hwConfig->getConfigGroup();
+ config.configGroup = mode->getConfigGroup();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -995,7 +995,7 @@
void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
ATRACE_CALL();
- auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+ auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str());
std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -1030,7 +1030,7 @@
}
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(refreshRate);
+ mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps());
}
}
@@ -1076,14 +1076,14 @@
return;
}
- auto& oldRefreshRate =
+ auto oldRefreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig());
std::lock_guard<std::mutex> lock(mActiveConfigLock);
mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
display->setActiveConfig(mUpcomingActiveConfig.configId);
- auto& refreshRate =
+ auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
mRefreshRateStats->setRefreshRate(refreshRate.getFps());
@@ -1126,9 +1126,9 @@
return;
}
- auto& refreshRate =
+ auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId);
- ALOGV("performSetActiveConfig changing active config to %d(%s)",
+ ALOGV("performSetActiveConfig changing active config to %zu(%s)",
refreshRate.getConfigId().value(), refreshRate.getName().c_str());
const auto display = getDefaultDisplayDeviceLocked();
if (!display || display->getActiveConfig() == desiredActiveConfig->configId) {
@@ -1158,13 +1158,12 @@
hal::VsyncPeriodChangeTimeline outTimeline;
auto status =
- getHwComposer().setActiveConfigWithConstraints(displayId,
- mUpcomingActiveConfig.configId.value(),
- constraints, &outTimeline);
+ getHwComposer().setActiveModeWithConstraints(displayId, mUpcomingActiveConfig.configId,
+ constraints, &outTimeline);
if (status != NO_ERROR) {
- // setActiveConfigWithConstraints may fail if a hotplug event is just about
+ // setActiveModeWithConstraints may fail if a hotplug event is just about
// to be sent. We just log the error in this case.
- ALOGW("setActiveConfigWithConstraints failed: %d", status);
+ ALOGW("setActiveModeWithConstraints failed: %d", status);
return;
}
@@ -1618,7 +1617,7 @@
// Don't do any updating if the current fps is the same as the new one.
if (!isDisplayConfigAllowed(refreshRate.getConfigId())) {
- ALOGV("Skipping config %d as it is not part of allowed configs",
+ ALOGV("Skipping config %zu as it is not part of allowed configs",
refreshRate.getConfigId().value());
return;
}
@@ -1663,7 +1662,7 @@
}
void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hal::HWDisplayId /*display*/) {
- // TODO(b/142753666): use constraints when calling to setActiveConfigWithConstrains and
+ // TODO(b/142753666): use constraints when calling to setActiveModeWithConstraints and
// use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
}
@@ -1994,11 +1993,6 @@
mScheduler->onDisplayRefreshed(presentTime);
- // Set presentation information before calling postComposition, such that jank information from
- // this' frame classification is already available when sending jank info to clients.
- mFrameTimeline->setSfPresent(systemTime(),
- std::make_shared<FenceTime>(mPreviousPresentFences[0]));
-
postFrame();
postComposition();
@@ -2120,11 +2114,6 @@
ATRACE_CALL();
ALOGV("postComposition");
- nsecs_t dequeueReadyTime = systemTime();
- for (const auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer(dequeueReadyTime);
- }
-
const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
@@ -2146,6 +2135,17 @@
auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
getBE().mDisplayTimeline.push(presentFenceTime);
+ // Set presentation information before calling Layer::releasePendingBuffer, such that jank
+ // information from previous' frame classification is already available when sending jank info
+ // to clients, so they get jank classification as early as possible.
+ mFrameTimeline->setSfPresent(systemTime(),
+ std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+
+ nsecs_t dequeueReadyTime = systemTime();
+ for (const auto& layer : mLayersWithQueuedFrames) {
+ layer->releasePendingBuffer(dequeueReadyTime);
+ }
+
const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
@@ -2473,7 +2473,7 @@
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
const auto physicalId = display->getPhysicalId();
- auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(physicalId));
+ auto activeConfigId = getHwComposer().getActiveMode(physicalId)->getId();
display->setActiveConfig(activeConfigId);
display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
@@ -2493,7 +2493,7 @@
ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
if (state.physical) {
const auto& activeConfig =
- getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
+ getCompositionEngine().getHwComposer().getActiveMode(state.physical->id);
width = activeConfig->getWidth();
height = activeConfig->getHeight();
pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
@@ -2620,7 +2620,10 @@
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
const auto displayId = currentState.physical->id;
- const auto configs = getHwComposer().getConfigs(displayId);
+ const auto configs = getHwComposer().getModes(displayId);
+ const auto currentConfig = getHwComposer().getActiveMode(displayId)->getId();
+ // TODO(b/175678215) Handle the case when currentConfig is not in configs
+ mRefreshRateConfigs->updateDisplayConfigs(configs, currentConfig);
mVsyncConfiguration->reset();
updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate());
if (mRefreshRateOverlay) {
@@ -2911,11 +2914,9 @@
return;
}
- auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
- mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
- primaryDisplayId),
- currentConfig);
+ auto currentConfig = getHwComposer().getActiveMode(primaryDisplayId)->getId();
+ const auto modes = getHwComposer().getModes(primaryDisplayId);
+ mRefreshRateConfigs = std::make_unique<scheduler::RefreshRateConfigs>(modes, currentConfig);
const auto& currRefreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig);
mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate.getFps(),
@@ -3376,8 +3377,10 @@
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
- if (pendingTransactions ||
- !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) {
+ // Call transactionIsReadyToBeApplied first in case we need to incrementPendingBufferCount
+ // if the transaction contains a buffer.
+ if (!transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true) ||
+ pendingTransactions) {
mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
desiredPresentTime, isAutoTimestamp, uncacheBuffer,
postTime, privileged, hasListenerCallbacks,
@@ -3840,7 +3843,9 @@
ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
}
}
+ std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
+ dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -3928,7 +3933,7 @@
: layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
- s.cachedBuffer, frameNumber)) {
+ s.cachedBuffer, frameNumber, dequeueBufferTimestamp)) {
flags |= eTraversalNeeded;
}
}
@@ -4507,12 +4512,9 @@
void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
result.append("Layer frame timestamps:\n");
-
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- currentLayers[i]->dumpFrameEvents(result);
- }
+ // Traverse all layers to dump frame-events for each layer
+ mCurrentState.traverseInZOrder(
+ [&] (Layer* layer) { layer->dumpFrameEvents(result); });
}
void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
@@ -4789,7 +4791,7 @@
if (const auto displayId = getInternalDisplayIdLocked();
displayId && getHwComposer().isConnected(*displayId)) {
- const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+ const auto activeConfig = getHwComposer().getActiveMode(*displayId);
std::string fps, xDpi, yDpi;
if (activeConfig) {
const auto vsyncPeriod = getHwComposer().getDisplayVsyncPeriod(*displayId);
@@ -5319,7 +5321,7 @@
ALOGE("No internal display found.");
return NO_ERROR;
}
- const auto numConfigs = getHwComposer().getConfigs(*displayId).size();
+ const auto numConfigs = getHwComposer().getModes(*displayId).size();
if (newConfigId >= 0 && newConfigId < numConfigs) {
const auto displayToken = getInternalDisplayToken();
status_t result = setActiveConfig(displayToken, newConfigId);
@@ -5406,16 +5408,16 @@
// mRefreshRateConfigs->getCurrentRefreshRate()
static_cast<void>(schedule([=] {
const auto desiredActiveConfig = getDesiredActiveConfig();
- const auto& current = desiredActiveConfig
- ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId)
- : mRefreshRateConfigs->getCurrentRefreshRate();
- const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+ const std::optional<HwcConfigIndexType> desiredConfigId = desiredActiveConfig
+ ? std::make_optional(desiredActiveConfig->configId)
+ : std::nullopt;
- if (current != min) {
- const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
+ const bool timerExpired = mKernelIdleTimerEnabled && expired;
+ const auto newRefreshRate =
+ mRefreshRateConfigs->onKernelTimerChanged(desiredConfigId, timerExpired);
+ if (newRefreshRate) {
if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+ mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
}
mEventQueue->invalidate();
}
@@ -6009,7 +6011,7 @@
if (!display->isPrimary()) {
// TODO(b/144711714): For non-primary displays we should be able to set an active config
- // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+ // as well. For now, just call directly to setActiveModeWithConstraints but ideally
// it should go thru setDesiredActiveConfig, similar to primary display.
ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
const auto displayId = display->getPhysicalId();
@@ -6019,8 +6021,8 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- if (getHwComposer().setActiveConfigWithConstraints(displayId, policy->defaultConfig.value(),
- constraints, &timeline) < 0) {
+ if (getHwComposer().setActiveModeWithConstraints(displayId, policy->defaultConfig,
+ constraints, &timeline) < 0) {
return BAD_VALUE;
}
if (timeline.refreshRequired) {
@@ -6029,7 +6031,7 @@
display->setActiveConfig(policy->defaultConfig);
const nsecs_t vsyncPeriod = getHwComposer()
- .getConfigs(displayId)[policy->defaultConfig.value()]
+ .getModes(displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
policy->defaultConfig, vsyncPeriod);
@@ -6065,20 +6067,20 @@
toggleKernelIdleTimer();
auto configId = mScheduler->getPreferredConfigId();
- auto& preferredRefreshRate = configId
+ auto preferredRefreshRate = configId
? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
// NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
: mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
- ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+ ALOGV("trying to switch to Scheduler preferred config %zu (%s)",
preferredRefreshRate.getConfigId().value(), preferredRefreshRate.getName().c_str());
if (isDisplayConfigAllowed(preferredRefreshRate.getConfigId())) {
- ALOGV("switching to Scheduler preferred config %d",
+ ALOGV("switching to Scheduler preferred config %zu",
preferredRefreshRate.getConfigId().value());
setDesiredActiveConfig(
{preferredRefreshRate.getConfigId(), Scheduler::ConfigEvent::Changed});
} else {
- LOG_ALWAYS_FATAL("Desired config not allowed: %d",
+ LOG_ALWAYS_FATAL("Desired config not allowed: %zu",
preferredRefreshRate.getConfigId().value());
}
@@ -6150,9 +6152,10 @@
return INVALID_OPERATION;
} else {
const auto displayId = display->getPhysicalId();
- *outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId);
+ const auto activeMode = getHwComposer().getActiveMode(displayId);
+ *outDefaultConfig = activeMode->getId().value();
*outAllowGroupSwitching = false;
- auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod();
+ auto vsyncPeriod = activeMode->getVsyncPeriod();
*outPrimaryRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
*outPrimaryRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
*outAppRequestRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
@@ -6371,7 +6374,8 @@
mRefreshRateOverlay->setViewport(display->getSize());
}
- mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+ mRefreshRateOverlay->changeRefreshRate(
+ mRefreshRateConfigs->getCurrentRefreshRate().getFps());
}
}));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9f0f2ea..7253593 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -99,6 +99,8 @@
class TimeStats;
class FrameTracer;
+using gui::ScreenCaptureResults;
+
namespace frametimeline {
class FrameTimeline;
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 97725ec..c043866 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -376,5 +376,9 @@
defaultValue);
}
+bool enable_frame_rate_override(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 37a6b40..816cb09 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -98,6 +98,8 @@
bool update_device_product_info_on_hotplug_reconnect(bool defaultValue);
+bool enable_frame_rate_override(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 421484f..4d25a7a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -445,3 +445,12 @@
access: Readonly
prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
}
+
+# Enables the frame rate override feature
+prop {
+ api_name: "enable_frame_rate_override"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_frame_rate_override"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index da66ece..0e0be09 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -41,6 +41,10 @@
prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
}
prop {
+ api_name: "enable_frame_rate_override"
+ prop_name: "ro.surface_flinger.enable_frame_rate_override"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index e66df4a..ecfed13 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -22,6 +22,8 @@
#include <gui/LayerState.h>
namespace android {
+using gui::ScreenCaptureResults;
+
namespace test {
TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
@@ -86,11 +88,11 @@
results.result = BAD_VALUE;
Parcel p;
- results.write(p);
+ results.writeToParcel(&p);
p.setDataPosition(0);
ScreenCaptureResults results2;
- results2.read(p);
+ results2.readFromParcel(&p);
// GraphicBuffer object is reallocated so compare the data in the graphic buffer
// rather than the object itself
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f2fe3cc..13b26fc 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -81,7 +81,6 @@
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
- "mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 169698b..e2584e2 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -33,8 +33,13 @@
using testing::AtLeast;
using testing::Contains;
using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
-using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
-using ProtoSurfaceFrame = perfetto::protos::FrameTimelineEvent_SurfaceFrame;
+using ProtoExpectedDisplayFrameStart =
+ perfetto::protos::FrameTimelineEvent_ExpectedDisplayFrameStart;
+using ProtoExpectedSurfaceFrameStart =
+ perfetto::protos::FrameTimelineEvent_ExpectedSurfaceFrameStart;
+using ProtoActualDisplayFrameStart = perfetto::protos::FrameTimelineEvent_ActualDisplayFrameStart;
+using ProtoActualSurfaceFrameStart = perfetto::protos::FrameTimelineEvent_ActualSurfaceFrameStart;
+using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd;
using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
@@ -67,10 +72,11 @@
void SetUp() override {
mTimeStats = std::make_shared<mock::TimeStats>();
- mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
kTestThresholds);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
+ mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
}
@@ -130,22 +136,31 @@
a.presentTime == b.presentTime;
}
- const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
+ const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
return mTokenManager->mPredictions;
}
- uint32_t getNumberOfDisplayFrames() {
+ uint32_t getNumberOfDisplayFrames() const {
std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
}
+ int64_t snoopCurrentTraceCookie() const { return mTraceCookieCounter->mTraceCookie; }
+
+ void flushTrace() {
+ using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+ FrameTimelineDataSource::Trace(
+ [&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); });
+ }
+
std::shared_ptr<mock::TimeStats> mTimeStats;
std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
impl::TokenManager* mTokenManager;
+ TraceCookieCounter* mTraceCookieCounter;
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
nsecs_t maxTokenRetentionTime;
- pid_t mSurfaceFlingerPid = 666;
+ static constexpr pid_t kSurfaceFlingerPid = 666;
static constexpr nsecs_t kPresentThreshold =
std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
static constexpr nsecs_t kDeadlineThreshold =
@@ -549,7 +564,7 @@
TEST_F(FrameTimelineTest, tracing_sanityTest) {
auto tracingSession = getTracingSessionForTest();
// Global increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
// Layer specific increment
EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -574,23 +589,18 @@
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
- // The SurfaceFrame packet from the first frame is emitted, but not flushed yet. Emitting a new
- // packet will flush it. To emit a new packet, we'll need to call flushPendingPresentFences()
- // again, which is done by setSfPresent().
- addEmptyDisplayFrame();
+ flushTrace();
tracingSession->StopBlocking();
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
- // Display Frame 1 has two packets - DisplayFrame and a SurfaceFrame.
- // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
- // flushed through traced, so this is not counted.
- EXPECT_EQ(packets.size(), 2);
+ // Display Frame 1 has 8 packets - 4 from DisplayFrame and 4 from SurfaceFrame.
+ EXPECT_EQ(packets.size(), 8);
}
TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
// Global increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -608,20 +618,17 @@
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
- addEmptyDisplayFrame();
+ flushTrace();
tracingSession->StopBlocking();
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
- // Display Frame 1 has no packets.
- // Display Frame 2 has one packet - DisplayFrame. However, this packet has
- // been emitted but not flushed through traced, so this is not counted.
EXPECT_EQ(packets.size(), 0);
}
TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
// Global increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -644,21 +651,38 @@
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
- addEmptyDisplayFrame();
+ flushTrace();
tracingSession->StopBlocking();
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
- // Display Frame 1 has one packet - DisplayFrame (SurfaceFrame shouldn't be traced since it has
- // an invalid token).
- // Display Frame 2 has one packet - DisplayFrame. However, this packet has
- // been emitted but not flushed through traced, so this is not counted.
- EXPECT_EQ(packets.size(), 1);
+ // Display Frame 1 has 4 packets (SurfaceFrame shouldn't be traced since it has an invalid
+ // token).
+ EXPECT_EQ(packets.size(), 4);
}
-void validateDisplayFrameEvent(const ProtoDisplayFrame& received, const ProtoDisplayFrame& source) {
+void validateTraceEvent(const ProtoExpectedDisplayFrameStart& received,
+ const ProtoExpectedDisplayFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
ASSERT_TRUE(received.has_token());
EXPECT_EQ(received.token(), source.token());
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+}
+
+void validateTraceEvent(const ProtoActualDisplayFrameStart& received,
+ const ProtoActualDisplayFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
ASSERT_TRUE(received.has_present_type());
EXPECT_EQ(received.present_type(), source.present_type());
ASSERT_TRUE(received.has_on_time_finish());
@@ -667,25 +691,43 @@
EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
ASSERT_TRUE(received.has_jank_type());
EXPECT_EQ(received.jank_type(), source.jank_type());
-
- ASSERT_TRUE(received.has_expected_start_ns());
- EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns());
- ASSERT_TRUE(received.has_expected_end_ns());
- EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns());
-
- ASSERT_TRUE(received.has_actual_start_ns());
- EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns());
- ASSERT_TRUE(received.has_actual_end_ns());
- EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns());
}
-void validateSurfaceFrameEvent(const ProtoSurfaceFrame& received, const ProtoSurfaceFrame& source) {
+void validateTraceEvent(const ProtoExpectedSurfaceFrameStart& received,
+ const ProtoExpectedSurfaceFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
ASSERT_TRUE(received.has_token());
EXPECT_EQ(received.token(), source.token());
ASSERT_TRUE(received.has_display_frame_token());
EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
+ ASSERT_TRUE(received.has_layer_name());
+ EXPECT_EQ(received.layer_name(), source.layer_name());
+}
+
+void validateTraceEvent(const ProtoActualSurfaceFrameStart& received,
+ const ProtoActualSurfaceFrameStart& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
+
+ ASSERT_TRUE(received.has_token());
+ EXPECT_EQ(received.token(), source.token());
+
+ ASSERT_TRUE(received.has_display_frame_token());
+ EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
+
+ ASSERT_TRUE(received.has_pid());
+ EXPECT_EQ(received.pid(), source.pid());
+
+ ASSERT_TRUE(received.has_layer_name());
+ EXPECT_EQ(received.layer_name(), source.layer_name());
+
ASSERT_TRUE(received.has_present_type());
EXPECT_EQ(received.present_type(), source.present_type());
ASSERT_TRUE(received.has_on_time_finish());
@@ -694,27 +736,17 @@
EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
ASSERT_TRUE(received.has_jank_type());
EXPECT_EQ(received.jank_type(), source.jank_type());
+}
- ASSERT_TRUE(received.has_expected_start_ns());
- EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns());
- ASSERT_TRUE(received.has_expected_end_ns());
- EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns());
-
- ASSERT_TRUE(received.has_actual_start_ns());
- EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns());
- ASSERT_TRUE(received.has_actual_end_ns());
- EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns());
-
- ASSERT_TRUE(received.has_layer_name());
- EXPECT_EQ(received.layer_name(), source.layer_name());
- ASSERT_TRUE(received.has_pid());
- EXPECT_EQ(received.pid(), source.pid());
+void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
+ ASSERT_TRUE(received.has_cookie());
+ EXPECT_EQ(received.cookie(), source.cookie());
}
TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
// Global increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -727,16 +759,27 @@
mFrameTimeline->setSfPresent(26, presentFence1);
presentFence1->signalForTest(31);
- ProtoDisplayFrame protoDisplayFrame;
- protoDisplayFrame.set_token(displayFrameToken1);
- protoDisplayFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
- protoDisplayFrame.set_on_time_finish(true);
- protoDisplayFrame.set_gpu_composition(false);
- protoDisplayFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
- protoDisplayFrame.set_expected_start_ns(10);
- protoDisplayFrame.set_expected_end_ns(25);
- protoDisplayFrame.set_actual_start_ns(20);
- protoDisplayFrame.set_actual_end_ns(26);
+ int64_t traceCookie = snoopCurrentTraceCookie();
+ ProtoExpectedDisplayFrameStart protoExpectedDisplayFrameStart;
+ protoExpectedDisplayFrameStart.set_cookie(traceCookie + 1);
+ protoExpectedDisplayFrameStart.set_token(displayFrameToken1);
+ protoExpectedDisplayFrameStart.set_pid(kSurfaceFlingerPid);
+
+ ProtoFrameEnd protoExpectedDisplayFrameEnd;
+ protoExpectedDisplayFrameEnd.set_cookie(traceCookie + 1);
+
+ ProtoActualDisplayFrameStart protoActualDisplayFrameStart;
+ protoActualDisplayFrameStart.set_cookie(traceCookie + 2);
+ protoActualDisplayFrameStart.set_token(displayFrameToken1);
+ protoActualDisplayFrameStart.set_pid(kSurfaceFlingerPid);
+ protoActualDisplayFrameStart.set_present_type(
+ ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
+ protoActualDisplayFrameStart.set_on_time_finish(true);
+ protoActualDisplayFrameStart.set_gpu_composition(false);
+ protoActualDisplayFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
+
+ ProtoFrameEnd protoActualDisplayFrameEnd;
+ protoActualDisplayFrameEnd.set_cookie(traceCookie + 2);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
@@ -744,30 +787,61 @@
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
- addEmptyDisplayFrame();
+ flushTrace();
tracingSession->StopBlocking();
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
- // Display Frame 1 has one packet - DisplayFrame.
- // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
- // flushed through traced, so this is not counted.
- EXPECT_EQ(packets.size(), 1);
+ EXPECT_EQ(packets.size(), 4);
- const auto& packet = packets[0];
- ASSERT_TRUE(packet.has_timestamp());
- ASSERT_TRUE(packet.has_frame_timeline_event());
+ // Packet - 0 : ExpectedDisplayFrameStart
+ const auto& packet0 = packets[0];
+ ASSERT_TRUE(packet0.has_timestamp());
+ EXPECT_EQ(packet0.timestamp(), 10);
+ ASSERT_TRUE(packet0.has_frame_timeline_event());
- const auto& event = packet.frame_timeline_event();
- ASSERT_TRUE(event.has_display_frame());
- ASSERT_FALSE(event.has_surface_frame());
- const auto& displayFrameEvent = event.display_frame();
- validateDisplayFrameEvent(displayFrameEvent, protoDisplayFrame);
+ const auto& event0 = packet0.frame_timeline_event();
+ ASSERT_TRUE(event0.has_expected_display_frame_start());
+ const auto& expectedDisplayFrameStart = event0.expected_display_frame_start();
+ validateTraceEvent(expectedDisplayFrameStart, protoExpectedDisplayFrameStart);
+
+ // Packet - 1 : FrameEnd (ExpectedDisplayFrame)
+ const auto& packet1 = packets[1];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), 25);
+ ASSERT_TRUE(packet1.has_frame_timeline_event());
+
+ const auto& event1 = packet1.frame_timeline_event();
+ ASSERT_TRUE(event1.has_frame_end());
+ const auto& expectedDisplayFrameEnd = event1.frame_end();
+ validateTraceEvent(expectedDisplayFrameEnd, protoExpectedDisplayFrameEnd);
+
+ // Packet - 2 : ActualDisplayFrameStart
+ const auto& packet2 = packets[2];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), 20);
+ ASSERT_TRUE(packet2.has_frame_timeline_event());
+
+ const auto& event2 = packet2.frame_timeline_event();
+ ASSERT_TRUE(event2.has_actual_display_frame_start());
+ const auto& actualDisplayFrameStart = event2.actual_display_frame_start();
+ validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart);
+
+ // Packet - 3 : FrameEnd (ActualDisplayFrame)
+ const auto& packet3 = packets[3];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 26);
+ ASSERT_TRUE(packet3.has_frame_timeline_event());
+
+ const auto& event3 = packet3.frame_timeline_event();
+ ASSERT_TRUE(event3.has_frame_end());
+ const auto& actualDisplayFrameEnd = event3.frame_end();
+ validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd);
}
TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
// Global increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
// Layer specific increment
EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -785,19 +859,33 @@
surfaceFrame1->setActualQueueTime(15);
surfaceFrame1->setAcquireFenceTime(20);
- ProtoSurfaceFrame protoSurfaceFrame;
- protoSurfaceFrame.set_token(surfaceFrameToken);
- protoSurfaceFrame.set_display_frame_token(displayFrameToken1);
- protoSurfaceFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
- protoSurfaceFrame.set_on_time_finish(true);
- protoSurfaceFrame.set_gpu_composition(false);
- protoSurfaceFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
- protoSurfaceFrame.set_expected_start_ns(10);
- protoSurfaceFrame.set_expected_end_ns(25);
- protoSurfaceFrame.set_actual_start_ns(0);
- protoSurfaceFrame.set_actual_end_ns(20);
- protoSurfaceFrame.set_layer_name(sLayerNameOne);
- protoSurfaceFrame.set_pid(sPidOne);
+ // First 2 cookies will be used by the DisplayFrame
+ int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+ ProtoExpectedSurfaceFrameStart protoExpectedSurfaceFrameStart;
+ protoExpectedSurfaceFrameStart.set_cookie(traceCookie + 1);
+ protoExpectedSurfaceFrameStart.set_token(surfaceFrameToken);
+ protoExpectedSurfaceFrameStart.set_display_frame_token(displayFrameToken1);
+ protoExpectedSurfaceFrameStart.set_pid(sPidOne);
+ protoExpectedSurfaceFrameStart.set_layer_name(sLayerNameOne);
+
+ ProtoFrameEnd protoExpectedSurfaceFrameEnd;
+ protoExpectedSurfaceFrameEnd.set_cookie(traceCookie + 1);
+
+ ProtoActualSurfaceFrameStart protoActualSurfaceFrameStart;
+ protoActualSurfaceFrameStart.set_cookie(traceCookie + 2);
+ protoActualSurfaceFrameStart.set_token(surfaceFrameToken);
+ protoActualSurfaceFrameStart.set_display_frame_token(displayFrameToken1);
+ protoActualSurfaceFrameStart.set_pid(sPidOne);
+ protoActualSurfaceFrameStart.set_layer_name(sLayerNameOne);
+ protoActualSurfaceFrameStart.set_present_type(
+ ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
+ protoActualSurfaceFrameStart.set_on_time_finish(true);
+ protoActualSurfaceFrameStart.set_gpu_composition(false);
+ protoActualSurfaceFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
+
+ ProtoFrameEnd protoActualSurfaceFrameEnd;
+ protoActualSurfaceFrameEnd.set_cookie(traceCookie + 2);
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
@@ -812,24 +900,55 @@
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
- addEmptyDisplayFrame();
+ flushTrace();
tracingSession->StopBlocking();
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
- // Display Frame 1 has one packet - DisplayFrame and a SurfaceFrame.
- // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
- // flushed through traced, so this is not counted.
- EXPECT_EQ(packets.size(), 2);
+ EXPECT_EQ(packets.size(), 8);
- const auto& packet = packets[1];
- ASSERT_TRUE(packet.has_timestamp());
- ASSERT_TRUE(packet.has_frame_timeline_event());
+ // Packet - 4 : ExpectedSurfaceFrameStart
+ const auto& packet4 = packets[4];
+ ASSERT_TRUE(packet4.has_timestamp());
+ EXPECT_EQ(packet4.timestamp(), 10);
+ ASSERT_TRUE(packet4.has_frame_timeline_event());
- const auto& event = packet.frame_timeline_event();
- ASSERT_TRUE(!event.has_display_frame());
- ASSERT_TRUE(event.has_surface_frame());
- const auto& surfaceFrameEvent = event.surface_frame();
- validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
+ const auto& event4 = packet4.frame_timeline_event();
+ ASSERT_TRUE(event4.has_expected_surface_frame_start());
+ const auto& expectedSurfaceFrameStart = event4.expected_surface_frame_start();
+ validateTraceEvent(expectedSurfaceFrameStart, protoExpectedSurfaceFrameStart);
+
+ // Packet - 5 : FrameEnd (ExpectedSurfaceFrame)
+ const auto& packet5 = packets[5];
+ ASSERT_TRUE(packet5.has_timestamp());
+ EXPECT_EQ(packet5.timestamp(), 25);
+ ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+ const auto& event5 = packet5.frame_timeline_event();
+ ASSERT_TRUE(event5.has_frame_end());
+ const auto& expectedSurfaceFrameEnd = event5.frame_end();
+ validateTraceEvent(expectedSurfaceFrameEnd, protoExpectedSurfaceFrameEnd);
+
+ // Packet - 6 : ActualSurfaceFrameStart
+ const auto& packet6 = packets[6];
+ ASSERT_TRUE(packet6.has_timestamp());
+ EXPECT_EQ(packet6.timestamp(), 10);
+ ASSERT_TRUE(packet6.has_frame_timeline_event());
+
+ const auto& event6 = packet6.frame_timeline_event();
+ ASSERT_TRUE(event6.has_actual_surface_frame_start());
+ const auto& actualSurfaceFrameStart = event6.actual_surface_frame_start();
+ validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+ // Packet - 7 : FrameEnd (ActualSurfaceFrame)
+ const auto& packet7 = packets[7];
+ ASSERT_TRUE(packet7.has_timestamp());
+ EXPECT_EQ(packet7.timestamp(), 20);
+ ASSERT_TRUE(packet7.has_frame_timeline_event());
+
+ const auto& event7 = packet7.frame_timeline_event();
+ ASSERT_TRUE(event7.has_frame_end());
+ const auto& actualSurfaceFrameEnd = event7.frame_end();
+ validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
}
// Tests for Jank classification
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index bc1e88a..5bab534 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -190,10 +190,10 @@
.WillRepeatedly(DoAll(SetArgPointee<3>(1), Return(V2_1::Error::NONE)));
}
- void testSetActiveConfigWithConstraintsCommon(bool isVsyncPeriodSwitchSupported);
+ void testSetActiveModeWithConstraintsCommon(bool isVsyncPeriodSwitchSupported);
};
-void HWComposerConfigsTest::testSetActiveConfigWithConstraintsCommon(
+void HWComposerConfigsTest::testSetActiveModeWithConstraintsCommon(
bool isVsyncPeriodSwitchSupported) {
EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
@@ -229,9 +229,9 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- constexpr size_t kConfigIndex = 0;
+ constexpr HwcConfigIndexType kConfigIndex(0);
const auto status =
- hwc.setActiveConfigWithConstraints(physicalId, kConfigIndex, constraints, &timeline);
+ hwc.setActiveModeWithConstraints(physicalId, kConfigIndex, constraints, &timeline);
EXPECT_EQ(NO_ERROR, status);
const std::vector<Config> kConfigs{7, 8, 9, 10, 11};
@@ -243,17 +243,18 @@
for (size_t configIndex = 0; configIndex < kConfigs.size(); configIndex++) {
const auto status =
- hwc.setActiveConfigWithConstraints(physicalId, configIndex, constraints, &timeline);
+ hwc.setActiveModeWithConstraints(physicalId, HwcConfigIndexType(configIndex),
+ constraints, &timeline);
EXPECT_EQ(NO_ERROR, status) << "Error when switching to config " << configIndex;
}
}
-TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingSupported) {
- testSetActiveConfigWithConstraintsCommon(/*supported=*/true);
+TEST_F(HWComposerConfigsTest, setActiveModeWithConstraintsWithVsyncSwitchingSupported) {
+ testSetActiveModeWithConstraintsCommon(/*supported=*/true);
}
-TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingNotSupported) {
- testSetActiveConfigWithConstraintsCommon(/*supported=*/false);
+TEST_F(HWComposerConfigsTest, setActiveModeWithConstraintsWithVsyncSwitchingNotSupported) {
+ testSetActiveModeWithConstraintsCommon(/*supported=*/false);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 612ed19..7024c1e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -110,12 +110,11 @@
<< "Frame rate is " << frameRate;
}
- Hwc2::mock::Display mDisplay;
- RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ RefreshRateConfigs mConfigs{{DisplayMode::Builder(0)
.setVsyncPeriod(int32_t(LO_FPS_PERIOD))
.setConfigGroup(0)
.build(),
- HWC2::Display::Config::Builder(mDisplay, 1)
+ DisplayMode::Builder(1)
.setVsyncPeriod(int32_t(HI_FPS_PERIOD))
.setConfigGroup(0)
.build()},
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 45f0ee6..27c181d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -25,13 +25,13 @@
#include <log/log.h>
#include <thread>
+#include <ui/Size.h>
+
#include "../../Scheduler/RefreshRateConfigs.h"
#include "DisplayHardware/HWC2.h"
#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
using namespace std::chrono_literals;
-using testing::_;
namespace android {
@@ -56,6 +56,21 @@
return refreshRateConfigs.mKnownFrameRates;
}
+ RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return refreshRateConfigs.getMinRefreshRateByPolicyLocked();
+ }
+
+ RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return *refreshRateConfigs.mMinSupportedRefreshRate;
+ }
+
+ RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return *refreshRateConfigs.mMaxSupportedRefreshRate;
+ }
+
// Test config IDs
static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
@@ -66,67 +81,48 @@
static inline const HwcConfigIndexType HWC_CONFIG_ID_50 = HwcConfigIndexType(6);
// Test configs
- std::shared_ptr<const HWC2::Display::Config> mConfig60 =
- createConfig(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig90 =
- createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentGroup =
+ DisplayModePtr mConfig60 = createConfig(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+ DisplayModePtr mConfig90 = createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
+ DisplayModePtr mConfig90DifferentGroup =
createConfig(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentResolution =
- createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), 111, 222);
- std::shared_ptr<const HWC2::Display::Config> mConfig72 =
- createConfig(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig72DifferentGroup =
+ DisplayModePtr mConfig90DifferentResolution =
+ createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
+ DisplayModePtr mConfig72 = createConfig(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
+ DisplayModePtr mConfig72DifferentGroup =
createConfig(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig120 =
- createConfig(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig120DifferentGroup =
+ DisplayModePtr mConfig120 = createConfig(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
+ DisplayModePtr mConfig120DifferentGroup =
createConfig(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig30 =
- createConfig(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig30DifferentGroup =
+ DisplayModePtr mConfig30 = createConfig(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
+ DisplayModePtr mConfig30DifferentGroup =
createConfig(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig25DifferentGroup =
+ DisplayModePtr mConfig25DifferentGroup =
createConfig(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
- std::shared_ptr<const HWC2::Display::Config> mConfig50 =
- createConfig(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+ DisplayModePtr mConfig50 = createConfig(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
// Test device configurations
// The positions of the configs in the arrays below MUST match their IDs. For example,
// the first config should always be 60Hz, the second 90Hz etc.
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups =
- {mConfig60, mConfig90DifferentGroup};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentResolutions =
- {mConfig60, mConfig90DifferentResolution};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_72_90Device = {mConfig60,
- mConfig90,
- mConfig72};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90_72_120Device = {mConfig60,
- mConfig90,
- mConfig72,
- mConfig120};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90_120Device = {mConfig60,
- mConfig90,
- mConfig72,
- mConfig120,
- mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60Device =
- {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup, mConfig120DifferentGroup,
- mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90Device =
- {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device =
- {mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30};
- std::vector<std::shared_ptr<const HWC2::Display::Config>> m25_30_50_60Device =
- {mConfig60,
- mConfig90,
- mConfig72DifferentGroup,
- mConfig120DifferentGroup,
- mConfig30DifferentGroup,
- mConfig25DifferentGroup,
- mConfig50};
+ DisplayModes m60OnlyConfigDevice = {mConfig60};
+ DisplayModes m60_90Device = {mConfig60, mConfig90};
+ DisplayModes m60_90DeviceWithDifferentGroups = {mConfig60, mConfig90DifferentGroup};
+ DisplayModes m60_90DeviceWithDifferentResolutions = {mConfig60, mConfig90DifferentResolution};
+ DisplayModes m60_72_90Device = {mConfig60, mConfig90, mConfig72};
+ DisplayModes m60_90_72_120Device = {mConfig60, mConfig90, mConfig72, mConfig120};
+ DisplayModes m30_60_72_90_120Device = {mConfig60, mConfig90, mConfig72, mConfig120, mConfig30};
+ DisplayModes m30_60Device = {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup,
+ mConfig120DifferentGroup, mConfig30};
+ DisplayModes m30_60_72_90Device = {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup,
+ mConfig30};
+ DisplayModes m30_60_90Device = {mConfig60, mConfig90, mConfig72DifferentGroup,
+ mConfig120DifferentGroup, mConfig30};
+ DisplayModes m25_30_50_60Device = {mConfig60,
+ mConfig90,
+ mConfig72DifferentGroup,
+ mConfig120DifferentGroup,
+ mConfig30DifferentGroup,
+ mConfig25DifferentGroup,
+ mConfig50};
// Expected RefreshRate objects
RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
@@ -147,18 +143,12 @@
RefreshRate::ConstructorTag(0)};
RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
RefreshRate::ConstructorTag(0)};
-
- Hwc2::mock::Display mDisplay;
-
private:
- std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
- int32_t configGroup,
- int64_t vsyncPeriod,
- int32_t hight = -1,
- int32_t width = -1);
+ DisplayModePtr createConfig(HwcConfigIndexType configId, int32_t configGroup,
+ int64_t vsyncPeriod, ui::Size resolution = ui::Size());
};
-using Builder = HWC2::Display::Config::Builder;
+using Builder = DisplayMode::Builder;
RefreshRateConfigsTest::RefreshRateConfigsTest() {
const ::testing::TestInfo* const test_info =
@@ -172,14 +162,14 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-std::shared_ptr<const HWC2::Display::Config> RefreshRateConfigsTest::createConfig(
- HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod, int32_t hight,
- int32_t width) {
- return HWC2::Display::Config::Builder(mDisplay, hal::HWConfigId(configId.value()))
+DisplayModePtr RefreshRateConfigsTest::createConfig(HwcConfigIndexType configId,
+ int32_t configGroup, int64_t vsyncPeriod,
+ ui::Size resolution) {
+ return DisplayMode::Builder(hal::HWConfigId(configId.value()))
.setVsyncPeriod(int32_t(vsyncPeriod))
.setConfigGroup(configGroup)
- .setHeight(hight)
- .setWidth(width)
+ .setHeight(resolution.height)
+ .setWidth(resolution.width)
.build();
}
@@ -209,13 +199,13 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRate();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected90Config, performanceRate);
- const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(minRateByPolicy, minRate);
ASSERT_EQ(performanceRateByPolicy, performanceRate);
@@ -226,9 +216,9 @@
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
- const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+ const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
@@ -239,7 +229,7 @@
0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate);
@@ -252,9 +242,9 @@
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
- const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+ const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
@@ -265,7 +255,7 @@
0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate);
@@ -278,8 +268,8 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected90Config, performanceRate);
@@ -287,8 +277,8 @@
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
0);
- auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
+ auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
}
@@ -298,20 +288,20 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60);
}
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
}
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
0);
{
- auto& current = refreshRateConfigs->getCurrentRefreshRate();
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
}
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 2188402..4a96fc5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -28,7 +28,6 @@
#include "Scheduler/HwcStrongTypes.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockTimeStats.h"
using namespace std::chrono_literals;
@@ -50,7 +49,7 @@
RefreshRateStatsTest();
~RefreshRateStatsTest();
- void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ void init(const DisplayModes& configs) {
mRefreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
@@ -59,14 +58,12 @@
/*currentPowerMode=*/PowerMode::OFF);
}
- Hwc2::mock::Display mDisplay;
mock::TimeStats mTimeStats;
std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<RefreshRateStats> mRefreshRateStats;
- std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
- int32_t configGroup,
- int64_t vsyncPeriod);
+ DisplayModePtr createConfig(HwcConfigIndexType configId, int32_t configGroup,
+ int64_t vsyncPeriod);
};
RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -81,9 +78,9 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-std::shared_ptr<const HWC2::Display::Config> RefreshRateStatsTest::createConfig(
- HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod) {
- return HWC2::Display::Config::Builder(mDisplay, static_cast<hal::HWConfigId>(configId.value()))
+DisplayModePtr RefreshRateStatsTest::createConfig(HwcConfigIndexType configId, int32_t configGroup,
+ int64_t vsyncPeriod) {
+ return DisplayMode::Builder(static_cast<hal::HWConfigId>(configId.value()))
.setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
.setConfigGroup(configGroup)
.build();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index fb02a33..757c702 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -24,7 +24,6 @@
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockEventThread.h"
#include "mock/MockLayer.h"
#include "mock/MockSchedulerCallback.h"
@@ -52,12 +51,9 @@
SchedulerTest();
- Hwc2::mock::Display mDisplay;
- const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(16'666'667)
- .setConfigGroup(0)
- .build()},
- HwcConfigIndexType(0)};
+ const scheduler::RefreshRateConfigs
+ mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setConfigGroup(0).build()},
+ HwcConfigIndexType(0)};
mock::SchedulerCallback mSchedulerCallback;
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
index 5406879..45b7610 100644
--- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -24,7 +24,6 @@
TEST(StrongTypeTest, comparison) {
using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>;
- SpunkyType f2(22);
SpunkyType f1(10);
EXPECT_TRUE(f1 == f1);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
index 65efc85..8552e15 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -604,11 +604,11 @@
EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
+TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
using Case = NonHwcVirtualDisplayCase;
- const Rect oldFrame(0, 0, 0, 0);
- const Rect newFrame(0, 0, 123, 456);
+ const Rect oldDisplayRect(0, 0);
+ const Rect newDisplayRect(123, 456);
// --------------------------------------------------------------------
// Preconditions
@@ -618,8 +618,8 @@
display.inject();
// There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldDisplayRect;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newDisplayRect;
// --------------------------------------------------------------------
// Invocation
@@ -629,7 +629,7 @@
// --------------------------------------------------------------------
// Postconditions
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+ EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
}
TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
@@ -722,5 +722,61 @@
mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
}
+TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr uint32_t kOldWidth = 567;
+ constexpr uint32_t kOldHeight = 456;
+ const auto kOldSize = Rect(kOldWidth, kOldHeight);
+
+ constexpr uint32_t kNewWidth = 234;
+ constexpr uint32_t kNewHeight = 123;
+ const auto kNewSize = Rect(kNewWidth, kNewHeight);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kOldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kOldHeight), Return(0)));
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = kOldWidth;
+ display.mutableDrawingDisplayState().height = kOldHeight;
+ display.mutableDrawingDisplayState().layerStackSpaceRect = kOldSize;
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = kOldSize;
+
+ display.mutableCurrentDisplayState().width = kNewWidth;
+ display.mutableCurrentDisplayState().height = kNewHeight;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = kNewSize;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = kNewSize;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(kNewWidth, kNewHeight)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
+ EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
+ EXPECT_EQ(display.mutableDisplayDevice()->getHeight(), kNewHeight);
+ EXPECT_EQ(display.mutableDisplayDevice()->getOrientedDisplaySpaceRect(), kNewSize);
+ EXPECT_EQ(display.mutableDisplayDevice()->getLayerStackSpaceRect(), kNewSize);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b57d473..2b8a67d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -23,6 +23,7 @@
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
+#include <gui/ScreenCaptureResults.h>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -40,7 +41,6 @@
#include "SurfaceFlingerDefaultFactory.h"
#include "SurfaceInterceptor.h"
#include "TestableScheduler.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockDisplayIdGenerator.h"
#include "mock/MockFrameTimeline.h"
#include "mock/MockFrameTracer.h"
@@ -210,17 +210,12 @@
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(16'666'667)
- .setConfigGroup(0)
- .build()};
+ DisplayModes configs{
+ DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setConfigGroup(0).build()};
if (hasMultipleConfigs) {
- configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
- .setVsyncPeriod(11'111'111)
- .setConfigGroup(0)
- .build());
+ configs.emplace_back(
+ DisplayMode::Builder(1).setVsyncPeriod(11'111'111).setConfigGroup(0).build());
}
const auto currConfig = HwcConfigIndexType(0);
@@ -469,7 +464,6 @@
}
auto& mutableIsConnected() { return this->mIsConnected; }
- auto& mutableConfigs() { return this->mConfigs; }
auto& mutableLayers() { return this->mLayers; }
};
@@ -544,19 +538,20 @@
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
- auto config = HWC2::Display::Config::Builder(*display, mActiveConfig);
- config.setWidth(mWidth);
- config.setHeight(mHeight);
- config.setVsyncPeriod(mRefreshRate);
- config.setDpiX(mDpiX);
- config.setDpiY(mDpiY);
- config.setConfigGroup(mConfigGroup);
- display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
display->mutableIsConnected() = true;
display->setPowerMode(mPowerMode);
-
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
+ auto config = DisplayMode::Builder(mActiveConfig)
+ .setWidth(mWidth)
+ .setHeight(mHeight)
+ .setVsyncPeriod(mRefreshRate)
+ .setDpiX(mDpiX)
+ .setDpiY(mDpiY)
+ .setConfigGroup(mConfigGroup)
+ .build();
+ flinger->mutableHwcDisplayData()[mDisplayId].modes.push_back(config);
+
if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
LOG_ALWAYS_FATAL_IF(!physicalId);
@@ -703,7 +698,6 @@
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
- Hwc2::mock::Display mDisplay;
mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
deleted file mode 100644
index a96d9db..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayHardware/HWC2.h"
-
-using android::HWC2::Layer;
-
-namespace android::Hwc2::mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-class Display : public HWC2::Display {
-public:
- using Layer = ::Layer;
-
- Display();
- ~Display();
-
- MOCK_CONST_METHOD0(getId, hal::HWDisplayId());
- MOCK_CONST_METHOD0(isConnected, bool());
- MOCK_METHOD1(setConnected, void(bool));
- MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<hal::DisplayCapability>&());
-
- MOCK_METHOD0(acceptChanges, hal::Error());
- MOCK_METHOD1(createLayer, hal::Error(Layer**));
- MOCK_METHOD1(destroyLayer, hal::Error(Layer*));
- MOCK_CONST_METHOD1(getActiveConfig, hal::Error(std::shared_ptr<const Config>*));
- MOCK_CONST_METHOD1(getActiveConfigIndex, hal::Error(int* outIndex));
- MOCK_METHOD1(getChangedCompositionTypes,
- hal::Error(std::unordered_map<Layer*, hal::Composition>*));
- MOCK_CONST_METHOD1(getColorModes, hal::Error(std::vector<hal::ColorMode>*));
-
- MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
- MOCK_CONST_METHOD2(getRenderIntents,
- hal::Error(hal::ColorMode, std::vector<hal::RenderIntent>*));
- MOCK_METHOD2(getDataspaceSaturationMatrix, hal::Error(hal::Dataspace, android::mat4*));
- MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
-
- MOCK_CONST_METHOD1(getName, hal::Error(std::string*));
- MOCK_METHOD2(getRequests,
- hal::Error(hal::DisplayRequest*, std::unordered_map<Layer*, hal::LayerRequest>*));
- MOCK_CONST_METHOD1(getType, hal::Error(hal::DisplayType*));
- MOCK_CONST_METHOD1(supportsDoze, hal::Error(bool*));
- MOCK_CONST_METHOD1(getHdrCapabilities, hal::Error(android::HdrCapabilities*));
- MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
- hal::Error(hal::PixelFormat*, hal::Dataspace*, uint8_t*));
- MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, hal::Error(bool, uint8_t, uint64_t));
- MOCK_CONST_METHOD3(getDisplayedContentSample,
- hal::Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
- MOCK_CONST_METHOD1(
- getReleaseFences,
- hal::Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
- MOCK_METHOD1(present, hal::Error(android::sp<android::Fence>*));
- MOCK_METHOD1(setActiveConfig, hal::Error(const std::shared_ptr<const HWC2::Display::Config>&));
- MOCK_METHOD4(setClientTarget,
- hal::Error(uint32_t, const android::sp<android::GraphicBuffer>&,
- const android::sp<android::Fence>&, hal::Dataspace));
- MOCK_METHOD2(setColorMode, hal::Error(hal::ColorMode, hal::RenderIntent));
- MOCK_METHOD2(setColorTransform, hal::Error(const android::mat4&, hal::ColorTransform));
- MOCK_METHOD2(setOutputBuffer,
- hal::Error(const android::sp<android::GraphicBuffer>&,
- const android::sp<android::Fence>&));
- MOCK_METHOD1(setPowerMode, hal::Error(hal::PowerMode));
- MOCK_METHOD1(setVsyncEnabled, hal::Error(hal::Vsync));
- MOCK_METHOD2(validate, hal::Error(uint32_t*, uint32_t*));
- MOCK_METHOD4(presentOrValidate,
- hal::Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
- MOCK_METHOD1(setDisplayBrightness, std::future<hal::Error>(float));
- MOCK_CONST_METHOD1(getDisplayVsyncPeriod, hal::Error(nsecs_t*));
- MOCK_METHOD3(setActiveConfigWithConstraints,
- hal::Error(const std::shared_ptr<const HWC2::Display::Config>&,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline*));
- MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
- MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
- MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
- MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
- MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
- MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
-};
-
-} // namespace android::Hwc2::mock