Merge "gralloc4: support crop as a seperate metadata type"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4a84884..0473bb8 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,7 @@
include/input/
libs/binder/fuzzer/
libs/binder/ndk/
+ libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
libs/input/
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
index 10db5d6..c1ec55e 100644
--- a/cmds/dumpstate/DumpstateInternal.h
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -49,6 +49,7 @@
// TODO: use functions from <chrono> instead
const uint64_t NANOS_PER_SEC = 1000000000;
+const uint64_t NANOS_PER_MILLI = 1000000;
uint64_t Nanotime();
// Switches to non-root user and group.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index fc94f23..8e0f8a3 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -135,6 +135,11 @@
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = nullptr;
static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
+// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes),
+// it's often the case that they time out far too quickly for consent with such a hefty dialog for
+// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to
+// roughly match full reports' durations.
+static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000;
// TODO: variables and functions below should be part of dumpstate object
@@ -149,12 +154,15 @@
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PREREBOOT_DATA_DIR "/data/misc/prereboot"
#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat"
#define WLUTIL "/vendor/xbin/wlutil"
#define WMTRACE_DATA_DIR "/data/misc/wmtrace"
#define OTA_METADATA_DIR "/metadata/ota"
+#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
+#define LINKERCONFIG_DIR "/linkerconfig"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -649,7 +657,7 @@
}
uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
- return Nanotime() - start_time_;
+ return (Nanotime() - start_time_) / NANOS_PER_MILLI;
}
void Dumpstate::PrintHeader() const {
@@ -877,6 +885,14 @@
CommandOptions::WithTimeoutInMs(timeout_ms).Build());
}
+static void DoRadioLogcat() {
+ unsigned long timeout_ms = logcat_timeout({"radio"});
+ RunCommand(
+ "RADIO LOG",
+ {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+}
+
static void DoLogcat() {
unsigned long timeout_ms;
// DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
@@ -895,11 +911,7 @@
"STATS LOG",
{"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
- timeout_ms = logcat_timeout({"radio"});
- RunCommand(
- "RADIO LOG",
- {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
- CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+ DoRadioLogcat();
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
@@ -1427,11 +1439,14 @@
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
/* Binder state is expensive to look at as it uses a lot of memory. */
- DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
- DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
- DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
- DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
- DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
+ std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ?
+ "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs";
+
+ DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log");
+ DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log");
+ DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions");
+ DumpFile("BINDER STATS", binder_logs_dir + "/stats");
+ DumpFile("BINDER STATE", binder_logs_dir + "/state");
/* Add window and surface trace files. */
if (!PropertiesHelper::IsUserBuild()) {
@@ -1532,6 +1547,9 @@
// This differs from the usual dumpsys stats, which is the stats report data.
RunDumpsys("STATSDSTATS", {"stats", "--metadata"});
+ // Add linker configuration directory
+ ds.AddDir(LINKERCONFIG_DIR, true);
+
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
return Dumpstate::RunStatus::OK;
@@ -1563,10 +1581,12 @@
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
+ ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
+ ds.AddDir(PREREBOOT_DATA_DIR, false);
add_mountinfo();
DumpIpTablesAsRoot();
DumpDynamicPartitionInfo();
@@ -1605,8 +1625,10 @@
return status;
}
-// This method collects common dumpsys for telephony and wifi
-static void DumpstateRadioCommon() {
+// This method collects common dumpsys for telephony and wifi. Typically, wifi
+// reports are fine to include all information, but telephony reports on user
+// builds need to strip some content (see DumpstateTelephonyOnly).
+static void DumpstateRadioCommon(bool include_sensitive_info = true) {
DumpIpTablesAsRoot();
ds.AddDir(LOGPERSIST_DATA_DIR, false);
@@ -1615,27 +1637,51 @@
return;
}
- do_dmesg();
- DoLogcat();
+ // We need to be picky about some stuff for telephony reports on user builds.
+ if (!include_sensitive_info) {
+ // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
+ DoRadioLogcat();
+ } else {
+ // Contains various system properties and process startup info.
+ do_dmesg();
+ // Logs other than the radio buffer may contain package/component names and potential PII.
+ DoLogcat();
+ // Too broad for connectivity problems.
+ DoKmsg();
+ // Contains unrelated hardware info (camera, NFC, biometrics, ...).
+ DumpHals();
+ }
+
DumpPacketStats();
- DoKmsg();
DumpIpAddrAndRules();
dump_route_tables();
- DumpHals();
-
RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
CommandOptions::WithTimeout(10).Build());
}
-// This method collects dumpsys for telephony debugging only
+// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular
+// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules
+// for what can be included on user builds: all reported information MUST directly relate to
+// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable
+// information. This information MUST NOT identify user-installed packages (UIDs are OK, package
+// names are not), and MUST NOT contain logs of user application traffic.
+// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead.
static void DumpstateTelephonyOnly() {
DurationReporter duration_reporter("DUMPSTATE");
const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
- DumpstateRadioCommon();
+ const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
- RunCommand("SYSTEM PROPERTIES", {"getprop"});
+ DumpstateRadioCommon(include_sensitive_info);
+
+ if (include_sensitive_info) {
+ // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't
+ // really cherrypick all of the connectivity-related ones. Apps generally have no business
+ // reading these anyway, and there should be APIs to supply the info in a more app-friendly
+ // way.
+ RunCommand("SYSTEM PROPERTIES", {"getprop"});
+ }
printf("========================================================\n");
printf("== Android Framework Services\n");
@@ -1643,15 +1689,28 @@
RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
- RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
- SEC_TO_MSEC(10));
- RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ // TODO(b/146521742) build out an argument to include bound services here for user builds
RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
- RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+ RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ if (include_sensitive_info) {
+ // Contains raw IP addresses, omit from reports on user builds.
+ RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ // Contains raw destination IP/MAC addresses, omit from reports on user builds.
+ RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ // Contains package/component names, omit from reports on user builds.
+ RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ // Contains package names, but should be relatively simple to remove them (also contains
+ // UIDs already), omit from reports on user builds.
+ RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ }
printf("========================================================\n");
printf("== Running Application Services\n");
@@ -1659,18 +1718,24 @@
RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"});
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
+ if (include_sensitive_info) {
+ printf("========================================================\n");
+ printf("== Running Application Services (non-platform)\n");
+ printf("========================================================\n");
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ // Contains package/component names and potential PII, omit from reports on user builds.
+ // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the
+ // carrier_config dumpsys instead.
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ printf("========================================================\n");
+ printf("== Checkins\n");
+ printf("========================================================\n");
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ // Contains package/component names, omit from reports on user builds.
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ }
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
@@ -2151,6 +2216,7 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
options->telephony_only = true;
+ options->do_progress_updates = true;
options->do_fb = false;
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
@@ -2635,8 +2701,13 @@
if (consent_result == UserConsentResult::UNAVAILABLE) {
// User has not responded yet.
uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
- if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
- uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+ // Telephony is a fast report type, particularly on user builds where information may be
+ // more aggressively limited. To give the user time to read the consent dialog, increase the
+ // timeout.
+ uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS
+ : USER_CONSENT_TIMEOUT_MS;
+ if (elapsed_ms < timeout_ms) {
+ uint delay_seconds = (timeout_ms - elapsed_ms) / 1000;
MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
sleep(delay_seconds);
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 1ae073c..718f459 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -295,12 +295,12 @@
EXPECT_FALSE(options_.do_fb);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
+ EXPECT_TRUE(options_.do_progress_updates);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
- EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
}
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index d398559..3a3df08 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/DisplayInfo.h>
-#include <gui/SurfaceComposerClient.h>
-
#include "GLHelper.h"
- namespace android {
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+
+namespace android {
GLHelper::GLHelper() :
mDisplay(EGL_NO_DISPLAY),
@@ -228,15 +227,15 @@
return false;
}
- DisplayInfo info;
- status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info);
+ DisplayConfig config;
+ status_t err = mSurfaceComposerClient->getActiveDisplayConfig(dpy, &config);
if (err != NO_ERROR) {
- fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err);
+ fprintf(stderr, "SurfaceComposer::getActiveDisplayConfig failed: %#x\n", err);
return false;
}
- float scaleX = float(info.w) / float(w);
- float scaleY = float(info.h) / float(h);
+ float scaleX = static_cast<float>(config.resolution.getWidth()) / w;
+ float scaleY = static_cast<float>(config.resolution.getHeight()) / h;
*scale = scaleX < scaleY ? scaleX : scaleY;
return true;
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7b0d5d..08d4657 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1102,7 +1102,7 @@
binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
const std::string& dataAppName, int32_t appId, const std::string& seInfo,
- int32_t targetSdkVersion) {
+ int32_t targetSdkVersion, const std::string& fromCodePath) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(fromUuid);
CHECK_ARGUMENT_UUID(toUuid);
@@ -1119,13 +1119,12 @@
// Copy app
{
- auto from = create_data_app_package_path(from_uuid, data_app_name);
auto to = create_data_app_package_path(to_uuid, data_app_name);
auto to_parent = create_data_app_path(to_uuid);
- int rc = copy_directory_recursive(from.c_str(), to_parent.c_str());
+ int rc = copy_directory_recursive(fromCodePath.c_str(), to_parent.c_str());
if (rc != 0) {
- res = error(rc, "Failed copying " + from + " to " + to);
+ res = error(rc, "Failed copying " + fromCodePath + " to " + to);
goto fail;
}
@@ -2250,26 +2249,6 @@
return *_aidl_return ? ok() : error("viewcompiler failed");
}
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* instruction_set = instructionSet.c_str();
-
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
- "%s/%s/%s/.booting",
- android_data_dir.c_str(),
- DALVIK_CACHE,
- instruction_set);
-
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- return error(StringPrintf("Failed to unlink %s", boot_marker_path));
- }
- return ok();
-}
-
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
const std::unique_ptr<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index bf11002..eb35fd3 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -97,7 +97,7 @@
binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
const std::string& dataAppName, int32_t appId, const std::string& seInfo,
- int32_t targetSdkVersion);
+ int32_t targetSdkVersion, const std::string& fromCodePath);
binder::Status dexopt(const std::string& apkPath, int32_t uid,
const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
@@ -130,7 +130,6 @@
const std::string& profileName);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status markBootComplete(const std::string& instructionSet);
binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index 287f2d9..c6583a1 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -15,6 +15,10 @@
{
"name": "installd_utils_test"
},
+ // AdoptableHostTest moves packages, part of which is handled by installd
+ {
+ "name": "AdoptableHostTest"
+ },
{
"name": "CtsUsesLibraryHostTestCases"
},
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 891b26d..80d9703 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -52,7 +52,7 @@
void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
@utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
- @utf8InCpp String seInfo, int targetSdkVersion);
+ @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
@utf8InCpp String instructionSet, int dexoptNeeded,
@@ -81,7 +81,6 @@
void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
void rmPackageDir(@utf8InCpp String packageDir);
- void markBootComplete(@utf8InCpp String instructionSet);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
long cacheReservedBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f95e445..70bbc33 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -299,9 +299,10 @@
// Namespace for Android Runtime flags applied during boot time.
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
// Feature flag name for running the JIT in Zygote experiment, b/119800099.
-static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
-// Location of the apex image.
-static const char* kApexImage = "/system/framework/apex.art";
+static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+ "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
// Phenotype property name for enabling profiling the boot class path.
static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -405,9 +406,9 @@
GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
std::string boot_image;
- std::string use_apex_image =
+ std::string use_jitzygote_image =
server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
- ENABLE_APEX_IMAGE,
+ ENABLE_JITZYGOTE_IMAGE,
/*default_value=*/ "");
std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
@@ -417,10 +418,10 @@
PROFILE_BOOT_CLASS_PATH,
/*default_value=*/ profile_boot_class_path);
- if (use_apex_image == "true" || profile_boot_class_path == "true") {
- boot_image = StringPrintf("-Ximage:%s", kApexImage);
+ if (use_jitzygote_image == "true" || profile_boot_class_path == "true") {
+ boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
} else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
}
// clang FORTIFY doesn't let us use strlen in constant array bounds, so we
@@ -513,7 +514,8 @@
AddArg(instruction_set_variant_arg);
AddArg(instruction_set_features_arg);
- AddRuntimeArg(boot_image);
+ AddArg(boot_image);
+
AddRuntimeArg(bootclasspath);
AddRuntimeArg(dex2oat_Xms_arg);
AddRuntimeArg(dex2oat_Xmx_arg);
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 3ff9d11..7c989f6 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -73,7 +73,7 @@
for (const apex::ApexFile& apex_file : active_packages) {
const std::string& package_path = apex_file.GetPath();
base::Result<void> status = apex::deactivatePackage(package_path);
- if (!status) {
+ if (!status.ok()) {
LOG(ERROR) << "Failed to deactivate " << package_path << ": "
<< status.error();
}
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index e61eb6e..d236f76 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -104,12 +104,12 @@
EXPECT_EQ(-1, validate_apk_path(badint2))
<< badint2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk";
+ // Should not have more than two sub directories
+ const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
@@ -120,6 +120,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopDir) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
@@ -127,6 +128,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopFile) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
@@ -134,6 +136,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
@@ -141,6 +144,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
@@ -148,6 +152,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
@@ -164,6 +169,10 @@
EXPECT_EQ(0, validate_apk_path(path2))
<< path2 << " should be allowed as a valid path";
+ const char *path3 = TEST_APP_DIR "random/example.com/example.apk";
+ EXPECT_EQ(0, validate_apk_path(path3))
+ << path3 << " should be allowed as a valid path";
+
const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
EXPECT_EQ(-1, validate_apk_path(badpriv1))
<< badpriv1 << " should be rejected as a invalid path";
@@ -172,16 +181,16 @@
EXPECT_EQ(-1, validate_apk_path(badpriv2))
<< badpriv2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk";
+ // Only one or two subdir should be allowed.
+ const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
- const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk";
+ const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path5))
<< bad_path5 << " should be rejected as a invalid path";
}
@@ -229,10 +238,16 @@
<< badasec6 << " should be rejected as a invalid path";
}
-TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
- const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
- EXPECT_EQ(-1, validate_apk_path(badasec7))
- << badasec7 << " should be rejected as a invalid path";
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) {
+ const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk";
+ EXPECT_EQ(0, validate_apk_path(badasec7))
+ << badasec7 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) {
+ const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec8))
+ << badasec8 << " should be rejcted as an invalid path";
}
TEST_F(UtilsTest, CheckSystemApp_Dir1) {
@@ -511,8 +526,8 @@
EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
@@ -527,8 +542,10 @@
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file"));
}
TEST_F(UtilsTest, MatchExtension_Valid) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 6012822..939cf90 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -954,11 +954,11 @@
}
int validate_apk_path(const char* path) {
- return validate_apk_path_internal(path, 1 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 2 /* maxSubdirs */);
}
int validate_apk_path_subdirs(const char* path) {
- return validate_apk_path_internal(path, 3 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 4 /* maxSubdirs */);
}
int ensure_config_user_dirs(userid_t userid) {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index ae74ac3..abe6436 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -423,11 +423,12 @@
void ServiceManager::handleClientCallbacks() {
for (const auto& [name, service] : mNameToService) {
- handleServiceClientCallback(name);
+ handleServiceClientCallback(name, true);
}
}
-ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName,
+ bool isCalledOnInterval) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
return -1;
@@ -451,14 +452,17 @@
service.guaranteeClient = false;
}
- if (hasClients && !service.hasClients) {
- // client was retrieved in some other way
- sendClientCallbackNotifications(serviceName, true);
- }
+ // only send notifications if this was called via the interval checking workflow
+ if (isCalledOnInterval) {
+ if (hasClients && !service.hasClients) {
+ // client was retrieved in some other way
+ sendClientCallbackNotifications(serviceName, true);
+ }
- // there are no more clients, but the callback has not been called yet
- if (!hasClients && service.hasClients) {
- sendClientCallbackNotifications(serviceName, false);
+ // there are no more clients, but the callback has not been called yet
+ if (!hasClients && service.hasClients) {
+ sendClientCallbackNotifications(serviceName, false);
+ }
}
return count;
@@ -518,7 +522,7 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- int clients = handleServiceClientCallback(name);
+ int clients = handleServiceClientCallback(name, false);
// clients < 0: feature not implemented or other error. Assume clients.
// Otherwise:
@@ -527,7 +531,7 @@
// So, if clients > 2, then at least one other service on the system must hold a refcount.
if (clients < 0 || clients > 2) {
// client callbacks are either disabled or there are other clients
- LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+ LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 77f5250..a2fc5a8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -75,7 +75,7 @@
void removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found);
- ssize_t handleServiceClientCallback(const std::string& serviceName);
+ ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
// Also updates mHasClients (of what the last callback was)
void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
// removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 571a5ca..727a4af 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -81,8 +81,8 @@
enum {
/** If this bit is set in AndroidBitmapInfo.flags, the Bitmap uses the
- * HARDWARE Config, and its AHardwareBuffer can be retrieved via
- * AndroidBitmap_getHardwareBuffer.
+ * HARDWARE Config, and its {@link AHardwareBuffer} can be retrieved via
+ * {@link AndroidBitmap_getHardwareBuffer}.
*/
ANDROID_BITMAP_FLAGS_IS_HARDWARE = 1 << 31,
};
@@ -97,15 +97,21 @@
uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
- /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK
- * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. One bit is used
- * to encode whether the Bitmap uses the HARDWARE Config. Use
- * ANDROID_BITMAP_FLAGS_IS_HARDWARE to know.*/
+ /** Bitfield containing information about the bitmap.
+ *
+ * <p>Two bits are used to encode alpha. Use {@link ANDROID_BITMAP_FLAGS_ALPHA_MASK}
+ * and {@link ANDROID_BITMAP_FLAGS_ALPHA_SHIFT} to retrieve them.</p>
+ *
+ * <p>One bit is used to encode whether the Bitmap uses the HARDWARE Config. Use
+ * {@link ANDROID_BITMAP_FLAGS_IS_HARDWARE} to know.</p>
+ *
+ * <p>These flags were introduced in API level 30.</p>
+ */
uint32_t flags;
} AndroidBitmapInfo;
/**
- * Given a java bitmap object, fill out the AndroidBitmapInfo struct for it.
+ * Given a java bitmap object, fill out the {@link AndroidBitmapInfo} struct for it.
* If the call fails, the info parameter will be ignored.
*/
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
@@ -114,10 +120,10 @@
#if __ANDROID_API__ >= 30
/**
- * Given a java bitmap object, return its ADataSpace.
+ * Given a java bitmap object, return its {@link ADataSpace}.
*
- * Note that ADataSpace only exposes a few values. This may return
- * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no
+ * Note that {@link ADataSpace} only exposes a few values. This may return
+ * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
* corresponding ADataSpace.
*/
int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30);
@@ -200,17 +206,17 @@
* @param dataspace {@link ADataSpace} describing the color space of the
* pixels.
* @param pixels Pointer to pixels to compress.
- * @param format (@link AndroidBitmapCompressFormat} to compress to.
+ * @param format {@link AndroidBitmapCompressFormat} to compress to.
* @param quality Hint to the compressor, 0-100. The value is interpreted
* differently depending on the
* {@link AndroidBitmapCompressFormat}.
* @param userContext User-defined data which will be passed to the supplied
* {@link AndroidBitmap_CompressWriteFunc} each time it is
* called. May be null.
- * @parm fn Function that writes the compressed data. Will be called each time
- * the compressor has compressed more data that is ready to be
- * written. May be called more than once for each call to this method.
- * May not be null.
+ * @param fn Function that writes the compressed data. Will be called each time
+ * the compressor has compressed more data that is ready to be
+ * written. May be called more than once for each call to this method.
+ * May not be null.
* @return AndroidBitmap functions result code.
*/
int AndroidBitmap_compress(const AndroidBitmapInfo* info,
@@ -229,11 +235,11 @@
*
* @param bitmap Handle to an android.graphics.Bitmap.
* @param outBuffer On success, is set to a pointer to the
- * AHardwareBuffer associated with bitmap. This acquires
+ * {@link AHardwareBuffer} associated with bitmap. This acquires
* a reference on the buffer, and the client must call
- * AHardwareBuffer_release when finished with it.
+ * {@link AHardwareBuffer_release} when finished with it.
* @return AndroidBitmap functions result code.
- * ANDROID_BITMAP_RESULT_BAD_PARAMETER if bitmap is not a
+ * {@link ANDROID_BITMAP_RESULT_BAD_PARAMETER} if bitmap is not a
* HARDWARE Bitmap.
*/
int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject bitmap,
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index da324b8..3a87da0 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -15,19 +15,40 @@
*/
/**
- * @addtogroup ImageDecoder
+ * @defgroup ImageDecoder
+ *
+ * Functions for converting encoded images into RGBA pixels.
+ *
+ * Similar to the Java counterpart android.graphics.ImageDecoder, it can be used
+ * to decode images in the following formats:
+ * - JPEG
+ * - PNG
+ * - GIF
+ * - WebP
+ * - BMP
+ * - ICO
+ * - WBMP
+ * - HEIF
+ * - Digital negatives (via the DNG SDK)
+ * <p>It has similar options for scaling, cropping, and choosing the output format.
+ * Unlike the Java API, which can create an android.graphics.Bitmap or
+ * android.graphics.drawable.Drawable object, AImageDecoder decodes directly
+ * into memory provided by the client. For more information, see the
+ * <a href="https://developer.android.com/ndk/guides/image-decoder">Image decoder</a>
+ * developer guide.
* @{
*/
/**
- * @file imageDecoder.h
+ * @file imagedecoder.h
+ * @brief API for decoding images.
*/
#ifndef ANDROID_IMAGE_DECODER_H
#define ANDROID_IMAGE_DECODER_H
#include "bitmap.h"
-
+#include <android/rect.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -35,36 +56,57 @@
#endif
struct AAsset;
-struct ARect;
#if __ANDROID_API__ >= 30
-/** AImageDecoder functions result code. */
+/**
+ * {@link AImageDecoder} functions result code. Many functions will return one of these
+ * 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.
+ */
enum {
- // Decoding was successful and complete.
+ /**
+ * Decoding was successful and complete.
+ */
ANDROID_IMAGE_DECODER_SUCCESS = 0,
- // The input was incomplete. In decodeImage, this means a partial
- // image was decoded. Undecoded lines are all zeroes.
- // In AImageDecoder_create*, no AImageDecoder was created.
+ /**
+ * The input is incomplete.
+ */
ANDROID_IMAGE_DECODER_INCOMPLETE = -1,
- // The input contained an error after decoding some lines. Similar to
- // INCOMPLETE, above.
+ /**
+ * The input contained an error after decoding some lines.
+ */
ANDROID_IMAGE_DECODER_ERROR = -2,
- // Could not convert, e.g. attempting to decode an image with
- // alpha to an opaque format.
+ /**
+ * Could not convert. For example, attempting to decode an image with
+ * alpha to an opaque format.
+ */
ANDROID_IMAGE_DECODER_INVALID_CONVERSION = -3,
- // The scale is invalid. It may have overflowed, or it may be incompatible
- // with the current alpha setting.
+ /**
+ * The scale is invalid. It may have overflowed, or it may be incompatible
+ * with the current alpha setting.
+ */
ANDROID_IMAGE_DECODER_INVALID_SCALE = -4,
- // Some other parameter was bad (e.g. pixels)
+ /**
+ * Some other parameter is invalid.
+ */
ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5,
- // Input was invalid i.e. broken before decoding any pixels.
+ /**
+ * Input was invalid before decoding any pixels.
+ */
ANDROID_IMAGE_DECODER_INVALID_INPUT = -6,
- // A seek was required, and failed.
+ /**
+ * A seek was required and it failed.
+ */
ANDROID_IMAGE_DECODER_SEEK_ERROR = -7,
- // Some other error (e.g. OOM)
+ /**
+ * Some other error. For example, an internal allocation failed.
+ */
ANDROID_IMAGE_DECODER_INTERNAL_ERROR = -8,
- // We did not recognize the format
+ /**
+ * AImageDecoder did not recognize the format.
+ */
ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9
};
@@ -77,35 +119,71 @@
* - {@link AImageDecoder_createFromAAsset}
* - {@link AImageDecoder_createFromFd}
* - {@link AImageDecoder_createFromBuffer}
+ *
+ * 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} objects are NOT thread-safe, and should not be shared across
+ * threads.
*/
typedef struct AImageDecoder AImageDecoder;
/**
- * Create a new AImageDecoder from an AAsset.
+ * Create a new {@link AImageDecoder} from an {@link AAsset}.
*
* @param asset {@link AAsset} containing encoded image data. Client is still
- * responsible for calling {@link AAsset_close} on it.
+ * responsible for calling {@link AAsset_close} on it, which may be
+ * done after deleting the returned {@link AImageDecoder}.
* @param outDecoder On success (i.e. return value is
* {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
* a newly created {@link AImageDecoder}. Caller is
* responsible for calling {@link AImageDecoder_delete} on it.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
- * indicating reason for the failure.
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The asset was truncated before
+ * reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is
+ * null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ * header.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ * failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ * supported.
*/
-int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+ __INTRODUCED_IN(30);
/**
- * Create a new AImageDecoder from a file descriptor.
+ * Create a new {@link AImageDecoder} from a file descriptor.
*
* @param fd Seekable, readable, open file descriptor for encoded data.
* Client is still responsible for closing it, which may be done
- * *after* deleting the returned AImageDecoder.
+ * after deleting the returned {@link AImageDecoder}.
* @param outDecoder On success (i.e. return value is
* {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
* a newly created {@link AImageDecoder}. Caller is
* responsible for calling {@link AImageDecoder_delete} on it.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
- * indicating reason for the failure.
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The file was truncated before
+ * reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} is
+ * null, or |fd| does not represent a valid, seekable file descriptor.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ * header.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The descriptor failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ * failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ * supported.
*/
int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
@@ -113,14 +191,26 @@
* Create a new AImageDecoder from a buffer.
*
* @param buffer Pointer to encoded data. Must be valid for the entire time
- * the AImageDecoder is used.
+ * the {@link AImageDecoder} is used.
* @param length Byte length of buffer.
* @param outDecoder On success (i.e. return value is
* {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
* a newly created {@link AImageDecoder}. Caller is
* responsible for calling {@link AImageDecoder_delete} on it.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
- * indicating reason for the failure.
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The encoded image was truncated before
+ * reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is
+ * invalid.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ * header.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ * failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ * supported.
*/
int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
AImageDecoder** outDecoder) __INTRODUCED_IN(30);
@@ -134,11 +224,18 @@
* Choose the desired output format.
*
* @param format {@link AndroidBitmapFormat} to use for the output.
- * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible
- * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}
- * otherwise. In the latter case, the AImageDecoder uses the
- * format it was already planning to use (either its default
- * or a previously successful setting from this function).
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure. On failure, the
+ * {@link AImageDecoder} uses the format it was already planning
+ * to use (either its default or a previously successful setting
+ * from this function).
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ * {@link AImageDecoder} is null or |format| does not correspond to an
+ * {@link AndroidBitmapFormat}.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
+ * {@link AndroidBitmapFormat} is incompatible with the image.
*/
int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
int32_t format) __INTRODUCED_IN(30);
@@ -146,23 +243,29 @@
/**
* Specify whether the output's pixels should be unpremultiplied.
*
- * By default, the decoder will premultiply the pixels, if they have alpha. Pass
- * false to this method to leave them unpremultiplied. This has no effect on an
+ * By default, {@link AImageDecoder_decodeImage} will premultiply the pixels, if they have alpha.
+ * Pass true to this method to leave them unpremultiplied. This has no effect on an
* opaque image.
*
- * @param required Pass true to leave the pixels unpremultiplied.
- * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
- * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion
- * is not possible
- * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters
+ * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: Unpremultiplied is not
+ * possible due to an existing scale set by
+ * {@link AImageDecoder_setTargetSize}.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ * {@link AImageDecoder} is null.
*/
-int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __INTRODUCED_IN(30);
+int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*,
+ bool unpremultipliedRequired) __INTRODUCED_IN(30);
/**
* Choose the dataspace for the output.
*
- * Not supported for {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
- * an ADataSpace.
+ * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
+ * an {@link ADataSpace}.
*
* @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
* specifies how to interpret the colors. By default,
@@ -170,10 +273,13 @@
* {@link AImageDecoderHeaderInfo_getDataSpace}. If this
* parameter is set to a different ADataSpace, AImageDecoder
* will transform the output into the specified ADataSpace.
- * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
- * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null
- * AImageDecoder or an integer that does not correspond to an
- * ADataSpace value.
+ * @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
+ * {@link AImageDecoder} is null or |dataspace| does not correspond to an
+ * {@link ADataSpace} value.
*/
int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
@@ -191,10 +297,16 @@
* {@link AImageDecoder_getMinimumStride}, which will now return
* a value based on this width.
* @param height Height of the output (prior to cropping).
- * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
- * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder
- * pointer is null, width or height is <= 0, or any existing crop is
- * not contained by the image dimensions.
+ * @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
+ * {@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
+ * {@link AImageDecoder_setUnpremultipliedRequired}(true).
*/
int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
@@ -213,10 +325,15 @@
* 1/2 of the original dimensions, with 1/4 the number of
* pixels.
* @param width Out parameter for the width sampled by sampleSize, and rounded
- * direction that the decoder can do most efficiently.
+ * in the direction that the decoder can do most efficiently.
* @param height Out parameter for the height sampled by sampleSize, and rounded
- * direction that the decoder can do most efficiently.
- * @return ANDROID_IMAGE_DECODER result code.
+ * in the direction that the decoder can do most efficiently.
+ * @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
+ * {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1.
*/
int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
int32_t* width, int32_t* height) __INTRODUCED_IN(30);
@@ -234,18 +351,24 @@
* value based on the width of the crop. An empty ARect -
* specifically { 0, 0, 0, 0 } - may be used to remove the cropping
* behavior. Any other empty or unsorted ARects will result in
- * returning ANDROID_IMAGE_DECODER_BAD_PARAMETER.
- * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
- * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder
- * pointer is null or the crop is not contained by the image
- * dimensions.
+ * returning {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}.
+ * @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
+ * {@link AImageDecoder} is null or the crop is not contained by the
+ * (possibly scaled) image dimensions.
*/
int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
-/**
- * Opaque handle for reading header info.
- */
struct AImageDecoderHeaderInfo;
+/**
+ * Opaque handle for representing information about the encoded image. Retrieved
+ * using {@link AImageDecoder_getHeaderInfo} and passed to methods like
+ * {@link AImageDecoderHeaderInfo_getWidth} and
+ * {@link AImageDecoderHeaderInfo_getHeight}.
+ */
typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo;
/**
@@ -258,12 +381,18 @@
const AImageDecoder*) __INTRODUCED_IN(30);
/**
- * Report the native width of the encoded image.
+ * Report the native width of the encoded image. This is also the logical
+ * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
+ * used to choose a different size or {@link AImageDecoder_setCrop} is used to
+ * set a crop rect.
*/
int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
- * Report the native height of the encoded image.
+ * Report the native height of the encoded image. This is also the logical
+ * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
+ * used to choose a different size or {@link AImageDecoder_setCrop} is used to
+ * set a crop rect.
*/
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -277,7 +406,7 @@
/**
* Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
- * by default. AImageDecoder will try to choose one that is sensible
+ * by default. {@link AImageDecoder} will try to choose one that is sensible
* for the image and the system. Note that this does not indicate the
* encoded format of the image.
*/
@@ -285,56 +414,76 @@
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
- * Report how the AImageDecoder will handle alpha by default. If the image
+ * Report how the {@link AImageDecoder} will handle alpha by default. If the image
* contains no alpha (according to its header), this will return
* {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
- * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}.
- *
- * For animated images only the opacity of the first frame is reported.
+ * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
+ * {@link AImageDecoder_decodeImage} will premultiply pixels by default.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
* Report the dataspace the AImageDecoder will decode to by default.
- * AImageDecoder will try to choose one that is sensible for the
- * image and the system. Note that this may not exactly match the ICC
- * profile (or other color information) stored in the encoded image.
*
- * @return The {@link ADataSpace} most closely representing the way the colors
- * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not an
- * approximate ADataSpace). This specifies how to interpret the colors
+ * By default, {@link AImageDecoder_decodeImage} will not do any color
+ * conversion.
+ *
+ * @return The {@link ADataSpace} representing the way the colors
+ * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
+ * corresponding ADataSpace). This specifies how to interpret the colors
* in the decoded image, unless {@link AImageDecoder_setDataSpace} is
* called to decode to a different ADataSpace.
*
* Note that ADataSpace only exposes a few values. This may return
- * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no
- * corresponding ADataSpace.
+ * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have
+ * no corresponding {@link ADataSpace}.
*/
int32_t AImageDecoderHeaderInfo_getDataSpace(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
- * Return the minimum stride that can be used, taking the specified
- * (or default) (possibly scaled) width, crop rect and
- * {@link AndroidBitmapFormat} into account.
+ * Return the minimum stride that can be used in
+ * {@link AImageDecoder_decodeImage).
+ *
+ * This stride provides no padding, meaning it will be exactly equal to the
+ * width times the number of bytes per pixel for the {@link AndroidBitmapFormat}
+ * being used.
+ *
+ * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
+ * cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
*/
size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
/**
- * Decode the image into pixels, using the settings of the AImageDecoder.
+ * Decode the image into pixels, using the settings of the {@link AImageDecoder}.
*
* @param decoder Opaque object representing the decoder.
* @param pixels On success, will be filled with the result
- * of the decode. Must be large enough to fit |size| bytes.
+ * of the decode. Must be large enough to hold |size| bytes.
* @param stride Width in bytes of a single row. Must be at least
- * {@link AImageDecoder_getMinimumStride}.
+ * {@link AImageDecoder_getMinimumStride} and a multiple of the
+ * bytes per pixel of the {@link AndroidBitmapFormat}.
* @param size Size of the pixel buffer in bytes. Must be at least
* stride * (height - 1) +
- * {@link AImageDecoder_getMinimumStride}. Must also be a multiple
- * of the bytes per pixel of the {@link AndroidBitmapFormat}.
- * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code
- * from the same enum describing the failure.
+ * {@link AImageDecoder_getMinimumStride}.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The image was truncated. A
+ * partial image was decoded, and undecoded lines have been initialized to all
+ * zeroes.
+ * - {@link ANDROID_IMAGE_DECODER_ERROR}: The image contained an error. A
+ * partial image was decoded, and undecoded lines have been initialized to all
+ * zeroes.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} or
+ * |pixels| is null, the stride is not large enough or not pixel aligned, or
+ * |size| is not large enough.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset or file descriptor
+ * failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ * failure to allocate memory.
*/
int AImageDecoder_decodeImage(AImageDecoder* decoder,
void* pixels, size_t stride,
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 12c00ad..e63ac4b 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -15,6 +15,9 @@
*/
/**
+ * Structures and functions to receive and process sensor events in
+ * native code.
+ *
* @addtogroup Sensor
* @{
*/
@@ -42,12 +45,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-/**
- * Structures and functions to receive and process sensor events in
- * native code.
- *
- */
-
#include <android/looper.h>
#include <stdbool.h>
diff --git a/include/input/Input.h b/include/input/Input.h
index f871847..cf0814c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,6 +31,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
+#include <array>
#include <limits>
#include <queue>
@@ -258,6 +259,11 @@
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
/*
* Pointer coordinate data.
*/
@@ -348,22 +354,25 @@
inline int32_t getDeviceId() const { return mDeviceId; }
- inline int32_t getSource() const { return mSource; }
+ inline uint32_t getSource() const { return mSource; }
- inline void setSource(int32_t source) { mSource = source; }
+ inline void setSource(uint32_t source) { mSource = source; }
inline int32_t getDisplayId() const { return mDisplayId; }
inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+ inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
protected:
- void initialize(int32_t deviceId, int32_t source, int32_t displayId);
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
int32_t mDeviceId;
- int32_t mSource;
+ uint32_t mSource;
int32_t mDisplayId;
+ std::array<uint8_t, 32> mHmac;
};
/*
@@ -396,18 +405,10 @@
static const char* getLabel(int32_t keyCode);
static int32_t getKeyCodeFromLabel(const char* label);
- void initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime);
void initialize(const KeyEvent& from);
protected:
@@ -463,6 +464,10 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
+ inline float getXScale() const { return mXScale; }
+
+ inline float getYScale() const { return mYScale; }
+
inline float getXOffset() const { return mXOffset; }
inline float getYOffset() const { return mYOffset; }
@@ -624,9 +629,10 @@
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xOffset,
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, float xScale, float yScale, float xOffset,
float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
@@ -651,7 +657,7 @@
status_t writeToParcel(Parcel* parcel) const;
#endif
- static bool isTouchEvent(int32_t source, int32_t action);
+ static bool isTouchEvent(uint32_t source, int32_t action);
inline bool isTouchEvent() const {
return isTouchEvent(mSource, mAction);
}
@@ -676,6 +682,8 @@
int32_t mMetaState;
int32_t mButtonState;
MotionClassification mClassification;
+ float mXScale;
+ float mYScale;
float mXOffset;
float mYOffset;
float mXPrecision;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ae47438..06fd3bb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -76,6 +76,9 @@
} header;
// Body *must* be 8 byte aligned.
+ // For keys and motions, rely on the fact that std::array takes up exactly as much space
+ // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
+ static_assert(sizeof(std::array<uint8_t, 32>) == 32);
union Body {
struct Key {
uint32_t seq;
@@ -84,6 +87,7 @@
int32_t deviceId;
int32_t source;
int32_t displayId;
+ std::array<uint8_t, 32> hmac;
int32_t action;
int32_t flags;
int32_t keyCode;
@@ -103,6 +107,7 @@
int32_t deviceId;
int32_t source;
int32_t displayId;
+ std::array<uint8_t, 32> hmac;
int32_t action;
int32_t actionButton;
int32_t flags;
@@ -112,6 +117,8 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
+ float xScale;
+ float yScale;
float xOffset;
float yOffset;
float xPrecision;
@@ -269,19 +276,10 @@
* Returns BAD_VALUE if seq is 0.
* Other errors probably indicate that the channel is broken.
*/
- status_t publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
+ status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
/* Publishes a motion event to the input channel.
*
@@ -292,9 +290,10 @@
* Other errors probably indicate that the channel is broken.
*/
status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
- int32_t action, int32_t actionButton, int32_t flags,
- int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xOffset, float yOffset,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification,
+ float xScale, float yScale, float xOffset, float yOffset,
float xPrecision, float yPrecision, float xCursorPosition,
float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h
new file mode 120000
index 0000000..c2047c0
--- /dev/null
+++ b/include/ui/FatVector.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
index 9cf0143..8ac044c 100644
--- a/libs/adbd_auth/Android.bp
+++ b/libs/adbd_auth/Android.bp
@@ -20,11 +20,14 @@
"-Wthread-safety",
"-Werror",
],
+ stl: "libc++_static",
+
srcs: ["adbd_auth.cpp"],
export_include_dirs: ["include"],
version_script: "libadbd_auth.map.txt",
stubs: {
+ versions: ["1"],
symbol_file: "libadbd_auth.map.txt",
},
@@ -36,7 +39,7 @@
}
},
- shared_libs: [
+ static_libs: [
"libbase",
"libcutils",
"liblog",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 079dd82..bc541f4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -73,7 +73,6 @@
// or dessert updates. Instead, apex users should use libbinder_ndk.
apex_available: [
"//apex_available:platform",
- "com.android.vndk.current",
// TODO(b/139016109) remove these three
"com.android.media.swcodec",
"test_com.android.media.swcodec",
@@ -139,7 +138,6 @@
"liblog",
"libcutils",
"libutils",
- "libbinderthreadstate",
],
header_libs: [
@@ -172,7 +170,6 @@
name: "libbinder_aidl_test_stub",
local_include_dir: "aidl",
srcs: [":libbinder_aidl"],
- visibility: [":__subpackages__"],
vendor_available: true,
backend: {
java: {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 238c9dc..f16c39c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -435,7 +435,8 @@
Vector<Obituary>* obits = mObituaries;
if(obits != nullptr) {
if (!obits->isEmpty()) {
- ALOGI("onLastStrongRef automatically unlinking death recipients");
+ ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
+ mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
}
if (ipc) ipc->clearDeathNotification(mHandle, this);
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 824b56b..8cf6097 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/Debug.h>
#include <cutils/atomic.h>
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
similarity index 100%
rename from libs/binder/include/binder/BufferedTextOutput.h
rename to libs/binder/BufferedTextOutput.h
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 4981d7a..4dcd07a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "IPCThreadState"
#include <binder/IPCThreadState.h>
-#include <binderthreadstate/IPCThreadStateBase.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
@@ -803,6 +802,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
mStrictModePolicy(0),
@@ -813,7 +813,6 @@
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
- mIPCThreadStateBase = IPCThreadStateBase::self();
}
IPCThreadState::~IPCThreadState()
@@ -1163,9 +1162,6 @@
"Not enough command data for brTRANSACTION");
if (result != NO_ERROR) break;
- //Record the fact that we're in a binder call.
- mIPCThreadStateBase->pushCurrentState(
- IPCThreadStateBase::CallState::BINDER);
Parcel buffer;
buffer.ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
@@ -1173,6 +1169,9 @@
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
+ const void* origServingStackPointer = mServingStackPointer;
+ mServingStackPointer = &origServingStackPointer; // anything on the stack
+
const pid_t origPid = mCallingPid;
const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
@@ -1223,7 +1222,6 @@
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
- mIPCThreadStateBase->popCurrentState();
//ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
// mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
@@ -1235,6 +1233,7 @@
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
+ mServingStackPointer = origServingStackPointer;
mCallingPid = origPid;
mCallingSid = origSid;
mCallingUid = origUid;
@@ -1290,8 +1289,8 @@
return result;
}
-bool IPCThreadState::isServingCall() const {
- return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER;
+const void* IPCThreadState::getServingStackPointer() const {
+ return mServingStackPointer;
}
void IPCThreadState::threadDestructor(void *st)
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 5ca9156..9888b59 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -85,27 +85,38 @@
sp<AidlServiceManager> mTheRealServiceManager;
};
+static std::once_flag gSmOnce;
+static sp<IServiceManager> gDefaultServiceManager;
+
sp<IServiceManager> defaultServiceManager()
{
- static Mutex gDefaultServiceManagerLock;
- static sp<IServiceManager> gDefaultServiceManager;
-
- if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;
-
- {
- AutoMutex _l(gDefaultServiceManagerLock);
- while (gDefaultServiceManager == nullptr) {
- gDefaultServiceManager = new ServiceManagerShim(
- interface_cast<AidlServiceManager>(
- ProcessState::self()->getContextObject(nullptr)));
- if (gDefaultServiceManager == nullptr)
+ std::call_once(gSmOnce, []() {
+ sp<AidlServiceManager> sm = nullptr;
+ while (sm == nullptr) {
+ sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
+ if (sm == nullptr) {
sleep(1);
+ }
}
- }
+
+ gDefaultServiceManager = new ServiceManagerShim(sm);
+ });
return gDefaultServiceManager;
}
+void setDefaultServiceManager(const sp<IServiceManager>& sm) {
+ bool called = false;
+ std::call_once(gSmOnce, [&]() {
+ gDefaultServiceManager = sm;
+ called = true;
+ });
+
+ if (!called) {
+ LOG_ALWAYS_FATAL("setDefaultServiceManager() called after defaultServiceManager().");
+ }
+}
+
#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
// IPermissionController is not accessible to vendors
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index dc9482c..f064bd7 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -53,14 +53,13 @@
struct Service {
sp<IBinder> service;
- std::string name;
bool allowIsolated;
int dumpFlags;
};
/**
- * Number of services that have been registered.
+ * Map of registered names and services
*/
- std::vector<Service> mRegisteredServices;
+ std::map<std::string, Service> mRegisteredServices;
};
bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
@@ -68,20 +67,24 @@
auto manager = interface_cast<AidlServiceManager>(
ProcessState::self()->getContextObject(nullptr));
- ALOGI("Registering service %s", name.c_str());
+ bool reRegister = mRegisteredServices.count(name) > 0;
+ std::string regStr = (reRegister) ? "Re-registering" : "Registering";
+ ALOGI("%s service %s", regStr.c_str(), name.c_str());
if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
ALOGE("Failed to register service %s", name.c_str());
return false;
}
- if (!manager->registerClientCallback(name, service, this).isOk())
- {
- ALOGE("Failed to add client callback for service %s", name.c_str());
- return false;
+ if (!manager->registerClientCallback(name, service, this).isOk()) {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
}
- mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+ if (!reRegister) {
+ // Only add this when a service is added for the first time, as it is not removed
+ mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+ }
return true;
}
@@ -119,10 +122,11 @@
for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
auto& entry = (*unRegisterIt);
- bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+ bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
+
if (!success) {
- ALOGI("Failed to unregister service %s", entry.name.c_str());
+ ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
}
}
@@ -137,7 +141,8 @@
auto& entry = (*reRegisterIt);
// re-register entry
- if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated,
+ entry.second.dumpFlags)) {
// Must restart. Otherwise, clients will never be able to get a hold of this service.
ALOGE("Bad state: could not re-register services");
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 994e3b9..9f8d752 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -507,7 +507,7 @@
}
}
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 37c0d77..bdc2e40 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -60,14 +60,14 @@
: mIsMain(isMain)
{
}
-
+
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
-
+
const bool mIsMain;
};
@@ -296,7 +296,7 @@
void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
{
AutoMutex _l(mLock);
-
+
handle_entry* e = lookupHandleLocked(handle);
// This handle may have already been replaced with a new BpBinder
@@ -387,7 +387,7 @@
{
// TODO(b/139016109): enforce in build system
-#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__)
+#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
@@ -418,5 +418,5 @@
}
mDriverFD = -1;
}
-
+
} // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index bd40536..7a77f6d 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -19,7 +19,7 @@
#include "Static.h"
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/IPCThreadState.h>
#include <utils/Log.h>
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index b3afd81..9aa7651 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,7 +19,16 @@
"name": "binderStabilityTest"
},
{
+ "name": "libbinder_ndk_unit_test"
+ },
+ {
"name": "CtsNdkBinderTestCases"
+ },
+ {
+ "name": "aidl_lazy_test"
+ },
+ {
+ "name": "libbinderthreadstateutils_test"
}
]
}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a7292..618f88c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -87,4 +87,7 @@
* package.
*/
@utf8InCpp String getModuleMetadataPackageName();
+
+ /* Returns the names of all packages. */
+ @utf8InCpp String[] getAllPackages();
}
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
index 521d36f..d2b4d52 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/fuzzer/Android.bp
@@ -16,7 +16,6 @@
],
static_libs: [
"libbase",
- "libbinderthreadstate",
"libcgrouprc",
"libcgrouprc_format",
"libcutils",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index ff9244e..4818889 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -29,8 +29,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class IPCThreadStateBase;
-
class IPCThreadState
{
public:
@@ -113,31 +111,12 @@
// Service manager registration
void setTheContextObject(sp<BBinder> obj);
- // Is this thread currently serving a binder call. This method
- // returns true if while traversing backwards from the function call
- // stack for this thread, we encounter a function serving a binder
- // call before encountering a hwbinder call / hitting the end of the
- // call stack.
- // Eg: If thread T1 went through the following call pattern
- // 1) T1 receives and executes hwbinder call H1.
- // 2) While handling H1, T1 makes binder call B1.
- // 3) The handler of B1, calls into T1 with a callback B2.
- // If isServingCall() is called during H1 before 3), this method
- // will return false, else true.
+ // WARNING: DO NOT USE THIS API
//
- // ----
- // | B2 | ---> While callback B2 is being handled, during 3).
- // ----
- // | H1 | ---> While H1 is being handled.
- // ----
- // Fig: Thread Call stack while handling B2
- //
- // This is since after 3), while traversing the thread call stack,
- // we hit a binder call before a hwbinder call / end of stack. This
- // method may be typically used to determine whether to use
- // hardware::IPCThreadState methods or IPCThreadState methods to
- // infer information about thread state.
- bool isServingCall() const;
+ // Returns a pointer to the stack from the last time a transaction
+ // was initiated by the kernel. Used to compare when making nested
+ // calls between multiple different transports.
+ const void* getServingStackPointer() const;
// The work source represents the UID of the process we should attribute the transaction
// to. We use -1 to specify that the work source was not set using #setWorkSource.
@@ -181,6 +160,7 @@
Parcel mIn;
Parcel mOut;
status_t mLastError;
+ const void* mServingStackPointer;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
@@ -191,7 +171,6 @@
bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
- IPCThreadStateBase *mIPCThreadStateBase;
ProcessState::CallRestriction mCallRestriction;
};
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2c43263..1d520c1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -100,6 +100,14 @@
sp<IServiceManager> defaultServiceManager();
+/**
+ * Directly set the default service manager. Only used for testing.
+ * Note that the caller is responsible for caling this method
+ * *before* any call to defaultServiceManager(); if the latter is
+ * called first, setDefaultServiceManager() will abort.
+ */
+void setDefaultServiceManager(const sp<IServiceManager>& sm);
+
template<typename INTERFACE>
sp<INTERFACE> waitForService(const String16& name) {
const sp<IServiceManager> sm = defaultServiceManager();
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index b2f51d3..2894482 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,7 +81,7 @@
VINTF = 0b111111,
};
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
static constexpr Level kLocalStability = Level::VENDOR;
#else
static constexpr Level kLocalStability = Level::SYSTEM;
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index e752c45..75dcdc8 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -24,10 +24,13 @@
#include <android-base/logging.h>
#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <private/android_filesystem_config.h>
using DeathRecipient = ::android::IBinder::DeathRecipient;
using ::android::IBinder;
+using ::android::IResultReceiver;
using ::android::Parcel;
using ::android::sp;
using ::android::status_t;
@@ -158,6 +161,45 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
+ } else if (code == SHELL_COMMAND_TRANSACTION) {
+ int in = data.readFileDescriptor();
+ int out = data.readFileDescriptor();
+ int err = data.readFileDescriptor();
+
+ int argc = data.readInt32();
+ std::vector<String8> utf8Args; // owns memory of utf8s
+ std::vector<const char*> utf8Pointers; // what can be passed over NDK API
+ for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+ utf8Args.push_back(String8(data.readString16()));
+ utf8Pointers.push_back(utf8Args[i].c_str());
+ }
+
+ data.readStrongBinder(); // skip over the IShellCallback
+ sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder());
+
+ // Shell commands should only be callable by ADB.
+ uid_t uid = AIBinder_getCallingUid();
+ if (uid != AID_ROOT && uid != AID_SHELL) {
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(-1);
+ }
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ // Check that the file descriptors are valid.
+ if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) {
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(-1);
+ }
+ return STATUS_BAD_VALUE;
+ }
+
+ binder_status_t status = getClass()->handleShellCommand(
+ this, in, out, err, utf8Pointers.data(), utf8Pointers.size());
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(status);
+ }
+ return status;
} else {
return BBinder::onTransact(code, data, reply, flags);
}
@@ -266,6 +308,13 @@
clazz->onDump = onDump;
}
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+ AIBinder_handleShellCommand handleShellCommand) {
+ CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
+
+ clazz->handleShellCommand = handleShellCommand;
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho);
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5cb68c2..5779427 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -17,6 +17,7 @@
#pragma once
#include <android/binder_ibinder.h>
+#include <android/binder_shell.h>
#include "ibinder_internal.h"
#include <atomic>
@@ -115,6 +116,7 @@
// optional methods for a class
AIBinder_onDump onDump;
+ AIBinder_handleShellCommand handleShellCommand;
private:
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index 83a1048..7331ba2 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -30,6 +30,11 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
+#if __has_include(<android/binder_shell.h>)
+#include <android/binder_shell.h>
+#define HAS_BINDER_SHELL_COMMAND
+#endif //_has_include
+
#include <assert.h>
#include <memory>
@@ -108,7 +113,15 @@
/**
* Dumps information about the interface. By default, dumps nothing.
*/
- virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+ virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+ /**
+ * Process shell commands. By default, does nothing.
+ */
+ virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc);
+#endif
/**
* Interprets this binder as this underlying interface if this has stored an ICInterface in the
@@ -136,6 +149,11 @@
static inline void onDestroy(void* userData);
static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+ static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err,
+ const char** argv, uint32_t argc);
+#endif
};
};
@@ -191,6 +209,13 @@
return STATUS_OK;
}
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/,
+ const char** /*argv*/, uint32_t /*argc*/) {
+ return STATUS_OK;
+}
+#endif
+
std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
return ICInterfaceData::getInterface(binder);
}
@@ -203,9 +228,12 @@
return nullptr;
}
- // We can't know if this method is overriden by a subclass interface, so we must register
- // ourselves. The default (nothing to dump) is harmless.
+ // We can't know if these methods are overridden by a subclass interface, so we must register
+ // ourselves. The defaults are harmless.
AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
+#ifdef HAS_BINDER_SHELL_COMMAND
+ AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
+#endif
return clazz;
}
@@ -234,6 +262,15 @@
return interface->dump(fd, args, numArgs);
}
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out,
+ int err, const char** argv,
+ uint32_t argc) {
+ std::shared_ptr<ICInterface> interface = getInterface(binder);
+ return interface->handleShellCommand(in, out, err, argv, argc);
+}
+#endif
+
template <typename INTERFACE>
SpAIBinder BnCInterface<INTERFACE>::asBinder() {
std::lock_guard<std::mutex> l(mMutex);
diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h
new file mode 100644
index 0000000..17b38b0
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_shell.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+/**
+ * Function to execute a shell command.
+ *
+ * Available since API level 30.
+ *
+ * \param binder the binder executing the command
+ * \param in input file descriptor, should be flushed, ownership is not passed
+ * \param out output file descriptor, should be flushed, ownership is not passed
+ * \param err error file descriptor, should be flushed, ownership is not passed
+ * \param argv array of null-terminated strings for command (may be null if argc
+ * is 0)
+ * \param argc length of argv array
+ *
+ * \return binder_status_t result of transaction
+ */
+typedef binder_status_t (*AIBinder_handleShellCommand)(AIBinder* binder, int in, int out, int err,
+ const char** argv, uint32_t argc);
+
+/**
+ * This sets the implementation of handleShellCommand for a class.
+ *
+ * If this isn't set, nothing will be executed when handleShellCommand is called.
+ *
+ * Available since API level 30.
+ *
+ * \param handleShellCommand function to call when a shell transaction is
+ * received
+ */
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+ AIBinder_handleShellCommand handleShellCommand)
+ __INTRODUCED_IN(30);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 56d95a7..f5e8bf6 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -30,8 +30,7 @@
FLAG_PRIVATE_VENDOR = 0x10000000,
};
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || \
- (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
enum {
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
@@ -46,8 +45,7 @@
AIBinder_markVendorStability(binder);
}
-#else // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
- // !defined(__ANDROID_APEX__))
+#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
enum {
FLAG_PRIVATE_LOCAL = 0,
@@ -64,8 +62,7 @@
AIBinder_markSystemStability(binder);
}
-#endif // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
- // !defined(__ANDROID_APEX__))
+#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/**
* This interface has system<->vendor stability
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index f3158d7..7e72f22 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -110,6 +110,7 @@
AIBinder_markSystemStability; # apex
AIBinder_markVendorStability; # llndk
AIBinder_markVintfStability; # apex llndk
+ AIBinder_Class_setHandleShellCommand; # apex llndk
local:
*;
};
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh
deleted file mode 100755
index a0c49fb..0000000
--- a/libs/binder/ndk/runtests.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-if [ -z $ANDROID_BUILD_TOP ]; then
- echo "You need to source and lunch before you can use this script"
- exit 1
-fi
-
-set -ex
-
-function run_libbinder_ndk_test() {
- adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
-
- # avoid getService 1s delay for most runs, non-critical
- sleep 0.1
-
- adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
- adb shell killall libbinder_ndk_test_server
-}
-
-[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
- MODULES-IN-frameworks-native-libs-binder-ndk
-
-adb root
-adb wait-for-device
-adb sync data
-
-# very simple unit tests, tests things outside of the NDK as well
-run_libbinder_ndk_test
-
-# CTS tests (much more comprehensive, new tests should ideally go here)
-atest android.binder.cts
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index daaaa5a..cb4b20f 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -56,16 +56,18 @@
// specifically the parts which are outside of the NDK. Actual users should
// also instead use AIDL to generate these stubs. See android.binder.cts.
cc_test {
- name: "libbinder_ndk_test_client",
+ name: "libbinder_ndk_unit_test",
defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_client.cpp"],
-}
+ srcs: ["libbinder_ndk_unit_test.cpp"],
+ static_libs: [
+ "IBinderNdkUnitTest-cpp",
+ "IBinderNdkUnitTest-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
-cc_test {
- name: "libbinder_ndk_test_server",
- defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_server.cpp"],
- gtest: false,
+ // force since binderVendorDoubleLoadTest has its own
+ auto_gen_config: true,
}
cc_test {
@@ -85,7 +87,7 @@
"libbinder_ndk",
"libutils",
],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
}
aidl_interface {
@@ -95,3 +97,11 @@
"IBinderVendorDoubleLoadTest.aidl",
],
}
+
+aidl_interface {
+ name: "IBinderNdkUnitTest",
+ srcs: [
+ "IBinderNdkUnitTest.aidl",
+ "IEmpty.aidl",
+ ],
+}
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
new file mode 100644
index 0000000..6e8e463
--- /dev/null
+++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
+// because it requires libbinder_ndk implementation details or APIs not
+// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
+// over here.
+
+import IEmpty;
+
+interface IBinderNdkUnitTest {
+ void takeInterface(IEmpty test);
+ void forceFlushCommands();
+}
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/libs/binder/ndk/test/IEmpty.aidl
similarity index 60%
copy from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
copy to libs/binder/ndk/test/IEmpty.aidl
index 08483cb..95e4341 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/libs/binder/ndk/test/IEmpty.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,4 @@
* limitations under the License.
*/
-#include <compositionengine/mock/Layer.h>
-
-namespace android::compositionengine::mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace android::compositionengine::mock
+interface IEmpty { }
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
new file mode 100644
index 0000000..fd30d87
--- /dev/null
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <IBinderNdkUnitTest.h>
+#include <aidl/BnBinderNdkUnitTest.h>
+#include <aidl/BnEmpty.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder_jni.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <iface/iface.h>
+
+// warning: this is assuming that libbinder_ndk is using the same copy
+// of libbinder that we are.
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
+
+#include <sys/prctl.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+using namespace android;
+
+constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
+constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+
+class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+ ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
+ (void)empty;
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus forceFlushCommands() {
+ // warning: this is assuming that libbinder_ndk is using the same copy
+ // of libbinder that we are.
+ android::IPCThreadState::self()->flushCommands();
+ return ndk::ScopedAStatus::ok();
+ }
+ binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args,
+ uint32_t numArgs) override {
+ for (uint32_t i = 0; i < numArgs; i++) {
+ dprintf(out, "%s", args[i]);
+ }
+ fsync(out);
+ return STATUS_OK;
+ }
+};
+
+int generatedService() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// manually-written parceling class considered bad practice
+class MyFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+
+ binder_status_t die() override {
+ LOG(FATAL) << "IFoo::die called!";
+ return STATUS_UNKNOWN_ERROR;
+ }
+};
+
+int manualService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ // Strong reference to MyFoo kept by service manager.
+ binder_status_t status = (new MyFoo)->addService(instance);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// This is too slow
+// TEST(NdkBinder, GetServiceThatDoesntExist) {
+// sp<IFoo> foo = IFoo::getService("asdfghkl;");
+// EXPECT_EQ(nullptr, foo.get());
+// }
+
+TEST(NdkBinder, CheckServiceThatDoesntExist) {
+ AIBinder* binder = AServiceManager_checkService("asdfghkl;");
+ ASSERT_EQ(nullptr, binder);
+}
+
+TEST(NdkBinder, CheckServiceThatDoesExist) {
+ AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
+ EXPECT_NE(nullptr, binder);
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, DoubleNumber) {
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+ ASSERT_NE(foo, nullptr);
+
+ int32_t out;
+ EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out));
+ EXPECT_EQ(2, out);
+}
+
+void LambdaOnDeath(void* cookie) {
+ auto onDeath = static_cast<std::function<void(void)>*>(cookie);
+ (*onDeath)();
+};
+TEST(NdkBinder, DeathRecipient) {
+ using namespace std::chrono_literals;
+
+ AIBinder* binder;
+ sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathRecieved = false;
+
+ std::function<void(void)> onDeath = [&] {
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathRecieved = true;
+ deathCv.notify_one();
+ };
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
+
+ // the binder driver should return this if the service dies during the transaction
+ EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+
+ foo = nullptr;
+
+ std::unique_lock<std::mutex> lock(deathMutex);
+ EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
+ EXPECT_TRUE(deathRecieved);
+
+ AIBinder_DeathRecipient_delete(recipient);
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+}
+
+TEST(NdkBinder, RetrieveNonNdkService) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binder);
+ EXPECT_TRUE(AIBinder_isRemote(binder));
+ EXPECT_TRUE(AIBinder_isAlive(binder));
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+
+ AIBinder_decStrong(binder);
+}
+
+void OnBinderDeath(void* cookie) {
+ LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie;
+}
+
+TEST(NdkBinder, LinkToDeath) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binder);
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
+ ASSERT_NE(nullptr, recipient);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+
+ AIBinder_DeathRecipient_delete(recipient);
+ AIBinder_decStrong(binder);
+}
+
+class MyTestFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+ binder_status_t die() override {
+ ADD_FAILURE() << "die called on local instance";
+ return STATUS_OK;
+ }
+};
+
+TEST(NdkBinder, GetServiceInProcess) {
+ static const char* kInstanceName = "test-get-service-in-process";
+
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+
+ sp<IFoo> getFoo = IFoo::getService(kInstanceName);
+ EXPECT_EQ(foo.get(), getFoo.get());
+
+ int32_t out;
+ EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out));
+ EXPECT_EQ(2, out);
+}
+
+TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
+ AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binderA);
+
+ AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binderB);
+
+ EXPECT_EQ(binderA, binderB);
+
+ AIBinder_decStrong(binderA);
+ AIBinder_decStrong(binderB);
+}
+
+TEST(NdkBinder, ToFromJavaNullptr) {
+ EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr));
+ EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr));
+}
+
+TEST(NdkBinder, ABpBinderRefCount) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ AIBinder_Weak* wBinder = AIBinder_Weak_new(binder);
+
+ ASSERT_NE(nullptr, binder);
+ EXPECT_EQ(1, AIBinder_debugGetRefCount(binder));
+
+ AIBinder_decStrong(binder);
+
+ // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
+ ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
+
+ AIBinder_Weak_delete(wBinder);
+}
+
+TEST(NdkBinder, AddServiceMultipleTimes) {
+ static const char* kInstanceName1 = "test-multi-1";
+ static const char* kInstanceName2 = "test-multi-2";
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+ EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
+}
+
+TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
+ static volatile bool destroyed = false;
+ static std::mutex dMutex;
+ static std::condition_variable cv;
+
+ class MyEmpty : public aidl::BnEmpty {
+ virtual ~MyEmpty() {
+ destroyed = true;
+ cv.notify_one();
+ }
+ };
+
+ std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+
+ ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+ EXPECT_FALSE(destroyed);
+
+ service->takeInterface(empty);
+ service->forceFlushCommands();
+ empty = nullptr;
+
+ // give other binder thread time to process commands
+ {
+ using namespace std::chrono_literals;
+ std::unique_lock<std::mutex> lk(dMutex);
+ cv.wait_for(lk, 1s, [] { return destroyed; });
+ }
+
+ EXPECT_TRUE(destroyed);
+}
+
+class MyResultReceiver : public BnResultReceiver {
+ public:
+ Mutex mMutex;
+ Condition mCondition;
+ bool mHaveResult = false;
+ int32_t mResult = 0;
+
+ virtual void send(int32_t resultCode) {
+ AutoMutex _l(mMutex);
+ mResult = resultCode;
+ mHaveResult = true;
+ mCondition.signal();
+ }
+
+ int32_t waitForResult() {
+ AutoMutex _l(mMutex);
+ while (!mHaveResult) {
+ mCondition.wait(mMutex);
+ }
+ return mResult;
+ }
+};
+
+class MyShellCallback : public BnShellCallback {
+ public:
+ virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/,
+ const String16& /*mode*/) {
+ // Empty implementation.
+ return 0;
+ }
+};
+
+bool ReadFdToString(int fd, std::string* content) {
+ char buf[64];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ content->append(buf, n);
+ }
+ return (n == 0) ? true : false;
+}
+
+std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) {
+ int inFd[2] = {-1, -1};
+ int outFd[2] = {-1, -1};
+ int errFd[2] = {-1, -1};
+
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd));
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd));
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd));
+
+ sp<MyShellCallback> cb = new MyShellCallback();
+ sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
+
+ Vector<String16> argsVec;
+ for (int i = 0; i < args.size(); i++) {
+ argsVec.add(String16(args[i]));
+ }
+ status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec,
+ cb, resultReceiver);
+ EXPECT_EQ(error, android::OK);
+
+ status_t res = resultReceiver->waitForResult();
+ EXPECT_EQ(res, android::OK);
+
+ close(inFd[0]);
+ close(inFd[1]);
+ close(outFd[0]);
+ close(errFd[0]);
+ close(errFd[1]);
+
+ std::string ret;
+ EXPECT_TRUE(ReadFdToString(outFd[1], &ret));
+ close(outFd[1]);
+ return ret;
+}
+
+TEST(NdkBinder, UseHandleShellCommand) {
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService));
+
+ EXPECT_EQ("", shellCmdToString(testService, {}));
+ EXPECT_EQ("", shellCmdToString(testService, {"", ""}));
+ EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"}));
+ EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
+}
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kInstanceNameToDieFor);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kSomeInstanceName);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return generatedService();
+ }
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
+ ABinderProcess_startThreadPool();
+
+ return RUN_ALL_TESTS();
+}
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
deleted file mode 100644
index 8467734..0000000
--- a/libs/binder/ndk/test/main_client.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <gtest/gtest.h>
-#include <iface/iface.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-using ::android::sp;
-
-constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
-
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-// sp<IFoo> foo = IFoo::getService("asdfghkl;");
-// EXPECT_EQ(nullptr, foo.get());
-// }
-
-TEST(NdkBinder, CheckServiceThatDoesntExist) {
- AIBinder* binder = AServiceManager_checkService("asdfghkl;");
- ASSERT_EQ(nullptr, binder);
-}
-
-TEST(NdkBinder, CheckServiceThatDoesExist) {
- AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
- EXPECT_NE(nullptr, binder);
- EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
-
- AIBinder_decStrong(binder);
-}
-
-TEST(NdkBinder, DoubleNumber) {
- sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
- ASSERT_NE(foo, nullptr);
-
- int32_t out;
- EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out));
- EXPECT_EQ(2, out);
-}
-
-void LambdaOnDeath(void* cookie) {
- auto onDeath = static_cast<std::function<void(void)>*>(cookie);
- (*onDeath)();
-};
-TEST(NdkBinder, DeathRecipient) {
- using namespace std::chrono_literals;
-
- AIBinder* binder;
- sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder);
- ASSERT_NE(nullptr, foo.get());
- ASSERT_NE(nullptr, binder);
-
- std::mutex deathMutex;
- std::condition_variable deathCv;
- bool deathRecieved = false;
-
- std::function<void(void)> onDeath = [&] {
- std::cerr << "Binder died (as requested)." << std::endl;
- deathRecieved = true;
- deathCv.notify_one();
- };
-
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
-
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
-
- // the binder driver should return this if the service dies during the transaction
- EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
-
- foo = nullptr;
- AIBinder_decStrong(binder);
- binder = nullptr;
-
- std::unique_lock<std::mutex> lock(deathMutex);
- EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
- EXPECT_TRUE(deathRecieved);
-
- AIBinder_DeathRecipient_delete(recipient);
-}
-
-TEST(NdkBinder, RetrieveNonNdkService) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binder);
- EXPECT_TRUE(AIBinder_isRemote(binder));
- EXPECT_TRUE(AIBinder_isAlive(binder));
- EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
-
- AIBinder_decStrong(binder);
-}
-
-void OnBinderDeath(void* cookie) {
- LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie;
-}
-
-TEST(NdkBinder, LinkToDeath) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binder);
-
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
- ASSERT_NE(nullptr, recipient);
-
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr));
-
- AIBinder_DeathRecipient_delete(recipient);
- AIBinder_decStrong(binder);
-}
-
-class MyTestFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
- binder_status_t die() override {
- ADD_FAILURE() << "die called on local instance";
- return STATUS_OK;
- }
-};
-
-TEST(NdkBinder, GetServiceInProcess) {
- static const char* kInstanceName = "test-get-service-in-process";
-
- sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
-
- sp<IFoo> getFoo = IFoo::getService(kInstanceName);
- EXPECT_EQ(foo.get(), getFoo.get());
-
- int32_t out;
- EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out));
- EXPECT_EQ(2, out);
-}
-
-TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
- AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binderA);
-
- AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binderB);
-
- EXPECT_EQ(binderA, binderB);
-
- AIBinder_decStrong(binderA);
- AIBinder_decStrong(binderB);
-}
-
-TEST(NdkBinder, ToFromJavaNullptr) {
- EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr));
- EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr));
-}
-
-TEST(NdkBinder, ABpBinderRefCount) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- AIBinder_Weak* wBinder = AIBinder_Weak_new(binder);
-
- ASSERT_NE(nullptr, binder);
- EXPECT_EQ(1, AIBinder_debugGetRefCount(binder));
-
- AIBinder_decStrong(binder);
-
- // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
- ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
-
- AIBinder_Weak_delete(wBinder);
-}
-
-TEST(NdkBinder, AddServiceMultipleTimes) {
- static const char* kInstanceName1 = "test-multi-1";
- static const char* kInstanceName2 = "test-multi-2";
- sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
- EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
-}
-
-int main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
- ABinderProcess_startThreadPool();
-
- return RUN_ALL_TESTS();
-}
-
-#include <android/binder_auto_utils.h>
-#include <android/binder_interface_utils.h>
-#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp
deleted file mode 100644
index a6e17e8..0000000
--- a/libs/binder/ndk/test/main_server.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-#include <android/binder_process.h>
-#include <iface/iface.h>
-
-using ::android::sp;
-
-class MyFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
-
- binder_status_t die() override {
- LOG(FATAL) << "IFoo::die called!";
- return STATUS_UNKNOWN_ERROR;
- }
-};
-
-int service(const char* instance) {
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- // Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
-
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
- }
-
- ABinderProcess_joinThreadPool();
-
- return 1; // should not return
-}
-
-int main() {
- if (fork() == 0) {
- return service(IFoo::kInstanceNameToDieFor);
- }
-
- return service(IFoo::kSomeInstanceName);
-}
diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp
new file mode 100644
index 0000000..ebdc932
--- /dev/null
+++ b/libs/binderthreadstate/1.0/Android.bp
@@ -0,0 +1,14 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "binderthreadstateutilstest@1.0",
+ root: "binderthreadstateutilstest",
+ system_ext_specific: true,
+ srcs: [
+ "IHidlStuff.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/libs/binderthreadstate/1.0/IHidlStuff.hal
similarity index 60%
copy from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
copy to libs/binderthreadstate/1.0/IHidlStuff.hal
index 08483cb..ffb6499 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/libs/binderthreadstate/1.0/IHidlStuff.hal
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-#include <compositionengine/mock/Layer.h>
+package binderthreadstateutilstest@1.0;
-namespace android::compositionengine::mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace android::compositionengine::mock
+interface IHidlStuff {
+ callLocal();
+ call(int32_t idx);
+};
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index ee1a6a4..4655e1d 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -12,6 +12,59 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// DO NOT ADD NEW USAGES OF THIS
+// See comments in header file.
+cc_library_static {
+ name: "libbinderthreadstateutils",
+ double_loadable: true,
+ vendor_available: true,
+ host_supported: true,
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase", // libhwbinder is in here
+ ],
+
+ export_include_dirs: ["include"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+hidl_package_root {
+ name: "binderthreadstateutilstest",
+}
+
+aidl_interface {
+ name: "binderthreadstateutilstest.aidl",
+ srcs: ["IAidlStuff.aidl"],
+}
+
+cc_test {
+ name: "libbinderthreadstateutils_test",
+ srcs: ["test.cpp"],
+ static_libs: [
+ "binderthreadstateutilstest@1.0",
+ "binderthreadstateutilstest.aidl-cpp",
+ "libbinderthreadstateutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "libutils",
+ "liblog",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ require_root: true,
+}
+
+// TODO(b/148692216): remove empty lib
cc_library {
name: "libbinderthreadstate",
recovery_available: true,
@@ -21,28 +74,4 @@
support_system_process: true,
},
host_supported: true,
-
- srcs: [
- "IPCThreadStateBase.cpp",
- ],
-
- header_libs: [
- "libbase_headers",
- "libutils_headers",
- ],
-
- shared_libs: [
- "liblog",
- ],
-
- export_include_dirs: ["include"],
-
- sanitize: {
- misc_undefined: ["integer"],
- },
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
}
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/libs/binderthreadstate/IAidlStuff.aidl
similarity index 60%
rename from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
rename to libs/binderthreadstate/IAidlStuff.aidl
index 08483cb..0c81c42 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/libs/binderthreadstate/IAidlStuff.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,8 @@
* limitations under the License.
*/
-#include <compositionengine/mock/Layer.h>
-namespace android::compositionengine::mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace android::compositionengine::mock
+interface IAidlStuff {
+ void callLocal();
+ void call(int idx);
+}
diff --git a/libs/binderthreadstate/IPCThreadStateBase.cpp b/libs/binderthreadstate/IPCThreadStateBase.cpp
deleted file mode 100644
index fede151..0000000
--- a/libs/binderthreadstate/IPCThreadStateBase.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IPCThreadStateBase"
-
-#include <binderthreadstate/IPCThreadStateBase.h>
-#include <android-base/macros.h>
-
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-
-namespace android {
-
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-
-IPCThreadStateBase::IPCThreadStateBase() {
- pthread_setspecific(gTLS, this);
-}
-
-IPCThreadStateBase* IPCThreadStateBase::self()
-{
- if (gHaveTLS) {
-restart:
- const pthread_key_t k = gTLS;
- IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k);
- if (st) return st;
- return new IPCThreadStateBase;
- }
-
- pthread_mutex_lock(&gTLSMutex);
- if (!gHaveTLS) {
- int key_create_value = pthread_key_create(&gTLS, threadDestructor);
- if (key_create_value != 0) {
- pthread_mutex_unlock(&gTLSMutex);
- ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n",
- strerror(key_create_value));
- return nullptr;
- }
- gHaveTLS = true;
- }
- pthread_mutex_unlock(&gTLSMutex);
- goto restart;
-}
-
-void IPCThreadStateBase::pushCurrentState(CallState callState) {
- mCallStateStack.emplace(callState);
-}
-
-IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() {
- ALOG_ASSERT(mCallStateStack.size > 0);
- CallState val = mCallStateStack.top();
- mCallStateStack.pop();
- return val;
-}
-
-IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() {
- if (mCallStateStack.size() > 0) {
- return mCallStateStack.top();
- }
- return CallState::NONE;
-}
-
-void IPCThreadStateBase::threadDestructor(void *st)
-{
- IPCThreadStateBase* const self = static_cast<IPCThreadStateBase*>(st);
- if (self) {
- delete self;
- }
-}
-
-}; // namespace android
diff --git a/libs/binderthreadstate/TEST_MAPPING b/libs/binderthreadstate/TEST_MAPPING
new file mode 100644
index 0000000..2bd0463
--- /dev/null
+++ b/libs/binderthreadstate/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libbinderthreadstateutils_test"
+ }
+ ]
+}
diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
new file mode 100644
index 0000000..a3e5026
--- /dev/null
+++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// WARNING: DO NOT USE THIS
+// You should:
+// - have code know how it is handling things. Pass in caller information rather
+// than assuming that code is running in a specific global context
+// - use AIDL exclusively in your stack (HIDL is no longer required anywhere)
+
+#include <binder/IPCThreadState.h>
+#include <hwbinder/IPCThreadState.h>
+
+namespace android {
+
+enum class BinderCallType {
+ NONE,
+ BINDER,
+ HWBINDER,
+};
+
+// Based on where we are in recursion of nested binder/hwbinder calls, determine
+// which one we are closer to.
+inline static BinderCallType getCurrentServingCall() {
+ const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
+ const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+
+ if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE;
+ if (hwbinderSp == nullptr) return BinderCallType::BINDER;
+ if (binderSp == nullptr) return BinderCallType::HWBINDER;
+
+ if (hwbinderSp < binderSp) return BinderCallType::HWBINDER;
+ return BinderCallType::BINDER;
+}
+
+} // namespace android
diff --git a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h
deleted file mode 100644
index 6fdcc84..0000000
--- a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
-#define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
-
-#include <stack>
-namespace android {
-
-class IPCThreadStateBase {
-public:
- enum CallState {
- HWBINDER,
- BINDER,
- NONE,
- };
- static IPCThreadStateBase* self();
- void pushCurrentState(CallState callState);
- CallState popCurrentState();
- CallState getCurrentBinderCallState();
-
-private:
- IPCThreadStateBase();
- static void threadDestructor(void *st);
-
- std::stack<CallState> mCallStateStack;
-};
-
-}; // namespace android
-
-#endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
new file mode 100644
index 0000000..68cc225
--- /dev/null
+++ b/libs/binderthreadstate/test.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 <BnAidlStuff.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <binderthreadstate/CallerUtils.h>
+#include <binderthreadstateutilstest/1.0/IHidlStuff.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlTransportSupport.h>
+#include <linux/prctl.h>
+#include <sys/prctl.h>
+
+using android::BinderCallType;
+using android::defaultServiceManager;
+using android::getCurrentServingCall;
+using android::getService;
+using android::OK;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::Return;
+using binderthreadstateutilstest::V1_0::IHidlStuff;
+
+constexpr size_t kP1Id = 1;
+constexpr size_t kP2Id = 2;
+
+// AIDL and HIDL are in separate namespaces so using same service names
+std::string id2name(size_t id) {
+ return "libbinderthreadstateutils-" + std::to_string(id);
+}
+
+// There are two servers calling each other recursively like this.
+//
+// P1 P2
+// | --HIDL--> |
+// | <--HIDL-- |
+// | --AIDL--> |
+// | <--AIDL-- |
+// | --HIDL--> |
+// | <--HIDL-- |
+// | --AIDL--> |
+// | <--AIDL-- |
+// ..........
+//
+// Calls always come in pairs (AIDL returns AIDL, HIDL returns HIDL) because
+// this means that P1 always has a 'waitForResponse' call which can service the
+// returning call and continue the recursion. Of course, with more threads, more
+// complicated calls are possible, but this should do here.
+
+static void callHidl(size_t id, int32_t idx) {
+ auto stuff = IHidlStuff::getService(id2name(id));
+ CHECK(stuff->call(idx).isOk());
+}
+
+static void callAidl(size_t id, int32_t idx) {
+ sp<IAidlStuff> stuff;
+ CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff));
+ CHECK(stuff->call(idx).isOk());
+}
+
+class HidlServer : public IHidlStuff {
+public:
+ HidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {}
+ size_t thisId;
+ size_t otherId;
+
+ Return<void> callLocal() {
+ CHECK(BinderCallType::NONE == getCurrentServingCall());
+ return android::hardware::Status::ok();
+ }
+ Return<void> call(int32_t idx) {
+ LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
+ << " with tid: " << gettid();
+ CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+ if (idx > 0) {
+ if (thisId == kP1Id && idx % 4 < 2) {
+ callHidl(otherId, idx - 1);
+ } else {
+ callAidl(otherId, idx - 1);
+ }
+ }
+ CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+ return android::hardware::Status::ok();
+ }
+};
+class AidlServer : public BnAidlStuff {
+public:
+ AidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {}
+ size_t thisId;
+ size_t otherId;
+
+ Status callLocal() {
+ CHECK(BinderCallType::NONE == getCurrentServingCall());
+ return Status::ok();
+ }
+ Status call(int32_t idx) {
+ LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
+ << " with tid: " << gettid();
+ CHECK(BinderCallType::BINDER == getCurrentServingCall());
+ if (idx > 0) {
+ if (thisId == kP2Id && idx % 4 < 2) {
+ callHidl(otherId, idx - 1);
+ } else {
+ callAidl(otherId, idx - 1);
+ }
+ }
+ CHECK(BinderCallType::BINDER == getCurrentServingCall());
+ return Status::ok();
+ }
+};
+
+TEST(BinderThreadState, LocalHidlCall) {
+ sp<IHidlStuff> server = new HidlServer(0, 0);
+ EXPECT_TRUE(server->callLocal().isOk());
+}
+
+TEST(BinderThreadState, LocalAidlCall) {
+ sp<IAidlStuff> server = new AidlServer(0, 0);
+ EXPECT_TRUE(server->callLocal().isOk());
+}
+
+TEST(BindThreadState, RemoteHidlCall) {
+ auto stuff = IHidlStuff::getService(id2name(kP1Id));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(0).isOk());
+}
+TEST(BindThreadState, RemoteAidlCall) {
+ sp<IAidlStuff> stuff;
+ ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(0).isOk());
+}
+
+TEST(BindThreadState, RemoteNestedStartHidlCall) {
+ auto stuff = IHidlStuff::getService(id2name(kP1Id));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(100).isOk());
+}
+TEST(BindThreadState, RemoteNestedStartAidlCall) {
+ sp<IAidlStuff> stuff;
+ ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
+ ASSERT_NE(nullptr, stuff);
+ EXPECT_TRUE(stuff->call(100).isOk());
+}
+
+int server(size_t thisId, size_t otherId) {
+ // AIDL
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ sp<AidlServer> aidlServer = new AidlServer(thisId, otherId);
+ CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
+ android::ProcessState::self()->startThreadPool();
+
+ // HIDL
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
+ sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
+ CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str()));
+ android::hardware::joinRpcThreadpool();
+
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return server(kP1Id, kP2Id);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return server(kP2Id, kP1Id);
+ }
+
+ android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str()));
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str());
+ android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str()));
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str());
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
new file mode 100644
index 0000000..bab2674
--- /dev/null
+++ b/libs/bufferqueueconverter/Android.bp
@@ -0,0 +1,28 @@
+cc_library_headers {
+ name: "libbufferqueueconverter_headers",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+ name: "libbufferqueueconverter",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+
+ srcs: [
+ "BufferQueueConverter.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "libui",
+ "libutils",
+ "libbinder",
+ "libbase",
+ "liblog",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/libs/bufferqueueconverter/BufferQueueConverter.cpp b/libs/bufferqueueconverter/BufferQueueConverter.cpp
new file mode 100644
index 0000000..b1896fa
--- /dev/null
+++ b/libs/bufferqueueconverter/BufferQueueConverter.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#include <gui/Surface.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+
+#include "include/bufferqueueconverter/BufferQueueConverter.h"
+
+
+using ::android::Surface;
+using ::android::IGraphicBufferProducer;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::H2BGraphicBufferProducer;
+
+
+namespace android {
+
+struct SurfaceHolder {
+ sp<Surface> surface;
+ SurfaceHolder(const sp<Surface>& s) : surface(s) {}
+};
+
+/**
+ * Custom deleter for SurfaceHolder unique pointer
+ */
+void destroySurfaceHolder(SurfaceHolder* surfaceHolder) {
+ delete surfaceHolder;
+}
+
+
+SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token) {
+ if (token == nullptr) {
+ ALOGE("Passed IGraphicBufferProducer handle is invalid.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ sp<IGraphicBufferProducer> bufferProducer = new H2BGraphicBufferProducer(token);
+ if (bufferProducer == nullptr) {
+ ALOGE("Failed to get IGraphicBufferProducer.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ sp<Surface> newSurface(new Surface(bufferProducer, true));
+ if (newSurface == nullptr) {
+ ALOGE("Failed to create Surface from HGBP.");
+ return SurfaceHolderUniquePtr(nullptr, nullptr);
+ }
+
+ return SurfaceHolderUniquePtr(new SurfaceHolder(newSurface), destroySurfaceHolder);
+}
+
+
+ANativeWindow* getNativeWindow(SurfaceHolder* handle) {
+ if (handle == nullptr) {
+ ALOGE("SurfaceHolder is invalid.");
+ return nullptr;
+ }
+
+ return static_cast<ANativeWindow*>(handle->surface.get());
+}
+
+} // namespace android
diff --git a/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
new file mode 100644
index 0000000..84bceba
--- /dev/null
+++ b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_BUFFER_QUEUE_CONVERTER_H
+#define ANDROID_BUFFER_QUEUE_CONVERTER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <android/native_window.h>
+
+using ::android::sp;
+using HGraphicBufferProducer =
+ ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+
+namespace android {
+ /**
+ * Opaque handle for a data structure holding Surface.
+ */
+ typedef struct SurfaceHolder SurfaceHolder;
+
+ /**
+ * SurfaceHolder unique pointer type
+ */
+ using SurfaceHolderUniquePtr = std::unique_ptr<SurfaceHolder, void(*)(SurfaceHolder*)>;
+
+ /**
+ * Returns a SurfaceHolder that wraps a Surface generated from a given HGBP.
+ *
+ * @param token Hardware IGraphicBufferProducer to create a
+ * Surface.
+ * @return SurfaceHolder Unique pointer to created SurfaceHolder object.
+ */
+ SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token);
+
+ /**
+ * Returns ANativeWindow pointer from a given SurfaceHolder. Returned
+ * pointer is valid only while the containing SurfaceHolder is alive.
+ *
+ * @param surfaceHolder SurfaceHolder to generate a native window.
+ * @return ANativeWindow* a pointer to a generated native window.
+ */
+ ANativeWindow* getNativeWindow(SurfaceHolder* surfaceHolder);
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_QUEUE_CONVERTER_H
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index ee44cf5..31a399b 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -49,6 +49,8 @@
static std::mutex gInitializedMutex;
static bool gInitialized = false;
+static std::mutex gTrackingMutex;
+static bool gTracking = false;
static uint32_t gNPolicies = 0;
static uint32_t gNCpus = 0;
static std::vector<std::vector<uint32_t>> gPolicyFreqs;
@@ -161,7 +163,9 @@
// This function should *not* be called while tracking is already active; doing so is unnecessary
// and can lead to accounting errors.
bool startTrackingUidTimes() {
+ std::lock_guard<std::mutex> guard(gTrackingMutex);
if (!initGlobals()) return false;
+ if (gTracking) return true;
unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
if (cpuPolicyFd < 0) return false;
@@ -209,8 +213,9 @@
if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
}
- return attachTracepointProgram("sched", "sched_switch") &&
+ gTracking = attachTracepointProgram("sched", "sched_switch") &&
attachTracepointProgram("power", "cpu_frequency");
+ return gTracking;
}
std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
new file mode 100644
index 0000000..de32ff4
--- /dev/null
+++ b/libs/fakeservicemanager/Android.bp
@@ -0,0 +1,25 @@
+cc_defaults {
+ name: "fakeservicemanager_defaults",
+ srcs: [
+ "ServiceManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_library {
+ name: "libfakeservicemanager",
+ defaults: ["fakeservicemanager_defaults"],
+}
+
+cc_test_host {
+ name: "fakeservicemanager_test",
+ defaults: ["fakeservicemanager_defaults"],
+ srcs: [
+ "test_sm.cpp",
+ ],
+ static_libs: ["libgmock"],
+}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
new file mode 100644
index 0000000..6964324
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "ServiceManager.h"
+
+namespace android {
+
+ServiceManager::ServiceManager() {}
+
+sp<IBinder> ServiceManager::getService( const String16& name) const {
+ // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
+ return checkService(name);
+}
+
+sp<IBinder> ServiceManager::checkService( const String16& name) const {
+ auto it = mNameToService.find(name);
+ if (it == mNameToService.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+ bool /*allowIsolated*/,
+ int /*dumpsysFlags*/) {
+ mNameToService[name] = service;
+ return NO_ERROR;
+}
+
+Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+ Vector<String16> services;
+ for (auto const& [name, service] : mNameToService) {
+ (void) service;
+ services.push_back(name);
+ }
+ return services;
+}
+
+IBinder* ServiceManager::onAsBinder() {
+ return nullptr;
+}
+
+sp<IBinder> ServiceManager::waitForService(const String16& name) {
+ return checkService(name);
+}
+
+bool ServiceManager::isDeclared(const String16& name) {
+ return mNameToService.find(name) != mNameToService.end();
+}
+
+} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
new file mode 100644
index 0000000..62311d4
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IServiceManager.h>
+
+#include <map>
+
+namespace android {
+
+/**
+ * A local host simple implementation of IServiceManager, that does not
+ * communicate over binder.
+*/
+class ServiceManager : public IServiceManager {
+public:
+ ServiceManager();
+
+ /**
+ * Equivalent of checkService.
+ */
+ sp<IBinder> getService( const String16& name) const override;
+
+ /**
+ * Retrieve an existing service, non-blocking.
+ */
+ sp<IBinder> checkService( const String16& name) const override;
+
+ /**
+ * Register a service.
+ */
+ status_t addService(const String16& name, const sp<IBinder>& service,
+ bool allowIsolated = false,
+ int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
+
+ /**
+ * Return list of all existing services.
+ */
+ Vector<String16> listServices(int dumpsysFlags = 0) override;
+
+ IBinder* onAsBinder() override;
+
+ /**
+ * Effectively no-oped in this implementation - equivalent to checkService.
+ */
+ sp<IBinder> waitForService(const String16& name) override;
+
+ /**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * If this returns true, waitForService should always be able to return the
+ * service.
+ */
+ bool isDeclared(const String16& name) override;
+
+private:
+ std::map<String16, sp<IBinder>> mNameToService;
+};
+
+} // namespace android
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
new file mode 100644
index 0000000..71e5abe
--- /dev/null
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <binder/Binder.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include "ServiceManager.h"
+
+using android::sp;
+using android::BBinder;
+using android::IBinder;
+using android::OK;
+using android::status_t;
+using android::ServiceManager;
+using android::String16;
+using android::IServiceManager;
+using testing::ElementsAre;
+
+static sp<IBinder> getBinder() {
+ class LinkableBinder : public BBinder {
+ status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override {
+ // let SM linkToDeath
+ return OK;
+ }
+ };
+
+ return new LinkableBinder;
+}
+
+TEST(AddService, HappyHappy) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(AddService, HappyOverExistingService) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(GetService, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_EQ(sm->getService(String16("foo")), service);
+}
+
+TEST(GetService, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
+TEST(ListServices, AllServices) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_EQ(sm->addService(String16("sc"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL), OK);
+ EXPECT_EQ(sm->addService(String16("sb"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_HIGH), OK);
+ EXPECT_EQ(sm->addService(String16("sa"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL), OK);
+
+ android::Vector<String16> out = sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+
+ // all there and in the right order
+ EXPECT_THAT(out, ElementsAre(String16("sa"), String16("sb"), String16("sc"),
+ String16("sd")));
+}
+
+TEST(WaitForService, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
+}
+
+TEST(WaitForService, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_EQ(sm->waitForService(String16("foo")), service);
+}
+
+TEST(IsDeclared, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_FALSE(sm->isDeclared(String16("foo")));
+}
+
+TEST(IsDeclared, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_TRUE(sm->isDeclared(String16("foo")));
+}
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 9f5b0ff..de3503b 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -48,50 +48,6 @@
remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
- if (!outStats) return UNEXPECTED_NULL;
-
- Parcel data, reply;
- status_t status;
-
- if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK)
- return status;
-
- if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) !=
- OK)
- return status;
-
- int32_t result = 0;
- if ((status = reply.readInt32(&result)) != OK) return status;
- if (result != OK) return result;
-
- outStats->clear();
- return reply.readParcelableVector(outStats);
- }
-
- virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
- if (!outStats) return UNEXPECTED_NULL;
-
- Parcel data, reply;
- status_t status;
-
- if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) {
- return status;
- }
-
- if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) !=
- OK) {
- return status;
- }
-
- int32_t result = 0;
- if ((status = reply.readInt32(&result)) != OK) return status;
- if (result != OK) return result;
-
- outStats->clear();
- return reply.readParcelableVector(outStats);
- }
-
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) {
Parcel data, reply;
@@ -150,32 +106,6 @@
return OK;
}
- case GET_GPU_STATS_GLOBAL_INFO: {
- CHECK_INTERFACE(IGpuService, data, reply);
-
- std::vector<GpuStatsGlobalInfo> stats;
- const status_t result = getGpuStatsGlobalInfo(&stats);
-
- if ((status = reply->writeInt32(result)) != OK) return status;
- if (result != OK) return result;
-
- if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
- return OK;
- }
- case GET_GPU_STATS_APP_INFO: {
- CHECK_INTERFACE(IGpuService, data, reply);
-
- std::vector<GpuStatsAppInfo> stats;
- const status_t result = getGpuStatsAppInfo(&stats);
-
- if ((status = reply->writeInt32(result)) != OK) return status;
- if (result != OK) return result;
-
- if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
- return OK;
- }
case SET_TARGET_STATS: {
CHECK_INTERFACE(IGpuService, data, reply);
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index f523d58..c7c6d1e 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -16,12 +16,11 @@
#pragma once
-#include <vector>
-
#include <binder/IInterface.h>
#include <cutils/compiler.h>
#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
+
+#include <vector>
namespace android {
@@ -43,20 +42,12 @@
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
-
- // get GPU global stats from GpuStats module.
- virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0;
-
- // get GPU app stats from GpuStats module.
- virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
public:
enum IGpuServiceTag {
SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
- GET_GPU_STATS_GLOBAL_INFO,
- GET_GPU_STATS_APP_INFO,
SET_TARGET_STATS,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 55a892e..99d3856 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,7 +55,11 @@
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
+ "IConsumerListener.cpp",
"IDisplayEventConnection.cpp",
+ "IGraphicBufferConsumer.cpp",
+ "IGraphicBufferProducer.cpp",
+ "IProducerListener.cpp",
"IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
@@ -63,22 +67,32 @@
"LayerDebugInfo.cpp",
"LayerMetadata.cpp",
"LayerState.cpp",
+ "OccupancyTracker.cpp",
"StreamSplitter.cpp",
"Surface.cpp",
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
"view/Surface.cpp",
+ "bufferqueue/1.0/B2HProducerListener.cpp",
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+ "bufferqueue/2.0/B2HProducerListener.cpp",
+ "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
],
shared_libs: [
"android.frameworks.bufferhub@1.0",
+ "libbinder",
"libbufferhub",
"libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
"libinput",
"libpdx_default_transport",
],
+ export_shared_lib_headers: [
+ "libbinder",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
@@ -118,6 +132,7 @@
cflags: [
"-DNO_BUFFERHUB",
+ "-DNO_BINDER",
],
defaults: ["libgui_bufferqueue-defaults"],
@@ -140,19 +155,11 @@
"FrameTimestamps.cpp",
"GLConsumerUtils.cpp",
"HdrMetadata.cpp",
- "IConsumerListener.cpp",
- "IGraphicBufferConsumer.cpp",
- "IGraphicBufferProducer.cpp",
- "IProducerListener.cpp",
- "OccupancyTracker.cpp",
- "bufferqueue/1.0/B2HProducerListener.cpp",
+ "QueueBufferInputOutput.cpp",
"bufferqueue/1.0/Conversion.cpp",
- "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
"bufferqueue/1.0/H2BProducerListener.cpp",
"bufferqueue/1.0/WProducerListener.cpp",
"bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
- "bufferqueue/2.0/B2HProducerListener.cpp",
- "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
"bufferqueue/2.0/H2BProducerListener.cpp",
"bufferqueue/2.0/types.cpp",
],
@@ -189,7 +196,6 @@
"android.hardware.graphics.common@1.2",
"android.hidl.token@1.0-utils",
"libbase",
- "libbinder",
"libcutils",
"libEGL",
"libGLESv2",
@@ -202,13 +208,16 @@
"libvndksupport",
],
+ static_libs: [
+ "libbinderthreadstateutils",
+ ],
+
header_libs: [
"libgui_headers",
"libnativebase_headers",
],
export_shared_lib_headers: [
- "libbinder",
"libEGL",
"libnativewindow",
"libui",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index d2b25a2..e2f5d31 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -31,6 +31,68 @@
namespace android {
+void BLASTBufferItemConsumer::onDisconnect() {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mPreviouslyConnected = mCurrentlyConnected;
+ mCurrentlyConnected = false;
+ if (mPreviouslyConnected) {
+ mDisconnectEvents.push(mCurrentFrameNumber);
+ }
+ mFrameEventHistory.onDisconnect();
+}
+
+void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (newTimestamps) {
+ // BufferQueueProducer only adds a new timestamp on
+ // queueBuffer
+ mCurrentFrameNumber = newTimestamps->frameNumber;
+ mFrameEventHistory.addQueue(*newTimestamps);
+ }
+ if (outDelta) {
+ // frame event histories will be processed
+ // only after the producer connects and requests
+ // deltas for the first time. Forward this intent
+ // to SF-side to turn event processing back on
+ mPreviouslyConnected = mCurrentlyConnected;
+ mCurrentlyConnected = true;
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ }
+}
+
+void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& glDoneFence,
+ const sp<Fence>& presentFence,
+ const sp<Fence>& prevReleaseFence,
+ CompositorTiming compositorTiming,
+ nsecs_t latchTime, nsecs_t dequeueReadyTime) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+
+ // if the producer is not connected, don't bother updating,
+ // the next producer that connects won't access this frame event
+ if (!mCurrentlyConnected) return;
+ std::shared_ptr<FenceTime> glDoneFenceTime = std::make_shared<FenceTime>(glDoneFence);
+ std::shared_ptr<FenceTime> presentFenceTime = std::make_shared<FenceTime>(presentFence);
+ std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence);
+
+ mFrameEventHistory.addLatch(frameNumber, latchTime);
+ mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime);
+ mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime,
+ compositorTiming);
+}
+
+void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) {
+ bool disconnect = false;
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ while (!mDisconnectEvents.empty() && mDisconnectEvents.front() <= frameNumber) {
+ disconnect = true;
+ mDisconnectEvents.pop();
+ }
+ if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
+}
+
BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
: mSurfaceControl(surface),
mWidth(width),
@@ -39,7 +101,7 @@
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mConsumer->setMaxAcquiredBufferCount(MAX_ACQUIRED_BUFFERS);
mBufferItemConsumer =
- new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
+ new BLASTBufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
static int32_t id = 0;
auto name = std::string("BLAST Consumer") + std::to_string(id);
id++;
@@ -79,10 +141,21 @@
std::unique_lock _lock{mMutex};
ATRACE_CALL();
+ if (!stats.empty()) {
+ mTransformHint = stats[0].transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
+ stats[0].frameEventStats.refreshStartTime,
+ stats[0].frameEventStats.gpuCompositionDoneFence,
+ stats[0].presentFence,
+ stats[0].previousReleaseFence,
+ stats[0].frameEventStats.compositorTiming,
+ stats[0].latchTime,
+ stats[0].frameEventStats.dequeueReadyTime);
+ }
if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
- if (stats.size() > 0) {
+ if (!stats.empty()) {
mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
- mTransformHint = stats[0].transformHint;
} else {
ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
mPendingReleaseItem.releaseFence = nullptr;
@@ -143,6 +216,14 @@
mNumAcquired++;
mSubmitted.push(bufferItem);
+ bool needsDisconnect = false;
+ mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
+
+ // if producer disconnected before, notify SurfaceFlinger
+ if (needsDisconnect) {
+ t->notifyProducerDisconnect(mSurfaceControl);
+ }
+
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
@@ -151,10 +232,11 @@
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+ t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+ t->setDesiredPresentTime(bufferItem.mTimestamp);
if (applyTransaction) {
t->apply();
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9b74fef..4435265 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -66,6 +66,8 @@
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
+ConsumerListener::~ConsumerListener() = default;
+
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
@@ -291,8 +293,9 @@
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
VALIDATE_CONSISTENCY();
}
@@ -765,7 +768,12 @@
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
std::vector<OccupancyTracker::Segment>* outHistory) {
std::lock_guard<std::mutex> lock(mCore->mMutex);
+#ifndef NO_BINDER
*outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+#else
+ (void)forceFlush;
+ outHistory->clear();
+#endif
return NO_ERROR;
}
@@ -798,7 +806,7 @@
bool denied = false;
const uid_t uid = BufferQueueThreadState::getCallingUid();
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
// permission check can't be done for vendors as vendors have no access to
// the PermissionController. We need to do a runtime check as well, since
// the system variant of libgui can be loaded in a vendor process. For eg:
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 3b0120b..9e5d681 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -84,54 +84,54 @@
return INVALID_OPERATION;
}
-BufferQueueCore::BufferQueueCore() :
- mMutex(),
- mIsAbandoned(false),
- mConsumerControlledByApp(false),
- mConsumerName(getUniqueName()),
- mConsumerListener(),
- mConsumerUsageBits(0),
- mConsumerIsProtected(false),
- mConnectedApi(NO_CONNECTED_API),
- mLinkedToDeath(),
- mConnectedProducerListener(),
- mBufferReleasedCbEnabled(false),
- mSlots(),
- mQueue(),
- mFreeSlots(),
- mFreeBuffers(),
- mUnusedSlots(),
- mActiveBuffers(),
- mDequeueCondition(),
- mDequeueBufferCannotBlock(false),
- mQueueBufferCanDrop(false),
- mLegacyBufferDrop(true),
- mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
- mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
- mMaxAcquiredBufferCount(1),
- mMaxDequeuedBufferCount(1),
- mBufferHasBeenQueued(false),
- mFrameCounter(0),
- mTransformHint(0),
- mIsAllocating(false),
- mIsAllocatingCondition(),
- mAllowAllocation(true),
- mBufferAge(0),
- mGenerationNumber(0),
- mAsyncMode(false),
- mSharedBufferMode(false),
- mAutoRefresh(false),
- mSharedBufferSlot(INVALID_BUFFER_SLOT),
- mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN),
- mLastQueuedSlot(INVALID_BUFFER_SLOT),
- mUniqueId(getUniqueId()),
- mAutoPrerotation(false),
- mTransformHintInUse(0)
-{
+BufferQueueCore::BufferQueueCore()
+ : mMutex(),
+ mIsAbandoned(false),
+ mConsumerControlledByApp(false),
+ mConsumerName(getUniqueName()),
+ mConsumerListener(),
+ mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
+ mConnectedApi(NO_CONNECTED_API),
+ mLinkedToDeath(),
+ mConnectedProducerListener(),
+ mBufferReleasedCbEnabled(false),
+ mSlots(),
+ mQueue(),
+ mFreeSlots(),
+ mFreeBuffers(),
+ mUnusedSlots(),
+ mActiveBuffers(),
+ mDequeueCondition(),
+ mDequeueBufferCannotBlock(false),
+ mQueueBufferCanDrop(false),
+ mLegacyBufferDrop(true),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+ mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+ mMaxAcquiredBufferCount(1),
+ mMaxDequeuedBufferCount(1),
+ mBufferHasBeenQueued(false),
+ mFrameCounter(0),
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition(),
+ mAllowAllocation(true),
+ mBufferAge(0),
+ mGenerationNumber(0),
+ mAsyncMode(false),
+ mSharedBufferMode(false),
+ mAutoRefresh(false),
+ mSharedBufferSlot(INVALID_BUFFER_SLOT),
+ mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ HAL_DATASPACE_UNKNOWN),
+ mLastQueuedSlot(INVALID_BUFFER_SLOT),
+ mUniqueId(getUniqueId()),
+ mAutoPrerotation(false),
+ mTransformHintInUse(0),
+ mFrameRate(0) {
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 6b11a54..9e86838 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -67,6 +67,7 @@
##__VA_ARGS__)
static constexpr uint32_t BQ_LAYER_COUNT = 1;
+ProducerListener::~ProducerListener() = default;
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
bool consumerIsSurfaceFlinger) :
@@ -1005,8 +1006,9 @@
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
@@ -1252,6 +1254,7 @@
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
+#ifndef NO_BINDER
if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
@@ -1261,6 +1264,7 @@
}
mCore->mLinkedToDeath = listener;
}
+#endif
mCore->mConnectedProducerListener = listener;
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
}
@@ -1329,6 +1333,7 @@
if (mCore->mConnectedApi == api) {
mCore->freeAllBuffersLocked();
+#ifndef NO_BINDER
// Remove our death notification callback if we have one
if (mCore->mLinkedToDeath != nullptr) {
sp<IBinder> token =
@@ -1338,6 +1343,7 @@
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
+#endif
mCore->mSharedBufferSlot =
BufferQueueCore::INVALID_BUFFER_SLOT;
mCore->mLinkedToDeath = nullptr;
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
index 3b531ec..976c9b9 100644
--- a/libs/gui/BufferQueueThreadState.cpp
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#ifndef NO_BINDER
#include <binder/IPCThreadState.h>
+#include <binderthreadstate/CallerUtils.h>
+#endif // NO_BINDER
#include <hwbinder/IPCThreadState.h>
#include <private/gui/BufferQueueThreadState.h>
#include <unistd.h>
@@ -22,17 +25,25 @@
namespace android {
uid_t BufferQueueThreadState::getCallingUid() {
- if (hardware::IPCThreadState::self()->isServingCall()) {
+#ifndef NO_BINDER
+ if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingUid();
}
return IPCThreadState::self()->getCallingUid();
+#else // NO_BINDER
+ return hardware::IPCThreadState::self()->getCallingUid();
+#endif // NO_BINDER
}
pid_t BufferQueueThreadState::getCallingPid() {
- if (hardware::IPCThreadState::self()->isServingCall()) {
+#ifndef NO_BINDER
+ if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingPid();
}
return IPCThreadState::self()->getCallingPid();
+#else // NO_BINDER
+ return hardware::IPCThreadState::self()->getCallingPid();
+#endif // NO_BINDER
}
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 8af1a1c..15f966d 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -104,7 +104,7 @@
mConfigChangeFlag = configChangeFlag;
}
-int DisplayEventDispatcher::getFd() {
+int DisplayEventDispatcher::getFd() const {
return mReceiver.getFd();
}
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index c04d907..6749c3c 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -355,6 +355,10 @@
mProducerWantsEvents = false;
}
+void ConsumerFrameEventHistory::setProducerWantsEvents() {
+ mProducerWantsEvents = true;
+}
+
void ConsumerFrameEventHistory::initializeCompositorTiming(
const CompositorTiming& compositorTiming) {
mCompositorTiming = compositorTiming;
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 48cb4b9..f3bd90c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -90,7 +90,6 @@
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
// clang warning -Wweak-vtables)
BpConsumerListener::~BpConsumerListener() = default;
-ConsumerListener::~ConsumerListener() = default;
IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 75876f2..7b5596e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -1113,133 +1113,5 @@
parcel.read(*this);
}
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps);
-}
-
-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/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 808e336..5c81b9d 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,8 +119,6 @@
return BBinder::onTransact(code, data, reply, flags);
}
-ProducerListener::~ProducerListener() = default;
-
DummyProducerListener::~DummyProducerListener() = default;
bool BnProducerListener::needsReleaseNotify() {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 073543c..2f27fd2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -34,8 +34,10 @@
#include <system/graphics.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
#include <ui/HdrCapabilities.h>
#include <utils/Log.h>
@@ -351,22 +353,43 @@
remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply);
}
- virtual status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs)
- {
+ virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ remote()->transact(BnSurfaceComposer::GET_DISPLAY_STATE, data, &reply);
+ const status_t result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(state, reply.readInplace(sizeof(ui::DisplayState)), sizeof(ui::DisplayState));
+ }
+ return result;
+ }
+
+ virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
+ const status_t result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
+ }
+ return result;
+ }
+
+ virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
- status_t result = reply.readInt32();
+ const status_t result = reply.readInt32();
if (result == NO_ERROR) {
- size_t numConfigs = reply.readUint32();
+ const size_t numConfigs = reply.readUint32();
configs->clear();
configs->resize(numConfigs);
for (size_t c = 0; c < numConfigs; ++c) {
- memcpy(&(configs->editItemAt(c)),
- reply.readInplace(sizeof(DisplayInfo)),
- sizeof(DisplayInfo));
+ memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
+ sizeof(DisplayConfig));
}
}
return result;
@@ -1297,17 +1320,40 @@
reply->writeStrongBinder(display);
return NO_ERROR;
}
+ case GET_DISPLAY_STATE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ ui::DisplayState state;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayState(display, &state);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(ui::DisplayState)), &state,
+ sizeof(ui::DisplayState));
+ }
+ return NO_ERROR;
+ }
+ case GET_DISPLAY_INFO: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ DisplayInfo info;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayInfo(display, &info);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
+ }
+ return NO_ERROR;
+ }
case GET_DISPLAY_CONFIGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- Vector<DisplayInfo> configs;
- sp<IBinder> display = data.readStrongBinder();
- status_t result = getDisplayConfigs(display, &configs);
+ Vector<DisplayConfig> configs;
+ const sp<IBinder> display = data.readStrongBinder();
+ const status_t result = getDisplayConfigs(display, &configs);
reply->writeInt32(result);
if (result == NO_ERROR) {
reply->writeUint32(static_cast<uint32_t>(configs.size()));
for (size_t c = 0; c < configs.size(); ++c) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)),
- &configs[c], sizeof(DisplayInfo));
+ memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
+ sizeof(DisplayConfig));
}
}
return NO_ERROR;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index e5e25aa..69f7894 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -30,6 +30,66 @@
} // Anonymous namespace
+status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
+ status_t err = output->writeUint64(frameNumber);
+ if (err != NO_ERROR) return err;
+
+ if (gpuCompositionDoneFence) {
+ err = output->writeBool(true);
+ if (err != NO_ERROR) return err;
+
+ err = output->write(*gpuCompositionDoneFence);
+ } else {
+ err = output->writeBool(false);
+ }
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.deadline);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.interval);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(compositorTiming.presentLatency);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(refreshStartTime);
+ if (err != NO_ERROR) return err;
+
+ err = output->writeInt64(dequeueReadyTime);
+ return err;
+}
+
+status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) {
+ status_t err = input->readUint64(&frameNumber);
+ if (err != NO_ERROR) return err;
+
+ bool hasFence = false;
+ err = input->readBool(&hasFence);
+ if (err != NO_ERROR) return err;
+
+ if (hasFence) {
+ gpuCompositionDoneFence = new Fence();
+ err = input->read(*gpuCompositionDoneFence);
+ if (err != NO_ERROR) return err;
+ }
+
+ err = input->readInt64(&(compositorTiming.deadline));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&(compositorTiming.interval));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&(compositorTiming.presentLatency));
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&refreshStartTime);
+ if (err != NO_ERROR) return err;
+
+ err = input->readInt64(&dequeueReadyTime);
+ return err;
+}
+
status_t SurfaceStats::writeToParcel(Parcel* output) const {
status_t err = output->writeStrongBinder(surfaceControl);
if (err != NO_ERROR) {
@@ -49,6 +109,11 @@
err = output->writeBool(false);
}
err = output->writeUint32(transformHint);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = output->writeParcelable(eventStats);
return err;
}
@@ -74,6 +139,11 @@
}
}
err = input->readUint32(&transformHint);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = input->readParcelable(&eventStats);
return err;
}
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
new file mode 100644
index 0000000..30f0ef6
--- /dev/null
+++ b/libs/gui/QueueBufferInputOutput.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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/Surface.cpp b/libs/gui/Surface.cpp
index d5cf11d..23532e7 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -50,6 +50,17 @@
using ui::ColorMode;
using ui::Dataspace;
+namespace {
+
+bool isInterceptorRegistrationOp(int op) {
+ return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR;
+}
+
+} // namespace
+
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
@@ -366,18 +377,58 @@
int Surface::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mDequeueInterceptor != nullptr) {
+ auto interceptor = c->mDequeueInterceptor;
+ auto data = c->mDequeueInterceptorData;
+ return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->dequeueBuffer(buffer, fenceFd);
+}
+
+int Surface::dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ Surface* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
int Surface::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mCancelInterceptor != nullptr) {
+ auto interceptor = c->mCancelInterceptor;
+ auto data = c->mCancelInterceptorData;
+ return interceptor(window, Surface::cancelBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->cancelBuffer(buffer, fenceFd);
+}
+
+int Surface::cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
return c->cancelBuffer(buffer, fenceFd);
}
int Surface::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mQueueInterceptor != nullptr) {
+ auto interceptor = c->mQueueInterceptor;
+ auto data = c->mQueueInterceptorData;
+ return interceptor(window, Surface::queueBufferInternal, data, buffer, fenceFd);
+ }
+ }
+ return c->queueBuffer(buffer, fenceFd);
+}
+
+int Surface::queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ Surface* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
@@ -420,21 +471,38 @@
return c->queueBuffer(buffer, -1);
}
-int Surface::hook_query(const ANativeWindow* window,
- int what, int* value) {
- const Surface* c = getSelf(window);
- return c->query(what, value);
-}
-
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
Surface* c = getSelf(window);
- int result = c->perform(operation, args);
+ int result;
+ // Don't acquire shared ownership of the interceptor mutex if we're going to
+ // do interceptor registration, as otherwise we'll deadlock on acquiring
+ // exclusive ownership.
+ if (!isInterceptorRegistrationOp(operation)) {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mPerformInterceptor != nullptr) {
+ result = c->mPerformInterceptor(window, Surface::performInternal,
+ c->mPerformInterceptorData, operation, args);
+ va_end(args);
+ return result;
+ }
+ }
+ result = c->perform(operation, args);
va_end(args);
return result;
}
+int Surface::performInternal(ANativeWindow* window, int operation, va_list args) {
+ Surface* c = getSelf(window);
+ return c->perform(operation, args);
+}
+
+int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
+ const Surface* c = getSelf(window);
+ return c->query(what, value);
+}
+
int Surface::setSwapInterval(int interval) {
ATRACE_CALL();
// EGL specification states:
@@ -1096,6 +1164,22 @@
case NATIVE_WINDOW_SET_FRAME_RATE:
res = dispatchSetFrameRate(args);
break;
+ case NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR:
+ res = dispatchAddCancelInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR:
+ res = dispatchAddDequeueInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR:
+ res = dispatchAddPerformInterceptor(args);
+ break;
+ case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
+ res = dispatchAddQueueInterceptor(args);
+ break;
+ case NATIVE_WINDOW_ALLOCATE_BUFFERS:
+ allocateBuffers();
+ res = NO_ERROR;
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1329,6 +1413,45 @@
return setFrameRate(frameRate);
}
+int Surface::dispatchAddCancelInterceptor(va_list args) {
+ ANativeWindow_cancelBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_cancelBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mCancelInterceptor = interceptor;
+ mCancelInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddDequeueInterceptor(va_list args) {
+ ANativeWindow_dequeueBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_dequeueBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mDequeueInterceptor = interceptor;
+ mDequeueInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddPerformInterceptor(va_list args) {
+ ANativeWindow_performInterceptor interceptor = va_arg(args, ANativeWindow_performInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mPerformInterceptor = interceptor;
+ mPerformInterceptorData = data;
+ return NO_ERROR;
+}
+
+int Surface::dispatchAddQueueInterceptor(va_list args) {
+ ANativeWindow_queueBufferInterceptor interceptor =
+ va_arg(args, ANativeWindow_queueBufferInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mQueueInterceptor = interceptor;
+ mQueueInterceptorData = data;
+ return NO_ERROR;
+}
+
bool Surface::transformToDisplayInverse() {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 63dc333..7017b7c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -31,8 +31,6 @@
#include <system/graphics.h>
-#include <ui/DisplayInfo.h>
-
#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
@@ -41,6 +39,7 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
#ifndef NO_INPUT
#include <input/InputWindow.h>
@@ -223,8 +222,10 @@
surfaceControlStats
.emplace_back(callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl],
- surfaceStats.acquireTime, surfaceStats.previousReleaseFence,
- surfaceStats.transformHint);
+ transactionStats.latchTime, surfaceStats.acquireTime,
+ transactionStats.presentFence,
+ surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+ surfaceStats.eventStats);
if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
@@ -1236,6 +1237,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eProducerDisconnect;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
@@ -1623,15 +1636,23 @@
return sf->injectVSync(when);
}
-status_t SurfaceComposerClient::getDisplayConfigs(
- const sp<IBinder>& display, Vector<DisplayInfo>* configs)
-{
+status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
+ ui::DisplayState* state) {
+ return ComposerService::getComposerService()->getDisplayState(display, state);
+}
+
+status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+ return ComposerService::getComposerService()->getDisplayInfo(display, info);
+}
+
+status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
+ Vector<DisplayConfig>* configs) {
return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
}
-status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display,
- DisplayInfo* info) {
- Vector<DisplayInfo> configs;
+status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
+ DisplayConfig* config) {
+ Vector<DisplayConfig> configs;
status_t result = getDisplayConfigs(display, &configs);
if (result != NO_ERROR) {
return result;
@@ -1643,7 +1664,7 @@
return NAME_NOT_FOUND;
}
- *info = configs[static_cast<size_t>(activeId)];
+ *config = configs[static_cast<size_t>(activeId)];
return NO_ERROR;
}
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 5cb3593..3e20a37 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -109,20 +109,6 @@
}
/**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(
- Return<void> const& t) {
- return ::android::binder::Status::fromExceptionCode(
- toStatusT(t),
- t.description().c_str());
-}
-
-/**
* \brief Wrap `native_handle_t*` in `hidl_handle`.
*
* \param[in] nh The source `native_handle_t*`.
@@ -1337,57 +1323,6 @@
return unflatten(&(t->surfaceDamage), buffer, size);
}
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
- *
- * If the return value is `true` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-bool wrapAs(
- HGraphicBufferProducer::QueueBufferInput* t,
- native_handle_t** nh,
- BGraphicBufferProducer::QueueBufferInput const& l) {
-
- size_t const baseSize = l.getFlattenedSize();
- std::unique_ptr<uint8_t[]> baseBuffer(
- new (std::nothrow) uint8_t[baseSize]);
- if (!baseBuffer) {
- return false;
- }
-
- size_t const baseNumFds = l.getFdCount();
- std::unique_ptr<int[]> baseFds(
- new (std::nothrow) int[baseNumFds]);
- if (!baseFds) {
- return false;
- }
-
- void* buffer = static_cast<void*>(baseBuffer.get());
- size_t size = baseSize;
- int* fds = baseFds.get();
- size_t numFds = baseNumFds;
- if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
- return false;
- }
-
- void const* constBuffer = static_cast<void const*>(baseBuffer.get());
- size = baseSize;
- int const* constFds = static_cast<int const*>(baseFds.get());
- numFds = baseNumFds;
- if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
- return false;
- }
-
- return true;
-}
/**
* \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
index 2712f42..598c8de 100644
--- a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
using ::android::hardware::Return;
H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
: CBase{base} {
+#else
+ : mBase(base) {
+#endif
}
void H2BProducerListener::onBufferReleased() {
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
index 78dc4e8..56b64b9 100644
--- a/libs/gui/bufferqueue/1.0/WProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -46,5 +46,7 @@
bool LWProducerListener::needsReleaseNotify() {
return static_cast<bool>(mBase->needsReleaseNotify());
}
+void LWProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
} // namespace android
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index e891ec5..0f3ae2e 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -249,6 +249,24 @@
return {};
}
+struct Obituary : public hardware::hidl_death_recipient {
+ wp<B2HGraphicBufferProducer> producer;
+ sp<HProducerListener> listener;
+ HConnectionType apiType;
+ Obituary(const wp<B2HGraphicBufferProducer>& p,
+ const sp<HProducerListener>& l,
+ HConnectionType t)
+ : producer(p), listener(l), apiType(t) {}
+
+ void serviceDied(uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+ sp<B2HGraphicBufferProducer> dr = producer.promote();
+ if (dr != nullptr) {
+ (void)dr->disconnect(apiType);
+ }
+ }
+};
+
Return<void> B2HGraphicBufferProducer::connect(
sp<HProducerListener> const& hListener,
HConnectionType hConnectionType,
@@ -270,6 +288,10 @@
&bOutput),
&hStatus) &&
b2h(bOutput, &hOutput);
+ if (converted) {
+ mObituary = new Obituary(this, hListener, hConnectionType);
+ hListener->linkToDeath(mObituary, 0);
+ }
_hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
return {};
}
@@ -282,6 +304,10 @@
}
HStatus hStatus{};
bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus);
+ if (mObituary != nullptr) {
+ mObituary->listener->unlinkToDeath(mObituary);
+ mObituary.clear();
+ }
return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
}
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
index b81a357..b2bd117 100644
--- a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
using ::android::hardware::Return;
H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
: CBase{base} {
+#else
+ : mBase(base) {
+#endif
}
void H2BProducerListener::onBufferReleased() {
@@ -48,6 +52,9 @@
return static_cast<bool>(mBase);
}
+void H2BProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
+
} // namespace utils
} // namespace V2_0
} // namespace bufferqueue
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index be429fe..d72eb5a 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -33,6 +33,35 @@
class BufferItemConsumer;
+class BLASTBufferItemConsumer : public BufferItemConsumer {
+public:
+ BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp)
+ : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+ mCurrentlyConnected(false),
+ mPreviouslyConnected(false) {}
+
+ void onDisconnect() override;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override
+ REQUIRES(mFrameEventHistoryMutex);
+ void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& gpuCompositionDoneFence,
+ const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+ CompositorTiming compositorTiming, nsecs_t latchTime,
+ nsecs_t dequeueReadyTime) REQUIRES(mFrameEventHistoryMutex);
+ void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
+
+private:
+ uint64_t mCurrentFrameNumber = 0;
+
+ Mutex mFrameEventHistoryMutex;
+ ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mFrameEventHistoryMutex);
+ std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mFrameEventHistoryMutex);
+ bool mCurrentlyConnected GUARDED_BY(mFrameEventHistoryMutex);
+ bool mPreviouslyConnected GUARDED_BY(mFrameEventHistoryMutex);
+};
+
class BLASTBufferQueue
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
@@ -89,7 +118,7 @@
sp<IGraphicBufferConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
- sp<BufferItemConsumer> mBufferItemConsumer;
+ sp<BLASTBufferItemConsumer> mBufferItemConsumer;
SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
};
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 2dec663..cbace5b 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -22,10 +22,15 @@
namespace android {
+class IBinder;
struct BufferSlot;
+#ifndef NO_BINDER
class BufferQueueProducer : public BnGraphicBufferProducer,
private IBinder::DeathRecipient {
+#else
+class BufferQueueProducer : public BnGraphicBufferProducer {
+#endif
public:
friend class BufferQueue; // Needed to access binderDied
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 679d572..fcdf6bf 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -32,7 +32,7 @@
void dispose();
status_t scheduleVsync();
void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
- int getFd();
+ int getFd() const;
virtual int handleEvent(int receiveFd, int events, void* data);
protected:
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index df02494..4af8659 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -174,7 +174,6 @@
std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
};
-
// Used by the consumer to keep track of which fields it already sent to
// the producer.
class FrameEventDirtyFields {
@@ -207,6 +206,7 @@
~ConsumerFrameEventHistory() override;
void onDisconnect();
+ void setProducerWantsEvents();
void initializeCompositorTiming(const CompositorTiming& compositorTiming);
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 046f6e1..0ab2399 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -92,6 +92,7 @@
FrameEventHistoryDelta* /*outDelta*/) {}
};
+#ifndef NO_BINDER
class IConsumerListener : public ConsumerListener, public IInterface {
public:
DECLARE_META_INTERFACE(ConsumerListener)
@@ -105,4 +106,11 @@
uint32_t flags = 0) override;
};
+#else
+class IConsumerListener : public ConsumerListener {
+};
+class BnConsumerListener : public IConsumerListener {
+};
+#endif
+
} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 54f77b4..56fe949 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -35,10 +35,14 @@
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
-
+#ifndef NO_BINDER
class IGraphicBufferConsumer : public IInterface {
public:
DECLARE_META_INTERFACE(GraphicBufferConsumer)
+#else
+class IGraphicBufferConsumer : public RefBase {
+public:
+#endif
enum {
// Returned by releaseBuffer, after which the consumer must free any references to the
@@ -292,6 +296,7 @@
}
};
+#ifndef NO_BINDER
class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
public:
BnGraphicBufferConsumer()
@@ -300,5 +305,9 @@
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags = 0) override;
};
+#else
+class BnGraphicBufferConsumer : public IGraphicBufferConsumer {
+};
+#endif
} // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 680d64e..87989da 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -45,6 +45,13 @@
class NativeHandle;
class Surface;
+using HGraphicBufferProducerV1_0 =
+ ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer;
+using HGraphicBufferProducerV2_0 =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer;
+
/*
* This class defines the Binder IPC interface for the producer side of
* a queue of graphics buffers. It's used to send graphics data from one
@@ -59,20 +66,15 @@
*
* This class was previously called ISurfaceTexture.
*/
-class IGraphicBufferProducer : public IInterface
-{
-public:
- using HGraphicBufferProducerV1_0 =
- ::android::hardware::graphics::bufferqueue::V1_0::
- IGraphicBufferProducer;
- using HGraphicBufferProducerV2_0 =
- ::android::hardware::graphics::bufferqueue::V2_0::
- IGraphicBufferProducer;
-
+#ifndef NO_BINDER
+class IGraphicBufferProducer : public IInterface {
DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
HGraphicBufferProducerV1_0,
HGraphicBufferProducerV2_0)
-
+#else
+class IGraphicBufferProducer : public RefBase {
+#endif
+public:
enum {
// A flag returned by dequeueBuffer when the client needs to call
// requestBuffer immediately thereafter.
@@ -640,6 +642,7 @@
// Sets the apps intended frame rate.
virtual status_t setFrameRate(float frameRate);
+#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer,
@@ -657,10 +660,11 @@
// it writes a strong binder object; for BufferHub, it writes a
// ProducerQueueParcelable object.
virtual status_t exportToParcel(Parcel* parcel);
+#endif
};
// ----------------------------------------------------------------------------
-
+#ifndef NO_BINDER
class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
{
public:
@@ -669,6 +673,10 @@
Parcel* reply,
uint32_t flags = 0);
};
+#else
+class BnGraphicBufferProducer : public IGraphicBufferProducer {
+};
+#endif
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 32a3690..0b1f4b5 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -51,6 +51,7 @@
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
};
+#ifndef NO_BINDER
class IProducerListener : public ProducerListener, public IInterface
{
public:
@@ -73,6 +74,12 @@
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
};
+#else
+class IProducerListener : public ProducerListener {
+};
+class BnProducerListener : public IProducerListener {
+};
+#endif
class DummyProducerListener : public BnProducerListener
{
public:
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 46c9f3a..e860f61 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -46,13 +45,13 @@
#include <vector>
namespace android {
-// ----------------------------------------------------------------------------
struct client_cache_t;
struct ComposerState;
-struct DisplayState;
+struct DisplayConfig;
struct DisplayInfo;
struct DisplayStatInfo;
+struct DisplayState;
struct InputWindowCommands;
class LayerDebugInfo;
class HdrCapabilities;
@@ -63,6 +62,12 @@
class Rect;
enum class FrameEvent;
+namespace ui {
+
+struct DisplayState;
+
+} // namespace ui
+
/*
* This class defines the Binder IPC interface for accessing various
* SurfaceFlinger features.
@@ -161,10 +166,6 @@
*/
virtual void setPowerMode(const sp<IBinder>& display, int mode) = 0;
- /* returns information for each configuration of the given display
- * intended to be used to get information about built-in displays */
- virtual status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs) = 0;
/* returns display statistics for a given display
* intended to be used by the media framework to properly schedule
@@ -172,8 +173,25 @@
virtual status_t getDisplayStats(const sp<IBinder>& display,
DisplayStatInfo* stats) = 0;
- /* indicates which of the configurations returned by getDisplayInfo is
- * currently active */
+ /**
+ * Get transactional state of given display.
+ */
+ virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0;
+
+ /**
+ * Get immutable information about given physical display.
+ */
+ virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
+
+ /**
+ * Get configurations supported by given physical display.
+ */
+ virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+
+ /**
+ * Get the index into configurations returned by getDisplayConfigs,
+ * corresponding to the active configuration.
+ */
virtual int getActiveConfig(const sp<IBinder>& display) = 0;
virtual status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -493,7 +511,7 @@
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
- CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
+ GET_DISPLAY_INFO,
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
@@ -503,7 +521,7 @@
GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
- CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
+ GET_DISPLAY_STATE,
CAPTURE_SCREEN,
CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
@@ -546,8 +564,4 @@
Parcel* reply, uint32_t flags = 0);
};
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_H
+} // namespace android
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 9c15225..c58634b 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -21,6 +21,7 @@
#include <binder/Parcelable.h>
#include <binder/SafeInterface.h>
+#include <gui/FrameTimestamps.h>
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -35,6 +36,27 @@
using CallbackId = int64_t;
+class FrameEventHistoryStats : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ FrameEventHistoryStats() = default;
+ FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
+ nsecs_t refreshTime, nsecs_t dequeueReadyTime)
+ : frameNumber(fn),
+ gpuCompositionDoneFence(gpuCompFence),
+ compositorTiming(compTiming),
+ refreshStartTime(refreshTime),
+ dequeueReadyTime(dequeueReadyTime) {}
+
+ uint64_t frameNumber;
+ sp<Fence> gpuCompositionDoneFence;
+ CompositorTiming compositorTiming;
+ nsecs_t refreshStartTime;
+ nsecs_t dequeueReadyTime;
+};
+
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -42,16 +64,18 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
- uint32_t hint)
+ uint32_t hint, FrameEventHistoryStats frameEventStats)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
- transformHint(hint) {}
+ transformHint(hint),
+ eventStats(frameEventStats) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
+ FrameEventHistoryStats eventStats;
};
class TransactionStats : public Parcelable {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index c256a09..2d53b48 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -102,6 +102,7 @@
eFrameRateSelectionPriority = 0x20'00000000,
eFrameRateChanged = 0x40'00000000,
eBackgroundBlurRadiusChanged = 0x80'00000000,
+ eProducerDisconnect = 0x100'00000000,
};
layer_state_t()
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 86cc61f..0139507 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -21,16 +21,15 @@
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
-
+#include <system/window.h>
#include <ui/ANativeObjectBase.h>
#include <ui/GraphicTypes.h>
#include <ui/Region.h>
-
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
-#include <system/window.h>
+#include <shared_mutex>
namespace android {
@@ -205,6 +204,13 @@
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
+ static int cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+ static int dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+ static int performInternal(ANativeWindow* window, int operation, va_list args);
+ static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
@@ -252,6 +258,10 @@
int dispatchGetLastDequeueDuration(va_list args);
int dispatchGetLastQueueDuration(va_list args);
int dispatchSetFrameRate(va_list args);
+ int dispatchAddCancelInterceptor(va_list args);
+ int dispatchAddDequeueInterceptor(va_list args);
+ int dispatchAddPerformInterceptor(va_list args);
+ int dispatchAddQueueInterceptor(va_list args);
bool transformToDisplayInverse();
protected:
@@ -457,6 +467,18 @@
// member variables are accessed.
mutable Mutex mMutex;
+ // mInterceptorMutex is the mutex guarding interceptors.
+ std::shared_mutex mInterceptorMutex;
+
+ ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
+ void* mCancelInterceptorData = nullptr;
+ ANativeWindow_dequeueBufferInterceptor mDequeueInterceptor = nullptr;
+ void* mDequeueInterceptorData = nullptr;
+ ANativeWindow_performInterceptor mPerformInterceptor = nullptr;
+ void* mPerformInterceptorData = nullptr;
+ ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
+ void* mQueueInterceptorData = nullptr;
+
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 08e6a5a..d0bb6a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -46,29 +45,31 @@
namespace android {
-// ---------------------------------------------------------------------------
-
-struct DisplayInfo;
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
class IRegionSamplingListener;
class Region;
-// ---------------------------------------------------------------------------
-
struct SurfaceControlStats {
- SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
- const sp<Fence>& prevReleaseFence, uint32_t hint)
+ SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
+ const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+ uint32_t hint, FrameEventHistoryStats eventStats)
: surfaceControl(sc),
- acquireTime(time),
+ latchTime(latchTime),
+ acquireTime(acquireTime),
+ presentFence(presentFence),
previousReleaseFence(prevReleaseFence),
- transformHint(hint) {}
+ transformHint(hint),
+ frameEventStats(eventStats) {}
sp<SurfaceControl> surfaceControl;
+ nsecs_t latchTime = -1;
nsecs_t acquireTime = -1;
+ sp<Fence> presentFence;
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
+ FrameEventHistoryStats frameEventStats;
};
using TransactionCompletedCallbackTakesContext =
@@ -102,18 +103,21 @@
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = nullptr, uint32_t flags = 0);
- // Get a list of supported configurations for a given display
- static status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs);
+ // Get transactional state of given display.
+ static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
- // Get the DisplayInfo for the currently-active configuration
- static status_t getDisplayInfo(const sp<IBinder>& display,
- DisplayInfo* info);
+ // Get immutable information about given physical display.
+ static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
- // Get the index of the current active configuration (relative to the list
- // returned by getDisplayInfo)
+ // Get configurations supported by given physical display.
+ static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+
+ // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
static int getActiveConfig(const sp<IBinder>& display);
+ // Shorthand for getDisplayConfigs element at getActiveConfig index.
+ static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
+
// Sets the refresh rate boundaries for display configuration.
// For all other parameters, default configuration is used. The index for the default is
// corresponting to the configs returned from getDisplayConfigs().
@@ -487,6 +491,9 @@
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+ // ONLY FOR BLAST ADAPTER
+ Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
// The child SurfaceControls will not throw exceptions or return errors,
@@ -644,8 +651,4 @@
void onTransactionCompleted(ListenerStats stats) override;
};
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
index 627845c..811dcbe 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -25,8 +25,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <binder/Binder.h>
-#include <binder/Status.h>
#include <ui/FenceTime.h>
#include <cutils/native_handle.h>
#include <gui/IGraphicBufferProducer.h>
@@ -127,15 +125,6 @@
*/
/**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(Return<void> const& t);
-
-/**
* \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
*
* \param[in] t The source `Return<void>`.
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
index 211fdd5..cda5103 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -34,7 +34,12 @@
using BProducerListener = ::android::IProducerListener;
class H2BProducerListener
+#ifndef NO_BINDER
: public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+ : public BProducerListener {
+ sp<HProducerListener> mBase;
+#endif
public:
H2BProducerListener(sp<HProducerListener> const& base);
virtual void onBufferReleased() override;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 029dcc0..99ab085 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -49,7 +49,6 @@
typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
typedef ::android::IProducerListener BProducerListener;
-using ::android::BnGraphicBufferProducer;
#ifndef LOG
struct LOG_dummy {
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
index 51dff5b..197db26 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -20,7 +20,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <binder/IBinder.h>
#include <gui/IProducerListener.h>
#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
@@ -55,6 +54,7 @@
LWProducerListener(sp<HProducerListener> const& base);
void onBufferReleased() override;
bool needsReleaseNotify() override;
+ void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
};
} // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
index 1c58167..16d054b 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -45,6 +45,7 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+struct Obituary;
class B2HGraphicBufferProducer : public HGraphicBufferProducer {
public:
@@ -108,6 +109,7 @@
protected:
sp<BGraphicBufferProducer> mBase;
+ sp<Obituary> mObituary;
};
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
index 898920b..92650b7 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -33,12 +33,20 @@
using BProducerListener = ::android::IProducerListener;
+#ifndef NO_BINDER
class H2BProducerListener
: public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+class H2BProducerListener
+ : public BProducerListener {
+ sp<HProducerListener> mBase;
+
+#endif
public:
H2BProducerListener(sp<HProducerListener> const& base);
virtual void onBufferReleased() override;
virtual bool needsReleaseNotify() override;
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
};
} // namespace utils
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 8fca883..e184c7f 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -21,11 +21,12 @@
#include <android/hardware/graphics/common/1.2/types.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
+#include <gui/FrameTimestamps.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
@@ -103,10 +104,11 @@
t.apply();
t.clear();
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplayToken, &info));
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
+ const ui::Size& resolution = config.resolution;
+ mDisplayWidth = resolution.getWidth();
+ mDisplayHeight = resolution.getHeight();
mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
@@ -114,7 +116,7 @@
/*parent*/ nullptr);
t.setLayerStack(mSurfaceControl, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setFrame(mSurfaceControl, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setFrame(mSurfaceControl, Rect(resolution))
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
.apply();
@@ -237,6 +239,33 @@
ASSERT_EQ(&next, adapter.getNextTransaction());
}
+TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
uint8_t r = 255;
uint8_t g = 0;
@@ -619,4 +648,79 @@
TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_270) {
test(ui::Transform::ROT_270);
}
+
+class BLASTFrameEventHistoryTest : public BLASTBufferQueueTest {
+public:
+ void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
+ nsecs_t* requestedPresentTime, nsecs_t* postedTime,
+ IGraphicBufferProducer::QueueBufferOutput* qbOutput,
+ bool getFrameTimestamps) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ nsecs_t requestedTime = systemTime();
+ if (requestedPresentTime) *requestedPresentTime = requestedTime;
+ IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE, /*sticky*/ 0,
+ getFrameTimestamps);
+ if (postedTime) *postedTime = systemTime();
+ igbProducer->queueBuffer(slot, input, qbOutput);
+ }
+};
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ ProducerFrameEventHistory history;
+ setUpProducer(adapter, igbProducer);
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ nsecs_t requestedPresentTimeA = 0;
+ nsecs_t postedTimeA = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ FrameEvents* events = nullptr;
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ adapter.waitForCallbacks();
+
+ // queue another buffer so we query for frame event deltas
+ nsecs_t requestedPresentTimeB = 0;
+ nsecs_t postedTimeB = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+
+ // frame number, requestedPresentTime, and postTime should not have changed
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ ASSERT_GE(events->latchTime, postedTimeA);
+ ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // we should also have gotten the initial values for the next frame
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+}
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 04749e6..1a623e2 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -41,7 +41,7 @@
#include <input/InputTransport.h>
#include <input/Input.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -223,13 +223,13 @@
const auto display = mComposerClient->getInternalDisplayToken();
ASSERT_NE(display, nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
}
void TearDown() {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 25c032f..70fd888 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -718,8 +718,15 @@
}
void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
- status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
- Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+ status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
+ return NO_ERROR;
+ }
+ status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+ return NO_ERROR;
+ }
+ status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
+ return NO_ERROR;
+ }
status_t getDisplayStats(const sp<IBinder>& /*display*/,
DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8ccbc7f..85b0fd0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -57,16 +57,19 @@
return "UNKNOWN";
}
-void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
+void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac) {
mDeviceId = deviceId;
mSource = source;
mDisplayId = displayId;
+ mHmac = hmac;
}
void InputEvent::initialize(const InputEvent& from) {
mDeviceId = from.mDeviceId;
mSource = from.mSource;
mDisplayId = from.mDisplayId;
+ mHmac = from.mHmac;
}
// --- KeyEvent ---
@@ -79,19 +82,11 @@
return getKeyCodeByLabel(label);
}
-void KeyEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- InputEvent::initialize(deviceId, source, displayId);
+void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime, nsecs_t eventTime) {
+ InputEvent::initialize(deviceId, source, displayId, hmac);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
@@ -250,15 +245,16 @@
// --- MotionEvent ---
-void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags,
- int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xOffset, float yOffset,
- float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- size_t pointerCount, const PointerProperties* pointerProperties,
+void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, float xScale,
+ float yScale, float xOffset, float yOffset, float xPrecision,
+ float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
- InputEvent::initialize(deviceId, source, displayId);
+ InputEvent::initialize(deviceId, source, displayId, hmac);
mAction = action;
mActionButton = actionButton;
mFlags = flags;
@@ -266,6 +262,8 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
+ mXScale = xScale;
+ mYScale = yScale;
mXOffset = xOffset;
mYOffset = yOffset;
mXPrecision = xPrecision;
@@ -281,7 +279,7 @@
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
- InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
+ InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac);
mAction = other->mAction;
mActionButton = other->mActionButton;
mFlags = other->mFlags;
@@ -289,6 +287,8 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
+ mXScale = other->mXScale;
+ mYScale = other->mYScale;
mXOffset = other->mXOffset;
mYOffset = other->mYOffset;
mXPrecision = other->mXPrecision;
@@ -321,17 +321,17 @@
float MotionEvent::getXCursorPosition() const {
const float rawX = getRawXCursorPosition();
- return rawX + mXOffset;
+ return rawX * mXScale + mXOffset;
}
float MotionEvent::getYCursorPosition() const {
const float rawY = getRawYCursorPosition();
- return rawY + mYOffset;
+ return rawY * mYScale + mYOffset;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -346,9 +346,9 @@
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -368,9 +368,9 @@
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -442,11 +442,11 @@
float oldXOffset = mXOffset;
float oldYOffset = mYOffset;
float newX, newY;
- float rawX = getRawX(0);
- float rawY = getRawY(0);
- transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
- mXOffset = newX - rawX;
- mYOffset = newY - rawY;
+ float scaledRawX = getRawX(0) * mXScale;
+ float scaledRawY = getRawY(0) * mYScale;
+ transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+ mXOffset = newX - scaledRawX;
+ mYOffset = newY - scaledRawY;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
@@ -455,22 +455,22 @@
// Apply the transformation to cursor position.
if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition + oldXOffset;
- float y = mRawYCursorPosition + oldYOffset;
+ float x = mRawXCursorPosition * mXScale + oldXOffset;
+ float y = mRawYCursorPosition * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
@@ -488,8 +488,14 @@
}
mDeviceId = parcel->readInt32();
- mSource = parcel->readInt32();
+ mSource = parcel->readUint32();
mDisplayId = parcel->readInt32();
+ std::vector<uint8_t> hmac;
+ status_t result = parcel->readByteVector(&hmac);
+ if (result != OK || hmac.size() != 32) {
+ return BAD_VALUE;
+ }
+ std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin());
mAction = parcel->readInt32();
mActionButton = parcel->readInt32();
mFlags = parcel->readInt32();
@@ -497,6 +503,8 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
+ mXScale = parcel->readFloat();
+ mYScale = parcel->readFloat();
mXOffset = parcel->readFloat();
mYOffset = parcel->readFloat();
mXPrecision = parcel->readFloat();
@@ -541,8 +549,10 @@
parcel->writeInt32(sampleCount);
parcel->writeInt32(mDeviceId);
- parcel->writeInt32(mSource);
+ parcel->writeUint32(mSource);
parcel->writeInt32(mDisplayId);
+ std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
+ parcel->writeByteVector(hmac);
parcel->writeInt32(mAction);
parcel->writeInt32(mActionButton);
parcel->writeInt32(mFlags);
@@ -550,6 +560,8 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
+ parcel->writeFloat(mXScale);
+ parcel->writeFloat(mYScale);
parcel->writeFloat(mXOffset);
parcel->writeFloat(mYOffset);
parcel->writeFloat(mXPrecision);
@@ -578,7 +590,7 @@
}
#endif
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
if (source & AINPUT_SOURCE_CLASS_POINTER) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
@@ -607,7 +619,7 @@
void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE);
+ ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
mInTouchMode = inTouchMode;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d53a557..d25a5cc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -147,6 +147,8 @@
msg->body.key.source = body.key.source;
// int32_t displayId
msg->body.key.displayId = body.key.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.key.hmac = body.key.hmac;
// int32_t action
msg->body.key.action = body.key.action;
// int32_t flags
@@ -174,6 +176,8 @@
msg->body.motion.source = body.motion.source;
// int32_t displayId
msg->body.motion.displayId = body.motion.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.motion.hmac = body.motion.hmac;
// int32_t action
msg->body.motion.action = body.motion.action;
// int32_t actionButton
@@ -190,6 +194,10 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
+ // float xScale
+ msg->body.motion.xScale = body.motion.xScale;
+ // float yScale
+ msg->body.motion.yScale = body.motion.yScale;
// float xOffset
msg->body.motion.xOffset = body.motion.xOffset;
// float yOffset
@@ -424,19 +432,11 @@
InputPublisher::~InputPublisher() {
}
-status_t InputPublisher::publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
+status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source,
+ int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime, nsecs_t eventTime) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
mChannel->getName().c_str(), keyCode);
@@ -461,6 +461,7 @@
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.displayId = displayId;
+ msg.body.key.hmac = hmac;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
@@ -473,11 +474,12 @@
}
status_t InputPublisher::publishMotionEvent(
- uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xOffset, float yOffset,
- float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, float xScale, float yScale, float xOffset,
+ float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
@@ -489,13 +491,14 @@
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
+ "xOffset=%.1f, yOffset=%.1f, "
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32,
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xOffset, yOffset, xPrecision,
- yPrecision, downTime, eventTime, pointerCount);
+ motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, downTime, eventTime, pointerCount);
}
if (!seq) {
@@ -515,6 +518,7 @@
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.displayId = displayId;
+ msg.body.motion.hmac = hmac;
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
@@ -522,6 +526,8 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
+ msg.body.motion.xScale = xScale;
+ msg.body.motion.yScale = yScale;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
@@ -1136,18 +1142,10 @@
}
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
- event->initialize(
- msg->body.key.deviceId,
- msg->body.key.source,
- msg->body.key.displayId,
- msg->body.key.action,
- msg->body.key.flags,
- msg->body.key.keyCode,
- msg->body.key.scanCode,
- msg->body.key.metaState,
- msg->body.key.repeatCount,
- msg->body.key.downTime,
- msg->body.key.eventTime);
+ event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId,
+ msg->body.key.hmac, msg->body.key.action, msg->body.key.flags,
+ msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState,
+ msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime);
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
@@ -1164,15 +1162,15 @@
}
event->initialize(msg->body.motion.deviceId, msg->body.motion.source,
- msg->body.motion.displayId, msg->body.motion.action,
+ msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
msg->body.motion.edgeFlags, msg->body.motion.metaState,
msg->body.motion.buttonState, msg->body.motion.classification,
- msg->body.motion.xOffset, msg->body.motion.yOffset,
- msg->body.motion.xPrecision, msg->body.motion.yPrecision,
- msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
+ msg->body.motion.yOffset, msg->body.motion.xPrecision,
+ msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
+ msg->body.motion.yCursorPosition, msg->body.motion.downTime,
+ msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e189d20..6f9b162 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -487,9 +487,9 @@
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
outEvents.push();
KeyEvent& event = outEvents.editTop();
- event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- 0, keyCode, 0, metaState, 0, time, time);
+ event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
+ 0, time, time);
}
void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b90857c..dce1f29 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,7 +26,12 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
-class BaseTest : public testing::Test { };
+class BaseTest : public testing::Test {
+protected:
+ static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+};
// --- PointerCoordsTest ---
@@ -176,16 +181,17 @@
KeyEvent event;
// Initialize and get properties.
- const nsecs_t ARBITRARY_DOWN_TIME = 1;
- const nsecs_t ARBITRARY_EVENT_TIME = 2;
- event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
- AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
+ constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
ASSERT_EQ(2, event.getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource());
ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
+ EXPECT_EQ(HMAC, event.getHmac());
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,7 +203,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set display id.
constexpr int32_t newDisplayId = 2;
@@ -210,19 +216,17 @@
class MotionEventTest : public BaseTest {
protected:
- static const nsecs_t ARBITRARY_DOWN_TIME;
- static const nsecs_t ARBITRARY_EVENT_TIME;
- static const float X_OFFSET;
- static const float Y_OFFSET;
+ static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ static constexpr float X_SCALE = 2.0;
+ static constexpr float Y_SCALE = 3.0;
+ static constexpr float X_OFFSET = 1;
+ static constexpr float Y_OFFSET = 1.1;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
PointerProperties pointerProperties[2];
@@ -254,12 +258,13 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
+ event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0,
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP,
AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
- X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME,
- ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords);
+ X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
+ pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -306,14 +311,17 @@
// Check properties.
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
ASSERT_EQ(2, event->getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
+ EXPECT_EQ(HMAC, event->getHmac());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
+ EXPECT_EQ(X_SCALE, event->getXScale());
+ EXPECT_EQ(Y_SCALE, event->getYScale());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -367,19 +375,19 @@
ASSERT_EQ(211, event->getRawY(0));
ASSERT_EQ(221, event->getRawY(1));
- ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+ ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+ ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+ ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+ ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+ ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+ ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
- ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+ ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+ ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+ ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+ ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+ ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+ ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -440,7 +448,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set displayId.
constexpr int32_t newDisplayId = 2;
@@ -505,8 +513,8 @@
ASSERT_EQ(210 * 2, event.getRawX(0));
ASSERT_EQ(211 * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+ ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+ ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -570,12 +578,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/,
- 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/,
- 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
- pointerProperties, pointerCoords);
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+ 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
+ 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -634,9 +643,10 @@
}
for (MotionClassification classification : classifications) {
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
@@ -654,9 +664,10 @@
pointerCoords[i].clear();
}
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0,
- 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0,
- 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 2fc77e9..d4bbf6c 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -73,8 +73,11 @@
constexpr uint32_t seq = 15;
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+ constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+ 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
constexpr int32_t keyCode = AKEYCODE_ENTER;
@@ -84,8 +87,9 @@
constexpr nsecs_t downTime = 3;
constexpr nsecs_t eventTime = 4;
- status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
- keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+ status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags,
+ keyCode, scanCode, metaState, repeatCount, downTime,
+ eventTime);
ASSERT_EQ(OK, status)
<< "publisher publishKeyEvent should return OK";
@@ -105,6 +109,7 @@
EXPECT_EQ(deviceId, keyEvent->getDeviceId());
EXPECT_EQ(source, keyEvent->getSource());
EXPECT_EQ(displayId, keyEvent->getDisplayId());
+ EXPECT_EQ(hmac, keyEvent->getHmac());
EXPECT_EQ(action, keyEvent->getAction());
EXPECT_EQ(flags, keyEvent->getFlags());
EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -134,8 +139,11 @@
constexpr uint32_t seq = 15;
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t actionButton = 0;
constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -143,6 +151,8 @@
constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+ constexpr float xScale = 2;
+ constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
constexpr float xPrecision = 0.25;
@@ -171,12 +181,12 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
- status =
- mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState, classification,
- xOffset, yOffset, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action,
+ actionButton, flags, edgeFlags, metaState, buttonState,
+ classification, xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, downTime, eventTime, pointerCount,
+ pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -196,18 +206,23 @@
EXPECT_EQ(deviceId, motionEvent->getDeviceId());
EXPECT_EQ(source, motionEvent->getSource());
EXPECT_EQ(displayId, motionEvent->getDisplayId());
+ EXPECT_EQ(hmac, motionEvent->getHmac());
EXPECT_EQ(action, motionEvent->getAction());
EXPECT_EQ(flags, motionEvent->getFlags());
EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
+ EXPECT_EQ(xScale, motionEvent->getXScale());
+ EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(xOffset, motionEvent->getXOffset());
+ EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+ EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -222,10 +237,10 @@
motionEvent->getRawX(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
- motionEvent->getY(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+ motionEvent->getX(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+ motionEvent->getY(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
motionEvent->getPressure(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
@@ -316,11 +331,12 @@
pointerCoords[i].clear();
}
- status =
- mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -331,11 +347,12 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
- status =
- mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -351,11 +368,12 @@
pointerCoords[i].clear();
}
- status =
- mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 9ab0dba..aa8a2d4 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -39,35 +39,39 @@
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
CHECK_OFFSET(InputMessage::Body::Key, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Key, action, 28);
- CHECK_OFFSET(InputMessage::Body::Key, flags, 32);
- CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36);
- CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40);
- CHECK_OFFSET(InputMessage::Body::Key, metaState, 44);
- CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48);
- CHECK_OFFSET(InputMessage::Body::Key, downTime, 56);
+ CHECK_OFFSET(InputMessage::Body::Key, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Key, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Key, flags, 64);
+ CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68);
+ CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72);
+ CHECK_OFFSET(InputMessage::Body::Key, metaState, 76);
+ CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
+ CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Motion, action, 28);
- CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32);
- CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
- CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
- CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
- CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
- CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
- CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
- CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80);
- CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Motion, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64);
+ CHECK_OFFSET(InputMessage::Body::Motion, flags, 68);
+ CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
+ CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
+ CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+ CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
+ CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
+ CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
@@ -86,7 +90,7 @@
* the Motion type, where "pointerCount" variable affects the size and can change at runtime.
*/
void TestBodySize() {
- static_assert(sizeof(InputMessage::Body::Key) == 64);
+ static_assert(sizeof(InputMessage::Body::Key) == 96);
static_assert(sizeof(InputMessage::Body::Motion) ==
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 968e2fa..731eb6a 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,11 +176,11 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+ action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+ AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/,
+ 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 15d937e..0f7e2fb 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -60,7 +60,7 @@
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
- void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb);
+ void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
enum {
MSG_SCHEDULE_CALLBACKS = 0,
@@ -152,21 +152,34 @@
void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
{
AutoMutex _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
}
}
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) {
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+ void* data) {
{
AutoMutex _l{mLock};
mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
mRefreshRateCallbacks.end(),
[&](const RefreshRateCallback& callback) {
- return cb == callback.callback;
- }));
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
if (mRefreshRateCallbacks.empty()) {
toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
+ // If callbacks are empty then clear out the most recently seen
+ // vsync period so that when another callback is registered then the
+ // up-to-date refresh rate can be communicated to the app again.
+ mVsyncPeriod = 0;
}
}
}
@@ -224,9 +237,9 @@
// on every single configuration change.
if (mVsyncPeriod != vsyncPeriod) {
cb.callback(vsyncPeriod, cb.data);
- mVsyncPeriod = vsyncPeriod;
}
}
+ mVsyncPeriod = vsyncPeriod;
}
}
@@ -251,6 +264,11 @@
return reinterpret_cast<Choreographer*>(choreographer);
}
+static inline const Choreographer* AChoreographer_to_Choreographer(
+ const AChoreographer* choreographer) {
+ return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
return reinterpret_cast<AChoreographer*>(choreographer);
}
@@ -285,8 +303,9 @@
AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
}
void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback callback) {
- AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback);
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
}
AChoreographer* AChoreographer_create() {
@@ -307,7 +326,7 @@
delete AChoreographer_to_Choreographer(choreographer);
}
-int AChoreographer_getFd(AChoreographer* choreographer) {
+int AChoreographer_getFd(const AChoreographer* choreographer) {
return AChoreographer_to_Choreographer(choreographer)->getFd();
}
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 1e25049..277635c 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -16,6 +16,7 @@
#include <apex/display.h>
#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
@@ -116,17 +117,12 @@
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
namespace {
+
sp<IBinder> getToken(ADisplay* display) {
DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
}
-int64_t computeSfOffset(const DisplayInfo& info) {
- // This should probably be part of the config instead of extrapolated from
- // the presentation deadline and fudged here, but the way the math works out
- // here we do get the right offset.
- return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
-}
} // namespace
namespace android {
@@ -142,9 +138,16 @@
int numConfigs = 0;
for (int i = 0; i < size; ++i) {
const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
- Vector<DisplayInfo> configs;
- const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
- if (status != OK) {
+
+ DisplayInfo info;
+ if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+ status != OK) {
+ return status;
+ }
+
+ Vector<DisplayConfig> configs;
+ if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ status != OK) {
return status;
}
if (configs.empty()) {
@@ -154,11 +157,11 @@
numConfigs += configs.size();
configsPerDisplay[i].reserve(configs.size());
for (int j = 0; j < configs.size(); ++j) {
- const DisplayInfo config = configs[j];
+ const DisplayConfig& config = configs[j];
configsPerDisplay[i].emplace_back(
- DisplayConfigImpl{static_cast<int32_t>(config.w),
- static_cast<int32_t>(config.h), config.density, config.fps,
- computeSfOffset(config), config.appVsyncOffset});
+ DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
+ info.density, config.refreshRate, config.sfVsyncOffset,
+ config.appVsyncOffset});
}
}
diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
index 0d97e8c..5fd3de9 100644
--- a/libs/nativedisplay/include/android/choreographer.h
+++ b/libs/nativedisplay/include/android/choreographer.h
@@ -54,6 +54,13 @@
*/
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
#if __ANDROID_API__ >= 24
/**
@@ -102,6 +109,42 @@
#endif /* __ANDROID_API__ >= 29 */
+#if __ANDROID_API__ >= 30
+
+/**
+ * Registers a callback to be run when the display refresh rate changes. The
+ * data pointer provided will be passed to the callback function when it's
+ * called. The same callback may be registered multiple times, provided that a
+ * different data pointer is provided each time.
+ *
+ * If an application registers a callback for this choreographer instance when
+ * no new callbacks were previously registered, that callback is guaranteed to
+ * be dispatched. However, if the callback and associated data pointer are
+ * unregistered prior to running the callback, then the callback may be silently
+ * dropped.
+ *
+ * This api is thread-safe. Any thread is allowed to register a new refresh
+ * rate callback for the choreographer instance.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes, along
+ * with the data pointer previously provided when registering the callback. The
+ * callback is only unregistered when the data pointer matches one that was
+ * previously registered.
+ *
+ * This api is thread-safe. Any thread is allowed to unregister an existing
+ * refresh rate callback for the choreographer instance. When a refresh rate
+ * callback and associated data pointer are unregistered, then there is a
+ * guarantee that when the unregistration completes that that callback will not
+ * be run with the data pointer passed.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback, void* data);
+#endif /* __ANDROID_API__ >= 30 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
index 5251fd3..683abc4 100644
--- a/libs/nativedisplay/include/apex/choreographer.h
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -22,25 +22,6 @@
__BEGIN_DECLS
/**
- * Prototype of the function that is called when the display refresh rate
- * changes. It's passed the new vsync period in nanoseconds, as well as the data
- * pointer provided by the application that registered a callback.
- */
-typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
-
-/**
- * Registers a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
-
-/**
- * Unregisters a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback);
-
-/**
* Creates an instance of AChoreographer.
*
* The key differences between this method and AChoreographer_getInstance are:
@@ -62,7 +43,7 @@
* events. One such way is registering the file descriptor to a Looper instance,
* although this is not a requirement.
*/
-int AChoreographer_getFd(AChoreographer* choreographer);
+int AChoreographer_getFd(const AChoreographer* choreographer);
/**
* Provides a callback to handle all pending events emitted by this
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index a60bc4d..a1c9eb8 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -304,3 +304,34 @@
int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
}
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
+
+void ANativeWindow_allocateBuffers(ANativeWindow* window) {
+ window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+ return query64(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID);
+}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 869b22e..02b886c 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -17,12 +17,159 @@
#pragma once
#include <nativebase/nativebase.h>
+#include <stdarg.h>
// apex is a superset of the NDK
#include <android/native_window.h>
__BEGIN_DECLS
+/*
+ * perform bits that can be used with ANativeWindow_perform()
+ *
+ * This is only to support the intercepting methods below - these should notbe
+ * used directly otherwise.
+ */
+enum ANativeWindowPerform {
+ // clang-format off
+ ANATIVEWINDOW_PERFORM_SET_USAGE = 0,
+ ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY = 5,
+ ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT = 9,
+ ANATIVEWINDOW_PERFORM_SET_USAGE64 = 30,
+ // clang-format on
+};
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_cancelBuffer is called.
+ */
+typedef int (*ANativeWindow_cancelBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_cancelBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_cancelBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_cancelBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_cancelBufferFn cancelBuffer,
+ void* data, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_dequeueBuffer is called.
+ */
+typedef int (*ANativeWindow_dequeueBufferFn)(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_dequeueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_dequeueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_dequeueBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_dequeueBufferFn dequeueBuffer,
+ void* data, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_perform is called.
+ */
+typedef int (*ANativeWindow_performFn)(ANativeWindow* window, int operation, va_list args);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_performFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_performFn if it were to be called.
+ */
+typedef int (*ANativeWindow_performInterceptor)(ANativeWindow* window,
+ ANativeWindow_performFn perform, void* data,
+ int operation, va_list args);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_queueBuffer is called.
+ */
+typedef int (*ANativeWindow_queueBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queueBufferInterceptor)(ANativeWindow* window,
+ ANativeWindow_queueBufferFn queueBuffer,
+ void* data, ANativeWindowBuffer* buffer,
+ int fenceFd);
+
+/**
+ * Registers an interceptor for ANativeWindow_cancelBuffer. Instead of calling
+ * the underlying cancelBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying cancelBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_cancelBufferInterceptor interceptor,
+ void* data);
+
+/**
+ * Registers an interceptor for ANativeWindow_dequeueBuffer. Instead of calling
+ * the underlying dequeueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying dequeueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_dequeueBufferInterceptor interceptor,
+ void* data);
+/**
+ * Registers an interceptor for ANativeWindow_perform. Instead of calling
+ * the underlying perform function, instead the provided interceptor is
+ * called, which may optionally call the underlying perform function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+ ANativeWindow_performInterceptor interceptor, void* data);
+/**
+ * Registers an interceptor for ANativeWindow_queueBuffer. Instead of calling
+ * the underlying queueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying queueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+ ANativeWindow_queueBufferInterceptor interceptor,
+ void* data);
+
/**
* Retrieves how long it took for the last time a buffer was dequeued.
*
@@ -53,8 +200,23 @@
* made by the window will return -ETIMEDOUT after the timeout if the dequeue
* takes too long.
*
- * \return NO_ERROR on succes, -errno on error.
+ * \return NO_ERROR on success, -errno on error.
*/
int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if a private API disallows allocation of new
+ * buffers. As such no success/error status is returned.
+ */
+void ANativeWindow_allocateBuffers(ANativeWindow* window);
+
+/**
+ * Retrieves an identifier for the next frame to be queued by this window.
+ *
+ * \return -errno on error, otherwise returns the next frame id.
+ */
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window);
+
__END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 14f7214..121374b 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -207,16 +207,16 @@
*/
enum {
// clang-format off
- NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */
+ NATIVE_WINDOW_SET_USAGE = ANATIVEWINDOW_PERFORM_SET_USAGE, /* deprecated */
NATIVE_WINDOW_CONNECT = 1, /* deprecated */
NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
NATIVE_WINDOW_SET_CROP = 3, /* private */
NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
- NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY, /* deprecated */
NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
- NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
+ NATIVE_WINDOW_SET_BUFFERS_FORMAT = ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT,
NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
NATIVE_WINDOW_LOCK = 11, /* private */
NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
@@ -237,7 +237,7 @@
NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
- NATIVE_WINDOW_SET_USAGE64 = 30,
+ NATIVE_WINDOW_SET_USAGE64 = ANATIVEWINDOW_PERFORM_SET_USAGE64,
NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
@@ -248,6 +248,11 @@
NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION = 38, /* private */
NATIVE_WINDOW_GET_LAST_QUEUE_DURATION = 39, /* private */
NATIVE_WINDOW_SET_FRAME_RATE = 40,
+ NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR = 41, /* private */
+ NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR = 42, /* private */
+ NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR = 43, /* private */
+ NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR = 44, /* private */
+ NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
// clang-format on
};
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 3002da2..e0e20c3 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -17,6 +17,7 @@
ANativeWindow_OemStorageGet; # llndk
ANativeWindow_OemStorageSet; # llndk
ANativeWindow_acquire;
+ ANativeWindow_allocateBuffers; # apex # introduced=30
ANativeWindow_cancelBuffer; # llndk
ANativeWindow_dequeueBuffer; # llndk
ANativeWindow_getBuffersDataSpace; # introduced=28
@@ -25,11 +26,16 @@
ANativeWindow_getLastDequeueDuration; # apex # introduced=30
ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
ANativeWindow_getLastQueueDuration; # apex # introduced=30
+ ANativeWindow_getNextFrameId; # apex # introduced=30
ANativeWindow_getWidth;
ANativeWindow_lock;
ANativeWindow_query; # llndk
ANativeWindow_queryf; # llndk
ANativeWindow_queueBuffer; # llndk
+ ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30
+ ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30
+ ANativeWindow_setPerformInterceptor; # apex # introduced=30
+ ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30
ANativeWindow_release;
ANativeWindow_setAutoPrerotation; # llndk
ANativeWindow_setAutoRefresh; # llndk
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 2e3ab4c..3d77059 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,13 +52,14 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
+ "gl/GLShadowTexture.cpp",
"gl/GLShadowVertexGenerator.cpp",
"gl/GLSkiaShadowPort.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
"gl/filters/BlurFilter.cpp",
- "gl/filters/LensBlurFilter.cpp",
+ "gl/filters/KawaseBlurFilter.cpp",
"gl/filters/GaussianBlurFilter.cpp",
"gl/filters/GenericProgram.cpp",
],
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e257704..e11b59f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -51,7 +51,7 @@
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
#include "filters/GaussianBlurFilter.h"
-#include "filters/LensBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -285,6 +285,9 @@
// now figure out what version of GL did we actually get
GlesVersion version = parseGlesVersion(extensions.getVersion());
+ LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
+ "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
+
// initialize the renderer while GL is current
std::unique_ptr<GLESRenderEngine> engine;
switch (version) {
@@ -428,11 +431,11 @@
if (args.supportsBackgroundBlur) {
char isGaussian[PROPERTY_VALUE_MAX];
- property_get("debug.sf.gaussianBlur", isGaussian, "1");
+ property_get("debug.sf.gaussianBlur", isGaussian, "0");
if (atoi(isGaussian)) {
mBlurFilter = new GaussianBlurFilter(*this);
} else {
- mBlurFilter = new LensBlurFilter(*this);
+ mBlurFilter = new KawaseBlurFilter(*this);
}
checkErrors("BlurFilter creation");
}
@@ -981,18 +984,19 @@
}
std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
- // Let's find the topmost layer requesting background blur (if any.)
- // Blurs in multiple layers are not supported, given the cost of the shader.
- const LayerSettings* blurLayer = nullptr;
+ // Gathering layers that requested blur, we'll need them to decide when to render to an
+ // offscreen buffer, and when to render to the native buffer.
+ std::deque<const LayerSettings*> blurLayers;
if (CC_LIKELY(mBlurFilter != nullptr)) {
- for (auto const layer : layers) {
+ for (auto layer : layers) {
if (layer->backgroundBlurRadius > 0) {
- blurLayer = layer;
+ blurLayers.push_back(layer);
}
}
}
+ const auto blurLayersSize = blurLayers.size();
- if (blurLayer == nullptr) {
+ if (blurLayersSize == 0) {
fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
if (fbo->getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
@@ -1003,7 +1007,8 @@
setViewportAndProjection(display.physicalDisplay, display.clip);
} else {
setViewportAndProjection(display.physicalDisplay, display.clip);
- auto status = mBlurFilter->setAsDrawTarget(display);
+ auto status =
+ mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
if (status != NO_ERROR) {
ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1036,8 +1041,10 @@
.setCropCoords(2 /* size */)
.build();
for (auto const layer : layers) {
- if (blurLayer == layer) {
- auto status = mBlurFilter->prepare(layer->backgroundBlurRadius);
+ if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+ blurLayers.pop_front();
+
+ auto status = mBlurFilter->prepare();
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1045,18 +1052,26 @@
return status;
}
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
- useFramebufferCache);
- status = fbo->getStatus();
+ if (blurLayers.size() == 0) {
+ // Done blurring, time to bind the native FBO and render our blur onto it.
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ useFramebufferCache);
+ status = fbo->getStatus();
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ // There's still something else to blur, so let's keep rendering to our FBO
+ // instead of to the display.
+ status = mBlurFilter->setAsDrawTarget(display,
+ blurLayers.front()->backgroundBlurRadius);
+ }
if (status != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->handle);
checkErrors("Can't bind native framebuffer");
return status;
}
- setViewportAndProjection(display.physicalDisplay, display.clip);
- status = mBlurFilter->render();
+ status = mBlurFilter->render(blurLayersSize > 1);
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1661,6 +1676,7 @@
mState.cornerRadius = 0.0f;
mState.drawShadows = true;
+ setupLayerTexturing(mShadowTexture.getTexture());
drawMesh(mesh);
mState.drawShadows = false;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 45c85de..ebf78fe 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -32,6 +32,7 @@
#include <renderengine/RenderEngine.h>
#include <renderengine/private/Description.h>
#include <sys/types.h>
+#include "GLShadowTexture.h"
#include "ImageManager.h"
#define EGL_NO_CONFIG ((EGLConfig)0)
@@ -183,6 +184,7 @@
GLuint mVpWidth;
GLuint mVpHeight;
Description mState;
+ GLShadowTexture mShadowTexture;
mat4 mSrgbToXyz;
mat4 mDisplayP3ToXyz;
@@ -260,7 +262,7 @@
friend class GLFramebuffer;
friend class BlurFilter;
friend class GaussianBlurFilter;
- friend class LensBlurFilter;
+ friend class KawaseBlurFilter;
friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 091eac9..153935b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -122,6 +122,14 @@
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
}
+void GLFramebuffer::bindAsReadBuffer() const {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
void GLFramebuffer::unbind() const {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 668685a..69102d6 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -48,6 +48,8 @@
int32_t getBufferWidth() const { return mBufferWidth; }
GLenum getStatus() const { return mStatus; }
void bind() const;
+ void bindAsReadBuffer() const;
+ void bindAsDrawBuffer() const;
void unbind() const;
private:
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
new file mode 100644
index 0000000..2423a34
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include "GLShadowTexture.h"
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowTexture::GLShadowTexture() {
+ fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
+
+ glGenTextures(1, &mName);
+ glBindTexture(GL_TEXTURE_2D, mName);
+ glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
+ SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
+ mTexture.init(Texture::TEXTURE_2D, mName);
+ mTexture.setFiltering(true);
+ mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
+}
+
+GLShadowTexture::~GLShadowTexture() {
+ glDeleteTextures(1, &mName);
+}
+
+const Texture& GLShadowTexture::getTexture() {
+ return mTexture;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
new file mode 100644
index 0000000..250a9d7
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <renderengine/Texture.h>
+#include <cstdint>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLShadowTexture {
+public:
+ GLShadowTexture();
+ ~GLShadowTexture();
+
+ const Texture& getTexture();
+
+private:
+ static constexpr int SHADOW_TEXTURE_WIDTH = 128;
+ static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
+
+ GLuint mName;
+ Texture mTexture;
+ uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
index 224ce6c..da8b435 100644
--- a/libs/renderengine/gl/GLSkiaShadowPort.cpp
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -644,6 +644,13 @@
2.0f * devSpaceSpotBlur, std::abs(insetWidth));
}
+void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
+ for (int i = 0; i < shadowTextureWidth; i++) {
+ const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
+ data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
+ }
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
index e7d1861..912c8bb 100644
--- a/libs/renderengine/gl/GLSkiaShadowPort.h
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -17,13 +17,11 @@
#pragma once
#include <math/vec4.h>
+#include <renderengine/Mesh.h>
#include <ui/Rect.h>
namespace android {
namespace renderengine {
-
-class Mesh;
-
namespace gl {
/**
@@ -79,6 +77,20 @@
void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
int startingVertexOffset, uint16_t* indices);
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Values are determined by an exponential falloff
+ * function provided by UX.
+ *
+ * The texture is used for quick lookup in theshadow shader.
+ *
+ * textureData - filled with shadow texture data that needs to be at least of
+ * size textureWidth
+ *
+ * textureWidth - width of the texture, height is always 1
+ */
+void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index ba0e4ad..3ae35ec 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -550,7 +550,7 @@
String8 ProgramCache::generateVertexShader(const Key& needs) {
Formatter vs;
- if (needs.isTexturing()) {
+ if (needs.hasTextureCoords()) {
vs << "attribute vec4 texCoords;"
<< "varying vec2 outTexCoords;";
}
@@ -559,16 +559,16 @@
vs << "varying lowp vec2 outCropCoords;";
}
if (needs.drawShadows()) {
- vs << "attribute vec4 shadowColor;";
- vs << "varying vec4 outShadowColor;";
- vs << "attribute vec4 shadowParams;";
- vs << "varying vec3 outShadowParams;";
+ vs << "attribute lowp vec4 shadowColor;";
+ vs << "varying lowp vec4 outShadowColor;";
+ vs << "attribute lowp vec4 shadowParams;";
+ vs << "varying lowp vec3 outShadowParams;";
}
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
<< "void main(void) {" << indent << "gl_Position = projection * position;";
- if (needs.isTexturing()) {
+ if (needs.hasTextureCoords()) {
vs << "outTexCoords = (texture * texCoords).st;";
}
if (needs.hasRoundedCorners()) {
@@ -592,11 +592,13 @@
fs << "precision mediump float;";
if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "uniform samplerExternalOES sampler;"
- << "varying vec2 outTexCoords;";
+ fs << "uniform samplerExternalOES sampler;";
} else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
- fs << "uniform sampler2D sampler;"
- << "varying vec2 outTexCoords;";
+ fs << "uniform sampler2D sampler;";
+ }
+
+ if (needs.hasTextureCoords()) {
+ fs << "varying vec2 outTexCoords;";
}
if (needs.hasRoundedCorners()) {
@@ -625,19 +627,17 @@
if (needs.drawShadows()) {
fs << R"__SHADER__(
- varying vec4 outShadowColor;
- varying vec3 outShadowParams;
+ varying lowp vec4 outShadowColor;
+ varying lowp vec3 outShadowParams;
/**
* Returns the shadow color.
*/
vec4 getShadowColor()
{
- // exponential falloff function provided by UX
- float d = length(outShadowParams.xy);
- float distance = outShadowParams.z * (1.0 - d);
- float factor = 1.0 - clamp(distance, 0.0, 1.0);
- factor = exp(-factor * factor * 4.0) - 0.018;
+ lowp float d = length(outShadowParams.xy);
+ vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
+ lowp float factor = texture2D(sampler, uv).a;
return outShadowColor * factor;
}
)__SHADER__";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index c8b6da7..901e631 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -128,6 +128,7 @@
}
inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+ inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index a18a999..b1a84bd 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,29 +31,24 @@
namespace gl {
BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) {
- mSimpleProgram.compile(getVertexShader(), getSimpleFragShader());
- mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition");
- mSUvLoc = mSimpleProgram.getAttributeLocation("aUV");
- mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture");
+ : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+ mMixProgram.compile(getVertexShader(), getMixFragShader());
+ mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+ mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+ mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+ mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+ mMMixLoc = mMixProgram.getUniformLocation("uMix");
}
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) {
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
ATRACE_NAME("BlurFilter::setAsDrawTarget");
+ mRadius = radius;
if (!mTexturesAllocated) {
mDisplayWidth = display.physicalDisplay.width();
mDisplayHeight = display.physicalDisplay.height();
mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
- // Let's use mimap filtering on the offscreen composition texture,
- // this will drastically improve overall shader quality.
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
- glBindTexture(GL_TEXTURE_2D, 0);
-
const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
@@ -76,8 +71,18 @@
}
void BlurFilter::drawMesh(GLuint uv, GLuint position) {
- GLfloat positions[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f};
- GLfloat texCoords[] = {0.0, 0.0, 0.0, 1.0f, 1.0f, 1.0f, 1.0f, 0};
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ GLfloat positions[] = {
+ translation-size, -translation-size,
+ translation-size, -translation+size,
+ translation+size, -translation+size
+ };
+ GLfloat texCoords[] = {
+ 0.0f, 0.0f-translation,
+ 0.0f, size-translation,
+ size, size-translation
+ };
// set attributes
glEnableVertexAttribArray(uv);
@@ -87,34 +92,52 @@
positions);
// draw mesh
- glDrawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
+ glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
mEngine.checkErrors("Drawing blur mesh");
}
-status_t BlurFilter::render() {
+status_t BlurFilter::render(bool multiPass) {
ATRACE_NAME("BlurFilter::render");
- // Now let's scale our blur up
- mSimpleProgram.useProgram();
+ // Now let's scale our blur up. It will be interpolated with the larger composited
+ // texture for the first frames, to hide downscaling artifacts.
+ GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+
+ // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+ // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+ // as large as the screen size.
+ if (mix >= 1 || multiPass) {
+ mBlurredFbo.bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
+ mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ return NO_ERROR;
+ }
+
+ mMixProgram.useProgram();
+ glUniform1f(mMMixLoc, mix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
- glUniform1i(mSTextureLoc, 0);
+ glUniform1i(mMTextureLoc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mMCompositionTextureLoc, 1);
mEngine.checkErrors("Setting final pass uniforms");
- drawMesh(mSUvLoc, mSPosLoc);
+ drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(
#version 310 es
- precision lowp float;
in vec2 aPosition;
- in mediump vec2 aUV;
- out mediump vec2 vUV;
+ in highp vec2 aUV;
+ out highp vec2 vUV;
void main() {
vUV = aUV;
@@ -123,18 +146,22 @@
)SHADER";
}
-string BlurFilter::getSimpleFragShader() const {
+string BlurFilter::getMixFragShader() const {
string shader = R"SHADER(
#version 310 es
- precision lowp float;
+ precision mediump float;
- in mediump vec2 vUV;
+ in highp vec2 vUV;
out vec4 fragColor;
+ uniform sampler2D uCompositionTexture;
uniform sampler2D uTexture;
+ uniform float uMix;
void main() {
- fragColor = texture(uTexture, vUV);
+ vec4 blurred = texture(uTexture, vUV);
+ vec4 composition = texture(uCompositionTexture, vUV);
+ fragColor = mix(composition, blurred, uMix);
}
)SHADER";
return shader;
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index e265b51..52dc8aa 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -31,22 +31,25 @@
public:
// Downsample FBO to improve performance
static constexpr float kFboScale = 0.25f;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
explicit BlurFilter(GLESRenderEngine& engine);
virtual ~BlurFilter(){};
// Set up render targets, redirecting output to offscreen texture.
- status_t setAsDrawTarget(const DisplaySettings&);
+ status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
// Allocate any textures needed for the filter.
virtual void allocateTextures() = 0;
// Execute blur passes, rendering to offscreen texture.
- virtual status_t prepare(uint32_t radius) = 0;
+ virtual status_t prepare() = 0;
// Render blur to the bound framebuffer (screen).
- status_t render();
+ status_t render(bool multiPass);
protected:
+ uint32_t mRadius;
void drawMesh(GLuint uv, GLuint position);
- string getSimpleFragShader() const;
string getVertexShader() const;
GLESRenderEngine& mEngine;
@@ -58,12 +61,15 @@
uint32_t mDisplayHeight;
private:
+ string getMixFragShader() const;
bool mTexturesAllocated = false;
- GenericProgram mSimpleProgram;
- GLuint mSPosLoc;
- GLuint mSUvLoc;
- GLuint mSTextureLoc;
+ GenericProgram mMixProgram;
+ GLuint mMPosLoc;
+ GLuint mMUvLoc;
+ GLuint mMMixLoc;
+ GLuint mMTextureLoc;
+ GLuint mMCompositionTextureLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
index f5ba02a..4d7bf44 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -26,6 +26,10 @@
#include <utils/Trace.h>
+#define PI 3.14159265359
+#define THETA 0.352
+#define K 1.0 / (2.0 * THETA * THETA)
+
namespace android {
namespace renderengine {
namespace gl {
@@ -39,22 +43,24 @@
mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
- mVSizeLoc = mVerticalProgram.getUniformLocation("uSize");
- mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius");
+ mVIncrementLoc = mVerticalProgram.getUniformLocation("uIncrement");
+ mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
+ mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
- mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize");
- mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius");
+ mHIncrementLoc = mHorizontalProgram.getUniformLocation("uIncrement");
+ mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
+ mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
}
void GaussianBlurFilter::allocateTextures() {
mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
}
-status_t GaussianBlurFilter::prepare(uint32_t radius) {
+status_t GaussianBlurFilter::prepare() {
ATRACE_NAME("GaussianBlurFilter::prepare");
if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -70,21 +76,38 @@
return GL_INVALID_OPERATION;
}
+ mCompositionFbo.bindAsReadBuffer();
+ mBlurredFbo.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
+ 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
// First, we'll apply the vertical pass, that receives the flattened background layers.
mVerticalPassFbo.bind();
mVerticalProgram.useProgram();
+ // Precompute gaussian bell curve, and send it to the shader to avoid
+ // unnecessary computations.
+ auto samples = min(mRadius, kNumSamples);
+ GLfloat gaussianWeights[kNumSamples] = {};
+ for (size_t i = 0; i < samples; i++) {
+ float normalized = float(i) / samples;
+ gaussianWeights[i] = (float)exp(-K * normalized * normalized);
+ }
+
// set uniforms
auto width = mVerticalPassFbo.getBufferWidth();
auto height = mVerticalPassFbo.getBufferHeight();
- auto radiusF = fmax(1.0f, radius * kFboScale);
+ auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glGenerateMipmap(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
glUniform1i(mVTextureLoc, 0);
- glUniform2f(mVSizeLoc, width, height);
- glUniform1f(mVRadiusLoc, radiusF);
+ glUniform2f(mVIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mVNumSamplesLoc, samples);
+ glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
drawMesh(mVUvLoc, mVPosLoc);
@@ -97,8 +120,9 @@
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
glUniform1i(mHTextureLoc, 0);
- glUniform2f(mHSizeLoc, width, height);
- glUniform1f(mHRadiusLoc, radiusF);
+ glUniform2f(mHIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mHNumSamplesLoc, samples);
+ glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical pass uniforms");
drawMesh(mHUvLoc, mHPosLoc);
@@ -115,42 +139,31 @@
}
string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
- string shader = "#version 310 es\n#define DIRECTION ";
- shader += (horizontal ? "1" : "0");
- shader += R"SHADER(
- precision lowp float;
+ stringstream shader;
+ shader << "#version 310 es\n"
+ << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
+ << "#define NUM_SAMPLES " << kNumSamples <<
+ R"SHADER(
+ precision mediump float;
uniform sampler2D uTexture;
- uniform vec2 uSize;
- uniform float uRadius;
+ uniform vec2 uIncrement;
+ uniform float[NUM_SAMPLES] uGaussianWeights;
+ uniform int uSamples;
- mediump in vec2 vUV;
+ highp in vec2 vUV;
out vec4 fragColor;
- #define PI 3.14159265359
- #define THETA 0.352
- #define MU 0.0
- #define A 1.0 / (THETA * sqrt(2.0 * PI))
- #define K 1.0 / (2.0 * THETA * THETA)
- #define MAX_SAMPLES 10
-
- float gaussianBellCurve(float x) {
- float tmp = (x - MU);
- return exp(-K * tmp * tmp);
- }
-
- vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size,
- mediump vec2 direction, float radius) {
+ vec3 gaussianBlur(sampler2D texture, highp vec2 uv, float inc, vec2 direction) {
float totalWeight = 0.0;
- vec3 blurred = vec3(0.);
- int samples = min(int(ceil(radius / 2.0)), MAX_SAMPLES);
- float inc = radius / (size * 2.0);
+ vec3 blurred = vec3(0.0);
+ float fSamples = 1.0 / float(uSamples);
- for (int i = -samples; i <= samples; i++) {
- float normalized = float(i) / float(samples);
- float weight = gaussianBellCurve(normalized);
+ for (int i = -uSamples; i <= uSamples; i++) {
+ float weight = uGaussianWeights[abs(i)];
+ float normalized = float(i) * fSamples;
float radInc = inc * normalized;
- blurred += weight * (texture(texture, uv + radInc * direction)).rgb;;
+ blurred += weight * (texture(texture, radInc * direction + uv, 0.0)).rgb;
totalWeight += weight;
}
@@ -159,15 +172,15 @@
void main() {
#if DIRECTION == 1
- vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.x, vec2(1.0, 0.0));
#else
- vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.y, vec2(0.0, 1.0));
#endif
fragColor = vec4(color, 1.0);
}
)SHADER";
- return shader;
+ return shader.str();
}
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
index acf0f07..8580522 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -30,8 +30,10 @@
class GaussianBlurFilter : public BlurFilter {
public:
+ static constexpr uint32_t kNumSamples = 12;
+
explicit GaussianBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
+ status_t prepare() override;
void allocateTextures() override;
private:
@@ -45,16 +47,18 @@
GLuint mVPosLoc;
GLuint mVUvLoc;
GLuint mVTextureLoc;
- GLuint mVSizeLoc;
- GLuint mVRadiusLoc;
+ GLuint mVIncrementLoc;
+ GLuint mVNumSamplesLoc;
+ GLuint mVGaussianWeightLoc;
// Horizontal pass and its uniforms
GenericProgram mHorizontalProgram;
GLuint mHPosLoc;
GLuint mHUvLoc;
GLuint mHTextureLoc;
- GLuint mHSizeLoc;
- GLuint mHRadiusLoc;
+ GLuint mHIncrementLoc;
+ GLuint mHNumSamplesLoc;
+ GLuint mHGaussianWeightLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..fc26bcc
--- /dev/null
+++ b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+KawaseBlurFilter::KawaseBlurFilter(GLESRenderEngine& engine)
+ : BlurFilter(engine), mFbo(engine), mProgram(engine) {
+ mProgram.compile(getVertexShader(), getFragmentShader());
+ mPosLoc = mProgram.getAttributeLocation("aPosition");
+ mUvLoc = mProgram.getAttributeLocation("aUV");
+ mTextureLoc = mProgram.getUniformLocation("uTexture");
+ mOffsetLoc = mProgram.getUniformLocation("uOffset");
+}
+
+void KawaseBlurFilter::allocateTextures() {
+ mFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
+}
+
+status_t KawaseBlurFilter::prepare() {
+ ATRACE_NAME("KawaseBlurFilter::prepare");
+
+ if (mFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid FBO");
+ return mFbo.getStatus();
+ }
+ if (!mProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
+
+ blit(mCompositionFbo, mBlurredFbo);
+
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ auto radius = mRadius / 6.0f;
+
+ // Calculate how many passes we'll do, based on the radius.
+ // Too many passes will make the operation expensive.
+ auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+ // We'll ping pong between our textures, to accumulate the result of various offsets.
+ mProgram.useProgram();
+ GLFramebuffer* draw = &mFbo;
+ GLFramebuffer* read = &mBlurredFbo;
+ float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
+ float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(mTextureLoc, 0);
+ for (auto i = 0; i < passes; i++) {
+ ATRACE_NAME("KawaseBlurFilter::renderPass");
+ draw->bind();
+
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+ glUniform2f(mOffsetLoc, stepX * i, stepY * i);
+ mEngine.checkErrors("Setting uniforms");
+
+ drawMesh(mUvLoc, mPosLoc);
+
+ // Swap buffers for next iteration
+ auto tmp = draw;
+ draw = read;
+ read = tmp;
+ }
+
+ // Copy texture, given that we're expected to end on mBlurredFbo.
+ if (draw == &mBlurredFbo) {
+ blit(mFbo, mBlurredFbo);
+ }
+
+ // Cleanup
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return NO_ERROR;
+}
+
+string KawaseBlurFilter::getFragmentShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ uniform sampler2D uTexture;
+ highp uniform vec2 uOffset;
+
+ highp in vec2 vUV;
+ out vec4 fragColor;
+
+ vec4 kawaseBlur() {
+ return (texture(uTexture, vec2(-1.0, 1.0) * uOffset + vUV, 0.0)
+ + texture(uTexture, uOffset + vUV, 0.0)
+ + texture(uTexture, vec2(1.0, -1.0) * uOffset + vUV, 0.0)
+ + texture(uTexture, vec2(-1.0) * uOffset + vUV, 0.0))
+ * 0.25;
+ }
+
+ void main() {
+ fragColor = kawaseBlur();
+ }
+ )SHADER";
+}
+
+void KawaseBlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+ read.bindAsReadBuffer();
+ draw.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
+ draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
+ GL_LINEAR);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.h b/libs/renderengine/gl/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..ec81f81
--- /dev/null
+++ b/libs/renderengine/gl/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "BlurFilter.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class KawaseBlurFilter : public BlurFilter {
+public:
+ static constexpr uint32_t kMaxPasses = 8;
+
+ explicit KawaseBlurFilter(GLESRenderEngine& engine);
+ status_t prepare() override;
+ void allocateTextures() override;
+
+private:
+ string getFragmentShader() const;
+ void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
+
+ GLFramebuffer mFbo;
+
+ GenericProgram mProgram;
+ GLuint mPosLoc;
+ GLuint mUvLoc;
+ GLuint mTextureLoc;
+ GLuint mOffsetLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp
deleted file mode 100644
index 799deac..0000000
--- a/libs/renderengine/gl/filters/LensBlurFilter.cpp
+++ /dev/null
@@ -1,227 +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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LensBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-#include <cstdint>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-// Number of blur samples in shader (for loop)
-static constexpr auto kNumSamples = 12;
-
-LensBlurFilter::LensBlurFilter(GLESRenderEngine& engine)
- : BlurFilter(engine),
- mVerticalDiagonalPassFbo(engine, true /* multiTarget */),
- mVerticalDiagonalProgram(engine),
- mCombinedProgram(engine) {
- mVerticalDiagonalProgram.compile(getVertexShader(), getFragmentShader(false));
- mCombinedProgram.compile(getVertexShader(), getFragmentShader(true));
-
- mVDPosLoc = mVerticalDiagonalProgram.getAttributeLocation("aPosition");
- mVDUvLoc = mVerticalDiagonalProgram.getAttributeLocation("aUV");
- mVDTexture0Loc = mVerticalDiagonalProgram.getUniformLocation("uTexture0");
- mVDSizeLoc = mVerticalDiagonalProgram.getUniformLocation("uSize");
- mVDRadiusLoc = mVerticalDiagonalProgram.getUniformLocation("uRadius");
- mVDNumSamplesLoc = mVerticalDiagonalProgram.getUniformLocation("uNumSamples");
-
- mCPosLoc = mCombinedProgram.getAttributeLocation("aPosition");
- mCUvLoc = mCombinedProgram.getAttributeLocation("aUV");
- mCTexture0Loc = mCombinedProgram.getUniformLocation("uTexture0");
- mCTexture1Loc = mCombinedProgram.getUniformLocation("uTexture1");
- mCSizeLoc = mCombinedProgram.getUniformLocation("uSize");
- mCRadiusLoc = mCombinedProgram.getUniformLocation("uRadius");
- mCNumSamplesLoc = mCombinedProgram.getUniformLocation("uNumSamples");
-}
-
-void LensBlurFilter::allocateTextures() {
- mVerticalDiagonalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(),
- mBlurredFbo.getBufferHeight());
-}
-
-status_t LensBlurFilter::prepare(uint32_t radius) {
- ATRACE_NAME("LensBlurFilter::prepare");
-
- if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid vertical-diagonal FBO");
- return mVerticalDiagonalPassFbo.getStatus();
- }
- if (!mVerticalDiagonalProgram.isValid()) {
- ALOGE("Invalid vertical-diagonal shader");
- return GL_INVALID_OPERATION;
- }
- if (!mCombinedProgram.isValid()) {
- ALOGE("Invalid blur shader");
- return GL_INVALID_OPERATION;
- }
-
- // First, we'll apply the vertical/diagonal pass, that receives the flattened background layers,
- // and writes the output to two textures (vertical and diagonal.)
- mVerticalDiagonalPassFbo.bind();
- mVerticalDiagonalProgram.useProgram();
-
- // set uniforms
- auto width = mVerticalDiagonalPassFbo.getBufferWidth();
- auto height = mVerticalDiagonalPassFbo.getBufferHeight();
- auto radiusF = fmax(1.0f, radius * kFboScale);
- glViewport(0, 0, width, height);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glGenerateMipmap(GL_TEXTURE_2D);
- glUniform1i(mVDTexture0Loc, 0);
- glUniform2f(mVDSizeLoc, mDisplayWidth, mDisplayHeight);
- glUniform1f(mVDRadiusLoc, radiusF);
- glUniform1i(mVDNumSamplesLoc, kNumSamples);
- mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
-
- drawMesh(mVDUvLoc, mVDPosLoc);
-
- // Now we'll combine the multi render pass into a blurred image
- mBlurredFbo.bind();
- mCombinedProgram.useProgram();
-
- // set uniforms
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getTextureName());
- glUniform1i(mCTexture0Loc, 0);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getSecondaryTextureName());
- glUniform1i(mCTexture1Loc, 1);
- glUniform2f(mCSizeLoc, mDisplayWidth, mDisplayHeight);
- glUniform1f(mCRadiusLoc, radiusF);
- glUniform1i(mCNumSamplesLoc, kNumSamples);
- mEngine.checkErrors("Setting vertical pass uniforms");
-
- drawMesh(mCUvLoc, mCPosLoc);
-
- // reset active texture
- mBlurredFbo.unbind();
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // unbind program
- glUseProgram(0);
-
- return NO_ERROR;
-}
-
-string LensBlurFilter::getFragmentShader(bool forComposition) const {
- string shader = "#version 310 es\n#define DIRECTION ";
- shader += (forComposition ? "1" : "0");
- shader += R"SHADER(
- precision lowp float;
-
- #define PI 3.14159265359
-
- uniform sampler2D uTexture0;
- uniform vec2 uSize;
- uniform float uRadius;
- uniform int uNumSamples;
-
- mediump in vec2 vUV;
-
- #if DIRECTION == 0
- layout(location = 0) out vec4 fragColor0;
- layout(location = 1) out vec4 fragColor1;
- #else
- uniform sampler2D uTexture1;
- out vec4 fragColor;
- #endif
-
- const vec2 verticalMult = vec2(cos(PI / 2.0), sin(PI / 2.0));
- const vec2 diagonalMult = vec2(cos(-PI / 6.0), sin(-PI / 6.0));
- const vec2 diagonal2Mult = vec2(cos(-5.0 * PI / 6.0), sin(-5.0 * PI / 6.0));
-
- vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
- int samples, float intensity) {
- vec3 finalColor = vec3(0.0);
- uv += direction * 0.5;
-
- for (int i = 0; i < samples; i++){
- float delta = radius * float(i) / float(samples);
- vec3 color = texture(tex, uv + direction * delta).rgb;
- color.rgb *= intensity;
- finalColor += color;
- }
-
- return finalColor / float(samples);
- }
-
- vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
- int samples) {
- return blur(tex, uv, direction, radius, samples, 1.0);
- }
-
- vec4[2] verticalDiagonalLensBlur (vec2 uv, sampler2D texture, vec2 resolution,
- float radius, int samples) {
- // Vertical Blur
- vec2 blurDirV = 1.0 / resolution.xy * verticalMult;
- vec3 colorV = blur(texture, uv, blurDirV, radius, samples);
-
- // Diagonal Blur
- vec2 blurDirD = 1.0 / resolution.xy * diagonalMult;
- vec3 colorD = blur(texture, uv, blurDirD, radius, samples);
-
- vec4 composed[2];
- composed[0] = vec4(colorV, 1.0);
- // added * 0.5, to remap
- composed[1] = vec4((colorD + colorV) * 0.5, 1.0);
-
- return composed;
- }
-
- vec4 rhombiLensBlur (vec2 uv, sampler2D texture0, sampler2D texture1, vec2 resolution,
- float radius, int samples) {
- vec2 blurDirection1 = 1.0 / resolution.xy * diagonalMult;
- vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples);
-
- vec2 blurDirection2 = 1.0 / resolution.xy * diagonal2Mult;
- vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0);
-
- return vec4((color1 + color2) * 0.33, 1.0);
- }
-
- void main() {
- #if DIRECTION == 0
- // First pass: outputs two textures
- vec4 colorOut[] = verticalDiagonalLensBlur(vUV, uTexture0, uSize, uRadius, uNumSamples);
- fragColor0 = colorOut[0];
- fragColor1 = colorOut[1];
- #else
- // Second pass: combines both textures into a blurred one.
- fragColor = rhombiLensBlur(vUV, uTexture0, uTexture1, uSize, uRadius, uNumSamples);
- #endif
- }
-
- )SHADER";
- return shader;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h
deleted file mode 100644
index 8543f0d..0000000
--- a/libs/renderengine/gl/filters/LensBlurFilter.h
+++ /dev/null
@@ -1,65 +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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "BlurFilter.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class LensBlurFilter : public BlurFilter {
-public:
- explicit LensBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
- void allocateTextures() override;
-
-private:
- string getFragmentShader(bool forComposition) const;
-
- // Intermediate render pass
- GLFramebuffer mVerticalDiagonalPassFbo;
-
- // Vertical/diagonal pass and its uniforms
- GenericProgram mVerticalDiagonalProgram;
- GLuint mVDPosLoc;
- GLuint mVDUvLoc;
- GLuint mVDTexture0Loc;
- GLuint mVDSizeLoc;
- GLuint mVDRadiusLoc;
- GLuint mVDNumSamplesLoc;
-
- // Blur composition pass and its uniforms
- GenericProgram mCombinedProgram;
- GLuint mCPosLoc;
- GLuint mCUvLoc;
- GLuint mCTexture0Loc;
- GLuint mCTexture1Loc;
- GLuint mCSizeLoc;
- GLuint mCRadiusLoc;
- GLuint mCNumSamplesLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
\ No newline at end of file
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index bf487c4..cd2a448 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -67,19 +67,20 @@
// ----------------------------------------------------------------------------
Region::Region() {
- mStorage.add(Rect(0,0));
+ mStorage.push_back(Rect(0, 0));
}
Region::Region(const Region& rhs)
- : mStorage(rhs.mStorage)
{
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
#if defined(VALIDATE_REGIONS)
validate(rhs, "rhs copy-ctor");
#endif
}
Region::Region(const Rect& rhs) {
- mStorage.add(rhs);
+ mStorage.push_back(rhs);
}
Region::~Region()
@@ -100,8 +101,8 @@
* final, correctly ordered region buffer. Each rectangle will be compared with the span directly
* above it, and subdivided to resolve any remaining T-junctions.
*/
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
- Vector<Rect>& dst, int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
+ int spanDirection) {
dst.clear();
const Rect* current = end - 1;
@@ -109,7 +110,7 @@
// add first span immediately
do {
- dst.add(*current);
+ dst.push_back(*current);
current--;
} while (current->top == lastTop && current >= begin);
@@ -147,12 +148,12 @@
if (prev.right <= left) break;
if (prev.right > left && prev.right < right) {
- dst.add(Rect(prev.right, top, right, bottom));
+ dst.push_back(Rect(prev.right, top, right, bottom));
right = prev.right;
}
if (prev.left > left && prev.left < right) {
- dst.add(Rect(prev.left, top, right, bottom));
+ dst.push_back(Rect(prev.left, top, right, bottom));
right = prev.left;
}
@@ -166,12 +167,12 @@
if (prev.left >= right) break;
if (prev.left > left && prev.left < right) {
- dst.add(Rect(left, top, prev.left, bottom));
+ dst.push_back(Rect(left, top, prev.left, bottom));
left = prev.left;
}
if (prev.right > left && prev.right < right) {
- dst.add(Rect(left, top, prev.right, bottom));
+ dst.push_back(Rect(left, top, prev.right, bottom));
left = prev.right;
}
// if an entry in the previous span is too far left, nothing further right in the
@@ -183,7 +184,7 @@
}
if (left < right) {
- dst.add(Rect(left, top, right, bottom));
+ dst.push_back(Rect(left, top, right, bottom));
}
current--;
@@ -201,13 +202,14 @@
if (r.isEmpty()) return r;
if (r.isRect()) return r;
- Vector<Rect> reversed;
+ FatVector<Rect> reversed;
reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
Region outputRegion;
- reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
- outputRegion.mStorage, direction_LTR);
- outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
+ reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
+ outputRegion.mStorage, direction_LTR);
+ outputRegion.mStorage.push_back(
+ r.getBounds()); // to make region valid, mStorage must end with bounds
#if defined(VALIDATE_REGIONS)
validate(outputRegion, "T-Junction free region");
@@ -222,7 +224,8 @@
validate(*this, "this->operator=");
validate(rhs, "rhs.operator=");
#endif
- mStorage = rhs.mStorage;
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
return *this;
}
@@ -231,7 +234,7 @@
if (mStorage.size() >= 2) {
const Rect bounds(getBounds());
mStorage.clear();
- mStorage.add(bounds);
+ mStorage.push_back(bounds);
}
return *this;
}
@@ -255,25 +258,25 @@
void Region::clear()
{
mStorage.clear();
- mStorage.add(Rect(0,0));
+ mStorage.push_back(Rect(0, 0));
}
void Region::set(const Rect& r)
{
mStorage.clear();
- mStorage.add(r);
+ mStorage.push_back(r);
}
void Region::set(int32_t w, int32_t h)
{
mStorage.clear();
- mStorage.add(Rect(w, h));
+ mStorage.push_back(Rect(w, h));
}
void Region::set(uint32_t w, uint32_t h)
{
mStorage.clear();
- mStorage.add(Rect(w, h));
+ mStorage.push_back(Rect(w, h));
}
bool Region::isTriviallyEqual(const Region& region) const {
@@ -299,8 +302,7 @@
void Region::addRectUnchecked(int l, int t, int r, int b)
{
Rect rect(l,t,r,b);
- size_t where = mStorage.size() - 1;
- mStorage.insertAt(rect, where, 1);
+ mStorage.insert(mStorage.end() - 1, rect);
}
// ----------------------------------------------------------------------------
@@ -350,7 +352,7 @@
Region& Region::scaleSelf(float sx, float sy) {
size_t count = mStorage.size();
- Rect* rects = mStorage.editArray();
+ Rect* rects = mStorage.data();
while (count) {
rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -455,10 +457,10 @@
class Region::rasterizer : public region_operator<Rect>::region_rasterizer
{
Rect bounds;
- Vector<Rect>& storage;
+ FatVector<Rect>& storage;
Rect* head;
Rect* tail;
- Vector<Rect> span;
+ FatVector<Rect> span;
Rect* cur;
public:
explicit rasterizer(Region& reg)
@@ -485,8 +487,8 @@
flushSpan();
}
if (storage.size()) {
- bounds.top = storage.itemAt(0).top;
- bounds.bottom = storage.top().bottom;
+ bounds.top = storage.front().top;
+ bounds.bottom = storage.back().bottom;
if (storage.size() == 1) {
storage.clear();
}
@@ -494,7 +496,7 @@
bounds.left = 0;
bounds.right = 0;
}
- storage.add(bounds);
+ storage.push_back(bounds);
}
void Region::rasterizer::operator()(const Rect& rect)
@@ -509,15 +511,15 @@
return;
}
}
- span.add(rect);
- cur = span.editArray() + (span.size() - 1);
+ span.push_back(rect);
+ cur = span.data() + (span.size() - 1);
}
void Region::rasterizer::flushSpan()
{
bool merge = false;
if (tail-head == ssize_t(span.size())) {
- Rect const* p = span.editArray();
+ Rect const* p = span.data();
Rect const* q = head;
if (p->top == q->bottom) {
merge = true;
@@ -532,17 +534,17 @@
}
}
if (merge) {
- const int bottom = span[0].bottom;
+ const int bottom = span.front().bottom;
Rect* r = head;
while (r != tail) {
r->bottom = bottom;
r++;
}
} else {
- bounds.left = min(span.itemAt(0).left, bounds.left);
- bounds.right = max(span.top().right, bounds.right);
- storage.appendVector(span);
- tail = storage.editArray() + storage.size();
+ bounds.left = min(span.front().left, bounds.left);
+ bounds.right = max(span.back().right, bounds.right);
+ storage.insert(storage.end(), span.begin(), span.end());
+ tail = storage.data() + storage.size();
head = tail - span.size();
}
span.clear();
@@ -550,7 +552,7 @@
bool Region::validate(const Region& reg, const char* name, bool silent)
{
- if (reg.mStorage.isEmpty()) {
+ if (reg.mStorage.empty()) {
ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
// return immediately as the code below assumes mStorage is non-empty
return false;
@@ -689,9 +691,8 @@
}
sk_dst.op(sk_lhs, sk_rhs, sk_op);
- if (sk_dst.isEmpty() && dst.isEmpty())
- return;
-
+ if (sk_dst.empty() && dst.empty()) return;
+
bool same = true;
Region::const_iterator head = dst.begin();
Region::const_iterator const tail = dst.end();
@@ -786,7 +787,7 @@
validate(reg, "translate (before)");
#endif
size_t count = reg.mStorage.size();
- Rect* rects = reg.mStorage.editArray();
+ Rect* rects = reg.mStorage.data();
while (count) {
rects->offsetBy(dx, dy);
rects++;
@@ -866,24 +867,25 @@
ALOGE("Region::unflatten() failed, invalid region");
return BAD_VALUE;
}
- mStorage = result.mStorage;
+ mStorage.clear();
+ mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
return NO_ERROR;
}
// ----------------------------------------------------------------------------
Region::const_iterator Region::begin() const {
- return mStorage.array();
+ return mStorage.data();
}
Region::const_iterator Region::end() const {
// Workaround for b/77643177
// mStorage should never be empty, but somehow it is and it's causing
// an abort in ubsan
- if (mStorage.isEmpty()) return mStorage.array();
+ if (mStorage.empty()) return mStorage.data();
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
- return mStorage.array() + numRects;
+ return mStorage.data() + numRects;
}
Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayConfig.h
new file mode 100644
index 0000000..09b8211
--- /dev/null
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <type_traits>
+
+#include <ui/Size.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Configuration supported by physical display.
+struct DisplayConfig {
+ ui::Size resolution;
+ float xDpi = 0;
+ float yDpi = 0;
+
+ float refreshRate = 0;
+ nsecs_t appVsyncOffset = 0;
+ nsecs_t sfVsyncOffset = 0;
+ nsecs_t presentationDeadline = 0;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayConfig>);
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 38f8d6b..7773319 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,35 +14,18 @@
* limitations under the License.
*/
-#ifndef ANDROID_UI_DISPLAY_INFO_H
-#define ANDROID_UI_DISPLAY_INFO_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Rotation.h>
-#include <utils/Timers.h>
+#include <type_traits>
namespace android {
-constexpr uint32_t NO_LAYER_STACK = static_cast<uint32_t>(-1);
-
+// Immutable information about physical display.
struct DisplayInfo {
- uint32_t w{0};
- uint32_t h{0};
- float xdpi{0};
- float ydpi{0};
- float fps{0};
- float density{0};
- ui::Rotation orientation{ui::ROTATION_0};
- bool secure{false};
- nsecs_t appVsyncOffset{0};
- nsecs_t presentationDeadline{0};
- uint32_t viewportW{0};
- uint32_t viewportH{0};
- uint32_t layerStack{NO_LAYER_STACK};
+ float density = 0.f;
+ bool secure = false;
};
-} // namespace android
+static_assert(std::is_trivially_copyable_v<DisplayInfo>);
-#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
new file mode 100644
index 0000000..64efc84
--- /dev/null
+++ b/libs/ui/include/ui/DisplayState.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <ui/Rotation.h>
+#include <ui/Size.h>
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android::ui {
+
+using LayerStack = uint32_t;
+constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
+
+// Transactional state of physical or virtual display. Note that libgui defines
+// android::DisplayState as a superset of android::ui::DisplayState.
+struct DisplayState {
+ LayerStack layerStack = NO_LAYER_STACK;
+ Rotation orientation = ROTATION_0;
+ Size viewport;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayState>);
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
new file mode 100644
index 0000000..25fe3a0
--- /dev/null
+++ b/libs/ui/include/ui/FatVector.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_REGION_FAT_VECTOR_H
+#define ANDROID_REGION_FAT_VECTOR_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+#include <type_traits>
+
+#include <vector>
+
+namespace android {
+
+template <typename T, size_t SIZE = 4>
+class InlineStdAllocator {
+public:
+ struct Allocation {
+ private:
+ Allocation(const Allocation&) = delete;
+ void operator=(const Allocation&) = delete;
+
+ public:
+ Allocation() {}
+ // char array instead of T array, so memory is uninitialized, with no destructors run
+ char array[sizeof(T) * SIZE];
+ bool inUse = false;
+ };
+
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
+ InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
+ ~InlineStdAllocator() {}
+
+ T* allocate(size_t num, const void* = 0) {
+ if (!mAllocation.inUse && num <= SIZE) {
+ mAllocation.inUse = true;
+ return static_cast<T*>(static_cast<void*>(mAllocation.array));
+ } else {
+ return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
+ }
+ }
+
+ void deallocate(pointer p, size_t) {
+ if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
+ mAllocation.inUse = false;
+ } else {
+ // 'free' instead of delete here - destruction handled separately
+ free(p);
+ }
+ }
+ Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE = 4>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+ FatVector()
+ : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
+ this->reserve(SIZE);
+ }
+
+ explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
+
+private:
+ typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+} // namespace android
+
+#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 2db3b10..6bb7b8d 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,13 +21,13 @@
#include <sys/types.h>
#include <ostream>
-#include <utils/Vector.h>
-
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <android-base/macros.h>
+#include "FatVector.h"
+
#include <string>
namespace android {
@@ -180,7 +180,7 @@
// with an extra Rect as the last element which is set to the
// bounds of the region. However, if the region is
// a simple Rect then mStorage contains only that rect.
- Vector<Rect> mStorage;
+ FatVector<Rect> mStorage;
};
@@ -235,4 +235,3 @@
}; // namespace android
#endif // ANDROID_UI_REGION_H
-
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
new file mode 120000
index 0000000..1450319
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayConfig.h
@@ -0,0 +1 @@
+../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayState.h b/libs/ui/include_vndk/ui/DisplayState.h
new file mode 120000
index 0000000..4e92849
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayState.h
@@ -0,0 +1 @@
+../../include/ui/DisplayState.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
new file mode 120000
index 0000000..bf30166
--- /dev/null
+++ b/libs/ui/include_vndk/ui/FatVector.h
@@ -0,0 +1 @@
+../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index 4dcc1ca..dfb9c92 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -16,10 +16,13 @@
#include <WindowSurface.h>
-#include <gui/SurfaceComposerClient.h>
+#include <utility>
+
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
-#include <ui/DisplayInfo.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
using namespace android;
@@ -33,28 +36,33 @@
return;
}
- // Get main display parameters.
- const auto mainDpy = SurfaceComposerClient::getInternalDisplayToken();
- if (mainDpy == nullptr) {
+ const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+ if (displayToken == nullptr) {
fprintf(stderr, "ERROR: no display\n");
return;
}
- DisplayInfo mainDpyInfo;
- err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+ DisplayConfig displayConfig;
+ err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: unable to get display characteristics\n");
+ fprintf(stderr, "ERROR: unable to get active display config\n");
return;
}
- uint32_t width, height;
- if (mainDpyInfo.orientation != ui::ROTATION_0 && mainDpyInfo.orientation != ui::ROTATION_180) {
- // rotated
- width = mainDpyInfo.h;
- height = mainDpyInfo.w;
- } else {
- width = mainDpyInfo.w;
- height = mainDpyInfo.h;
+ ui::DisplayState displayState;
+ err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to get display state\n");
+ return;
+ }
+
+ const ui::Size& resolution = displayConfig.resolution;
+ auto width = resolution.getWidth();
+ auto height = resolution.getHeight();
+
+ if (displayState.orientation == ui::ROTATION_90 ||
+ displayState.orientation == ui::ROTATION_270) {
+ std::swap(width, height);
}
sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 5a5dc89..d94fb27 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -18,8 +18,8 @@
name: "android.frameworks.automotive.display@1.0-service",
defaults: ["hidl_defaults"],
srcs: [
- "main_automotivedisplay.cpp",
- "CarWindowService.cpp",
+ "main_automotivedisplayproxy.cpp",
+ "AutomotiveDisplayProxyService.cpp",
],
init_rc: ["android.frameworks.automotive.display@1.0-service.rc"],
diff --git a/services/automotive/display/CarWindowService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
similarity index 73%
rename from services/automotive/display/CarWindowService.cpp
rename to services/automotive/display/AutomotiveDisplayProxyService.cpp
index e95c9e1..3cd8e39 100644
--- a/services/automotive/display/CarWindowService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -13,10 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-#include <ui/DisplayInfo.h>
+
+#include <utility>
+
#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
-#include "CarWindowService.h"
+#include "AutomotiveDisplayProxyService.h"
namespace android {
namespace frameworks {
@@ -26,7 +28,7 @@
namespace implementation {
Return<sp<IGraphicBufferProducer>>
- CarWindowService::getIGraphicBufferProducer() {
+AutomotiveDisplayProxyService::getIGraphicBufferProducer() {
if (mSurface == nullptr) {
status_t err;
mSurfaceComposerClient = new SurfaceComposerClient();
@@ -38,31 +40,35 @@
return nullptr;
}
- // Get main display parameters.
- sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
- if (mainDpy == nullptr) {
+ const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+ if (displayToken == nullptr) {
ALOGE("Failed to get internal display ");
return nullptr;
}
- DisplayInfo mainDpyInfo;
- err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+
+ err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &mDpyConfig);
if (err != NO_ERROR) {
- ALOGE("Failed to get display characteristics");
+ ALOGE("Failed to get active display config");
return nullptr;
}
- unsigned int mWidth, mHeight;
- if (mainDpyInfo.orientation != ui::ROTATION_0 &&
- mainDpyInfo.orientation != ui::ROTATION_180) {
- // rotated
- mWidth = mainDpyInfo.h;
- mHeight = mainDpyInfo.w;
- } else {
- mWidth = mainDpyInfo.w;
- mHeight = mainDpyInfo.h;
+
+ err = SurfaceComposerClient::getDisplayState(displayToken, &mDpyState);
+ if (err != NO_ERROR) {
+ ALOGE("Failed to get display state");
+ return nullptr;
+ }
+
+ const ui::Size& resolution = mDpyConfig.resolution;
+ auto width = resolution.getWidth();
+ auto height = resolution.getHeight();
+
+ if (mDpyState.orientation == ui::ROTATION_90 ||
+ mDpyState.orientation == ui::ROTATION_270) {
+ std::swap(width, height);
}
mSurfaceControl = mSurfaceComposerClient->createSurface(
- String8("Automotive Display"), mWidth, mHeight,
+ String8("Automotive Display"), width, height,
PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
if (mSurfaceControl == nullptr || !mSurfaceControl->isValid()) {
ALOGE("Failed to create SurfaceControl");
@@ -80,7 +86,7 @@
mSurface->getIGraphicBufferProducer());
}
-Return<bool> CarWindowService::showWindow() {
+Return<bool> AutomotiveDisplayProxyService::showWindow() {
status_t status = NO_ERROR;
if (mSurfaceControl != nullptr) {
@@ -97,7 +103,7 @@
return status == NO_ERROR;
}
-Return<bool> CarWindowService::hideWindow() {
+Return<bool> AutomotiveDisplayProxyService::hideWindow() {
status_t status = NO_ERROR;
if (mSurfaceControl != nullptr) {
diff --git a/services/automotive/display/include/CarWindowService.h b/services/automotive/display/include/AutomotiveDisplayProxyService.h
similarity index 70%
rename from services/automotive/display/include/CarWindowService.h
rename to services/automotive/display/include/AutomotiveDisplayProxyService.h
index 3290cc7..3956602 100644
--- a/services/automotive/display/include/CarWindowService.h
+++ b/services/automotive/display/include/AutomotiveDisplayProxyService.h
@@ -15,11 +15,13 @@
//
#pragma once
-#include <android/frameworks/automotive/display/1.0/ICarWindowService.h>
+#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
#include <gui/ISurfaceComposer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
namespace android {
namespace frameworks {
@@ -32,16 +34,28 @@
using ::android::sp;
using ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
-class CarWindowService : public ICarWindowService {
+class AutomotiveDisplayProxyService : public IAutomotiveDisplayProxyService {
public:
Return<sp<IGraphicBufferProducer>> getIGraphicBufferProducer() override;
Return<bool> showWindow() override;
Return<bool> hideWindow() override;
+ Return<void> getDisplayInfo(getDisplayInfo_cb _info_cb) override {
+ HwDisplayConfig cfg;
+ cfg.setToExternal((uint8_t*)&mDpyConfig, sizeof(DisplayConfig));
+
+ HwDisplayState state;
+ state.setToExternal((uint8_t*)&mDpyState, sizeof(DisplayState));
+
+ _info_cb(cfg, state);
+ return hardware::Void();
+ }
private:
sp<android::Surface> mSurface;
sp<android::SurfaceComposerClient> mSurfaceComposerClient;
sp<android::SurfaceControl> mSurfaceControl;
+ DisplayConfig mDpyConfig;
+ ui::DisplayState mDpyState;
};
} // namespace implementation
} // namespace V1_0
diff --git a/services/automotive/display/main_automotivedisplay.cpp b/services/automotive/display/main_automotivedisplayproxy.cpp
similarity index 88%
rename from services/automotive/display/main_automotivedisplay.cpp
rename to services/automotive/display/main_automotivedisplayproxy.cpp
index 69f0a13..626c185 100644
--- a/services/automotive/display/main_automotivedisplay.cpp
+++ b/services/automotive/display/main_automotivedisplayproxy.cpp
@@ -23,14 +23,14 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
-#include "CarWindowService.h"
+#include "AutomotiveDisplayProxyService.h"
// libhidl:
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
// Generated HIDL files
-using android::frameworks::automotive::display::V1_0::ICarWindowService;
+using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
// The namespace in which all our implementation code lives
using namespace android::frameworks::automotive::display::V1_0::implementation;
@@ -41,7 +41,7 @@
int main() {
ALOGI("Car Window Service is starting");
- android::sp<ICarWindowService> service = new CarWindowService();
+ android::sp<IAutomotiveDisplayProxyService> service = new AutomotiveDisplayProxyService();
configureRpcThreadpool(1, true /* callerWillJoin */);
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index baba64f..6eed24a 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -20,6 +20,7 @@
"libbase",
"libbinder",
"libcutils",
+ "libgfxstats",
"libgraphicsenv",
"liblog",
"libutils",
@@ -52,7 +53,6 @@
name: "libgpuservice_sources",
srcs: [
"GpuService.cpp",
- "gpustats/GpuStats.cpp"
],
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index be4a462..7bb0e4a 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,14 +24,13 @@
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
+#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
#include <utils/String8.h>
#include <utils/Trace.h>
#include <vkjson.h>
-#include "gpustats/GpuStats.h"
-
namespace android {
using base::StringAppendF;
@@ -53,18 +52,9 @@
int64_t driverBuildTime, const std::string& appPackageName,
const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
bool isDriverLoaded, int64_t driverLoadingTime) {
- mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
- appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime);
-}
-
-status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
- mGpuStats->pullGlobalStats(outStats);
- return OK;
-}
-
-status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
- mGpuStats->pullAppStats(outStats);
- return OK;
+ mGpuStats->insertDriverStats(driverPackageName, driverVersionName, driverVersionCode,
+ driverBuildTime, appPackageName, vulkanVersion, driver,
+ isDriverLoaded, driverLoadingTime);
}
void GpuService::setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index b3dc2e2..b3e34d5 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -25,22 +25,11 @@
#include <mutex>
#include <vector>
-#include <unordered_map>
namespace android {
class GpuStats;
-struct MemoryStruct {
- int64_t gpuMemory;
- int64_t mappedMemory;
- int64_t ionMemory;
-};
-
-// A map that keeps track of how much memory of each type is allocated by every process.
-// Format: map[pid][memoryType] = MemoryStruct()'
-using GpuMemoryMap = std::unordered_map<int32_t, std::unordered_map<std::string, MemoryStruct>>;
-
class GpuService : public BnGpuService, public PriorityDumper {
public:
static const char* const SERVICE_NAME ANDROID_API;
@@ -59,8 +48,6 @@
const std::string& appPackageName, const int32_t vulkanVersion,
GpuStatsInfo::Driver driver, bool isDriverLoaded,
int64_t driverLoadingTime) override;
- status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override;
- status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override;
void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) override;
@@ -82,8 +69,6 @@
status_t doDump(int fd, const Vector<String16>& args, bool asProto);
- status_t getQCommGpuMemoryInfo(GpuMemoryMap* memories, std::string* result, int32_t dumpPid) const;
-
/*
* Attributes
*/
diff --git a/services/gpuservice/TEST_MAPPING b/services/gpuservice/TEST_MAPPING
new file mode 100644
index 0000000..b345355
--- /dev/null
+++ b/services/gpuservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "gpuservice_unittest"
+ }
+ ]
+}
diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp
new file mode 100644
index 0000000..f52602a
--- /dev/null
+++ b/services/gpuservice/gpustats/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+ name: "libgfxstats",
+ srcs: [
+ "GpuStats.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libgraphicsenv",
+ "liblog",
+ "libprotoutil",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libstatspull",
+ "libstatssocket",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 7fff6ed..d094532 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -17,16 +17,31 @@
#define LOG_TAG "GpuStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "GpuStats.h"
+#include "gpustats/GpuStats.h"
+#include <android/util/ProtoOutputStream.h>
#include <cutils/properties.h>
#include <log/log.h>
+#include <stats_event.h>
+#include <statslog.h>
#include <utils/Trace.h>
#include <unordered_set>
namespace android {
+GpuStats::GpuStats() {
+ AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
+ GpuStats::pullAtomCallback, nullptr, this);
+ AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
+ GpuStats::pullAtomCallback, nullptr, this);
+}
+
+GpuStats::~GpuStats() {
+ AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+ AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+}
+
static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
GpuStatsGlobalInfo* const outGlobalInfo) {
switch (driver) {
@@ -74,10 +89,11 @@
}
}
-void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName,
- uint64_t driverVersionCode, int64_t driverBuildTime,
- const std::string& appPackageName, const int32_t vulkanVersion,
- GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) {
+void GpuStats::insertDriverStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName,
+ const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mLock);
@@ -191,6 +207,11 @@
dumpAll = false;
}
+ if (dumpAll) {
+ dumpGlobalLocked(result);
+ dumpAppLocked(result);
+ }
+
if (argsSet.count("--clear")) {
bool clearAll = true;
@@ -208,13 +229,6 @@
mGlobalStats.clear();
mAppStats.clear();
}
-
- dumpAll = false;
- }
-
- if (dumpAll) {
- dumpGlobalLocked(result);
- dumpAppLocked(result);
}
}
@@ -234,34 +248,114 @@
}
}
-void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) {
- ATRACE_CALL();
+static std::string protoOutputStreamToByteString(android::util::ProtoOutputStream& proto) {
+ if (!proto.size()) return "";
- std::lock_guard<std::mutex> lock(mLock);
- outStats->clear();
- outStats->reserve(mGlobalStats.size());
-
- interceptSystemDriverStatsLocked();
-
- for (const auto& ele : mGlobalStats) {
- outStats->emplace_back(ele.second);
+ std::string byteString;
+ sp<android::util::ProtoReader> reader = proto.data();
+ while (reader->readBuffer() != nullptr) {
+ const size_t toRead = reader->currentToRead();
+ byteString.append((char*)reader->readBuffer(), toRead);
+ reader->move(toRead);
}
- mGlobalStats.clear();
+ if (byteString.size() != proto.size()) return "";
+
+ return byteString;
}
-void GpuStats::pullAppStats(std::vector<GpuStatsAppInfo>* outStats) {
+static std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
+ if (value.empty()) return "";
+
+ android::util::ProtoOutputStream proto;
+ for (const auto& ele : value) {
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ 1 /* field id */,
+ (long long)ele);
+ }
+
+ return protoOutputStreamToByteString(proto);
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* data) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mLock);
- outStats->clear();
- outStats->reserve(mAppStats.size());
- for (const auto& ele : mAppStats) {
- outStats->emplace_back(ele.second);
+ if (data) {
+ for (const auto& ele : mAppStats) {
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
+ AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
+ AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+
+ std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
+ AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+ bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
+ AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+ bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
+ AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+ AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
+ AStatsEvent_writeBool(event, ele.second.falsePrerotation);
+ AStatsEvent_writeBool(event, ele.second.gles1InUse);
+ AStatsEvent_build(event);
+ }
}
mAppStats.clear();
+
+ return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullGlobalInfoAtom(AStatsEventList* data) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mLock);
+ // flush cpuVulkanVersion and glesVersion to builtin driver stats
+ interceptSystemDriverStatsLocked();
+
+ if (data) {
+ for (const auto& ele : mGlobalStats) {
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
+ AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
+ AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
+ AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+ AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
+ AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
+ AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
+ AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
+ AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
+ AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
+ AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
+ AStatsEvent_writeInt32(event, ele.second.glesVersion);
+ AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
+ AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
+ AStatsEvent_build(event);
+ }
+ }
+
+ mGlobalStats.clear();
+
+ return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAtomCallback(int32_t atomTag,
+ AStatsEventList* data,
+ void* cookie) {
+ ATRACE_CALL();
+
+ GpuStats* pGpuStats = reinterpret_cast<GpuStats*>(cookie);
+ if (atomTag == android::util::GPU_STATS_GLOBAL_INFO) {
+ return pGpuStats->pullGlobalInfoAtom(data);
+ } else if (atomTag == android::util::GPU_STATS_APP_INFO) {
+ return pGpuStats->pullAppInfoAtom(data);
+ }
+
+ return AStatsManager_PULL_SKIP;
}
} // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
similarity index 64%
rename from services/gpuservice/gpustats/GpuStats.h
rename to services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 656b181..8ca4e4e 100644
--- a/services/gpuservice/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -16,41 +16,50 @@
#pragma once
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <stats_pull_atom_callback.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
#include <mutex>
#include <unordered_map>
#include <vector>
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
namespace android {
class GpuStats {
public:
- GpuStats() = default;
- ~GpuStats() = default;
+ GpuStats();
+ ~GpuStats();
- // Insert new gpu stats into global stats and app stats.
- void insert(const std::string& driverPackageName, const std::string& driverVersionName,
- uint64_t driverVersionCode, int64_t driverBuildTime,
- const std::string& appPackageName, const int32_t vulkanVersion,
- GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
+ // Insert new gpu driver stats into global stats and app stats.
+ void insertDriverStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName,
+ const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime);
// Insert target stats into app stats or potentially global stats as well.
void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value);
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
- // Pull gpu global stats
- void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats);
- // Pull gpu app stats
- void pullAppStats(std::vector<GpuStatsAppInfo>* outStats);
// This limits the worst case number of loading times tracked.
static const size_t MAX_NUM_LOADING_TIMES = 50;
private:
+ // Friend class for testing.
+ friend class TestableGpuStats;
+
+ // Native atom puller callback registered in statsd.
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+ AStatsEventList* data,
+ void* cookie);
+ // Pull global into into global atom.
+ AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data);
+ // Pull app into into app atom.
+ AStatsManager_PullAtomCallbackReturn pullAppInfoAtom(AStatsEventList* data);
// Dump global stats
void dumpGlobalLocked(std::string* result);
// Dump app stats
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
new file mode 100644
index 0000000..538506d
--- /dev/null
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+cc_test {
+ name: "gpuservice_unittest",
+ test_suites: ["device-tests"],
+ sanitize: {
+ address: true,
+ },
+ srcs: [
+ "GpuStatsTest.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libgfxstats",
+ "libgraphicsenv",
+ "liblog",
+ "libstatslog",
+ "libstatspull",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
new file mode 100644
index 0000000..66f51c7
--- /dev/null
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for gpuservice_unittest">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="gpuservice_unittest" />
+ </test>
+</configuration>
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
new file mode 100644
index 0000000..37ebeae
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gpustats/GpuStats.h>
+#include <gtest/gtest.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuStats.h"
+
+namespace android {
+namespace {
+
+using testing::HasSubstr;
+
+// clang-format off
+#define BUILTIN_DRIVER_PKG_NAME "system"
+#define BUILTIN_DRIVER_VER_NAME "0"
+#define BUILTIN_DRIVER_VER_CODE 0
+#define BUILTIN_DRIVER_BUILD_TIME 123
+#define UPDATED_DRIVER_PKG_NAME "updated"
+#define UPDATED_DRIVER_VER_NAME "1"
+#define UPDATED_DRIVER_VER_CODE 1
+#define UPDATED_DRIVER_BUILD_TIME 234
+#define VULKAN_VERSION 345
+#define APP_PKG_NAME_1 "testapp1"
+#define APP_PKG_NAME_2 "testapp2"
+#define DRIVER_LOADING_TIME_1 678
+#define DRIVER_LOADING_TIME_2 789
+#define DRIVER_LOADING_TIME_3 891
+
+enum InputCommand : int32_t {
+ DUMP_ALL = 0,
+ DUMP_GLOBAL = 1,
+ DUMP_APP = 2,
+ DUMP_ALL_THEN_CLEAR = 3,
+ DUMP_GLOBAL_THEN_CLEAR = 4,
+ DUMP_APP_THEN_CLEAR = 5,
+};
+// clang-format on
+
+class GpuStatsTest : public testing::Test {
+public:
+ GpuStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~GpuStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ std::string inputCommand(InputCommand cmd);
+
+ void SetUp() override {
+ mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
+ mGlesVersion = property_get_int32("ro.opengles.version", 0);
+ }
+
+ std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
+ int32_t mCpuVulkanVersion = 0;
+ int32_t mGlesVersion = 0;
+};
+
+std::string GpuStatsTest::inputCommand(InputCommand cmd) {
+ std::string result;
+ Vector<String16> args;
+
+ switch (cmd) {
+ case InputCommand::DUMP_ALL:
+ break;
+ case InputCommand::DUMP_GLOBAL:
+ args.push_back(String16("--global"));
+ break;
+ case InputCommand::DUMP_APP:
+ args.push_back(String16("--app"));
+ break;
+ case InputCommand::DUMP_ALL_THEN_CLEAR:
+ args.push_back(String16("--clear"));
+ break;
+ case InputCommand::DUMP_GLOBAL_THEN_CLEAR:
+ args.push_back(String16("--global"));
+ args.push_back(String16("--clear"));
+ break;
+ case InputCommand::DUMP_APP_THEN_CLEAR:
+ args.push_back(String16("--app"));
+ args.push_back(String16("--clear"));
+ break;
+ }
+
+ mGpuStats->dump(args, &result);
+ return result;
+}
+
+TEST_F(GpuStatsTest, statsEmptyByDefault) {
+ ASSERT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertBuiltinDriverStats) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ std::string expectedResult = "driverPackageName = " + std::string(BUILTIN_DRIVER_PKG_NAME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverVersionName = " + std::string(BUILTIN_DRIVER_VER_NAME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverBuildTime = " + std::to_string(BUILTIN_DRIVER_BUILD_TIME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingCount = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingFailureCount = 0"));
+ expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_1);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+ expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+ expectedResult = "glDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_1);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertUpdatedDriverStats) {
+ mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+ UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+ VULKAN_VERSION, GpuStatsInfo::Driver::VULKAN_UPDATED, false,
+ DRIVER_LOADING_TIME_2);
+
+ std::string expectedResult = "driverPackageName = " + std::string(UPDATED_DRIVER_PKG_NAME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverVersionName = " + std::string(UPDATED_DRIVER_VER_NAME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "driverBuildTime = " + std::to_string(UPDATED_DRIVER_BUILD_TIME);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingCount = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingFailureCount = 1"));
+ expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_2);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+ expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+ expectedResult = "vkDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_2);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertAngleDriverStats) {
+ mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+ UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+ VULKAN_VERSION, GpuStatsInfo::Driver::ANGLE, true,
+ DRIVER_LOADING_TIME_3);
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingCount = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingFailureCount = 0"));
+ std::string expectedResult = "angleDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_3);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canDump3dApiVersion) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ std::string expectedResult = "vulkanVersion = " + std::to_string(VULKAN_VERSION);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "cpuVulkanVersion = " + std::to_string(mCpuVulkanVersion);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+ expectedResult = "glesVersion = " + std::to_string(mGlesVersion);
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canNotInsertTargetStatsBeforeProperSetup) {
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+}
+
+TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_ALL_THEN_CLEAR).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpGlobalBeforeClearGlobal) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL_THEN_CLEAR).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpAppBeforeClearApp) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP_THEN_CLEAR).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+}
+
+TEST_F(GpuStatsTest, skipPullInvalidAtom) {
+ TestableGpuStats testableGpuStats(mGpuStats.get());
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+ EXPECT_TRUE(testableGpuStats.makePullAtomCallback(-1) == AStatsManager_PULL_SKIP);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullGlobalAtom) {
+ TestableGpuStats testableGpuStats(mGpuStats.get());
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+ EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO) ==
+ AStatsManager_PULL_SUCCESS);
+
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullAppAtom) {
+ TestableGpuStats testableGpuStats(mGpuStats.get());
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+ EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_APP_INFO) ==
+ AStatsManager_PULL_SUCCESS);
+
+ EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+ EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuStats.h b/services/gpuservice/tests/unittests/TestableGpuStats.h
new file mode 100644
index 0000000..4ea564c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuStats.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <gpustats/GpuStats.h>
+#include <stdint.h>
+
+namespace android {
+
+class TestableGpuStats {
+public:
+ explicit TestableGpuStats(GpuStats *gpuStats) : mGpuStats(gpuStats) {}
+
+ AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atomTag) {
+ return mGpuStats->pullAtomCallback(atomTag, nullptr, mGpuStats);
+ }
+
+private:
+ GpuStats *mGpuStats;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f6b5935..308e93a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -83,6 +83,7 @@
srcs: [
"InputListener.cpp",
"InputReaderBase.cpp",
+ "InputThread.cpp",
],
shared_libs: [
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+ explicit InputThreadImpl(std::function<void()> loop)
+ : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+ ~InputThreadImpl() {}
+
+private:
+ std::function<void()> mThreadLoop;
+
+ bool threadLoop() override {
+ mThreadLoop();
+ return true;
+ }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+ : mName(name), mThreadWake(wake) {
+ mThread = new InputThreadImpl(loop);
+ mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+ mThread->requestExit();
+ if (mThreadWake) {
+ mThreadWake();
+ }
+ mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+ return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 246e735..9a6ef21 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -203,9 +203,10 @@
const nsecs_t currentTime = now();
MotionEvent event;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ 1 /* xScale */, 1 /* yScale */,
/* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 14bcbf7..f2b95e7 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -325,24 +325,6 @@
return dispatchEntry;
}
-// --- InputDispatcherThread ---
-
-class InputDispatcher::InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(InputDispatcher* dispatcher)
- : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {}
-
- ~InputDispatcherThread() {}
-
-private:
- InputDispatcher* mDispatcher;
-
- virtual bool threadLoop() override {
- mDispatcher->dispatchOnce();
- return true;
- }
-};
-
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -367,8 +349,6 @@
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
-
- mThread = new InputDispatcherThread(this);
}
InputDispatcher::~InputDispatcher() {
@@ -387,25 +367,21 @@
}
status_t InputDispatcher::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+ return OK;
}
status_t InputDispatcher::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputDispatcher cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mLooper is waiting for a long timeout.
- mThread->requestExit();
- mLooper->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputDispatcher::dispatchOnce() {
@@ -2410,7 +2386,7 @@
status = connection->inputPublisher
.publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
keyEntry->source, keyEntry->displayId,
- dispatchEntry->resolvedAction,
+ INVALID_HMAC, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags, keyEntry->keyCode,
keyEntry->scanCode, keyEntry->metaState,
keyEntry->repeatCount, keyEntry->downTime,
@@ -2424,26 +2400,28 @@
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry->pointerCoords;
- // Set the X and Y offset depending on the input source.
- float xOffset, yOffset;
+ // Set the X and Y offset and X and Y scale depending on the input source.
+ float xOffset = 0.0f, yOffset = 0.0f;
+ float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- float wxs = dispatchEntry->windowXScale;
- float wys = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * wxs;
- yOffset = dispatchEntry->yOffset * wys;
- if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+ xScale = dispatchEntry->windowXScale;
+ yScale = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * xScale;
+ yOffset = dispatchEntry->yOffset * yScale;
+ if (globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+ // Don't apply window scale here since we don't want scale to affect raw
+ // coordinates. The scale will be sent back to the client and applied
+ // later when requesting relative coordinates.
+ scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+ 1 /* windowYScale */);
}
usingCoords = scaledCoords;
}
} else {
- xOffset = 0.0f;
- yOffset = 0.0f;
-
// We don't want the dispatch target to know.
if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2457,13 +2435,13 @@
status = connection->inputPublisher
.publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
motionEntry->source, motionEntry->displayId,
- dispatchEntry->resolvedAction,
+ INVALID_HMAC, dispatchEntry->resolvedAction,
motionEntry->actionButton,
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
- motionEntry->classification, xOffset, yOffset,
- motionEntry->xPrecision,
+ motionEntry->classification, xScale, yScale,
+ xOffset, yOffset, motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
@@ -2708,6 +2686,19 @@
connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
options.mode);
#endif
+
+ InputTarget target;
+ sp<InputWindowHandle> windowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+ if (windowHandle != nullptr) {
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ }
+ target.inputChannel = connection->inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
for (size_t i = 0; i < cancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
@@ -2733,18 +2724,6 @@
}
}
- InputTarget target;
- sp<InputWindowHandle> windowHandle =
- getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- if (windowHandle != nullptr) {
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
target, InputTarget::FLAG_DISPATCH_AS_IS);
@@ -2754,6 +2733,65 @@
startDispatchCycleLocked(currentTime, connection);
}
+void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
+ const sp<Connection>& connection) {
+ if (connection->status == Connection::STATUS_BROKEN) {
+ return;
+ }
+
+ nsecs_t currentTime = now();
+
+ std::vector<EventEntry*> downEvents =
+ connection->inputState.synthesizePointerDownEvents(currentTime);
+
+ if (downEvents.empty()) {
+ return;
+ }
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
+ connection->getInputChannelName().c_str(), downEvents.size());
+#endif
+
+ InputTarget target;
+ sp<InputWindowHandle> windowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+ if (windowHandle != nullptr) {
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ }
+ target.inputChannel = connection->inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+ for (EventEntry* downEventEntry : downEvents) {
+ switch (downEventEntry->type) {
+ case EventEntry::Type::MOTION: {
+ logOutboundMotionDetails("down - ",
+ static_cast<const MotionEntry&>(*downEventEntry));
+ break;
+ }
+
+ case EventEntry::Type::KEY:
+ case EventEntry::Type::FOCUS:
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+ EventEntry::typeToString(downEventEntry->type));
+ break;
+ }
+ }
+
+ enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
+ target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+ downEventEntry->release();
+ }
+
+ startDispatchCycleLocked(currentTime, connection);
+}
+
MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
@@ -2930,8 +2968,9 @@
accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
KeyEvent event;
- event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
- args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
+ event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action,
+ flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime,
+ args->eventTime);
android::base::Timer t;
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -3024,9 +3063,10 @@
mLock.unlock();
MotionEvent event;
- event.initialize(args->deviceId, args->source, args->displayId, args->action,
- args->actionButton, args->flags, args->edgeFlags, args->metaState,
- args->buttonState, args->classification, 0, 0, args->xPrecision,
+ event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC,
+ args->action, args->actionButton, args->flags, args->edgeFlags,
+ args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
+ 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
args->yPrecision, args->xCursorPosition, args->yCursorPosition,
args->downTime, args->eventTime, args->pointerCount,
args->pointerProperties, args->pointerCoords);
@@ -3126,7 +3166,7 @@
accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
/*byref*/ keyCode, /*byref*/ metaState);
keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
- keyEvent.getDisplayId(), action, flags, keyCode,
+ keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
keyEvent.getDownTime(), keyEvent.getEventTime());
@@ -3790,11 +3830,12 @@
sp<Connection> fromConnection = getConnectionLocked(fromToken);
sp<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
- fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
+ fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions
options(CancelationOptions::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
+ synthesizePointerDownEventsForConnectionLocked(toConnection);
}
if (DEBUG_FOCUS) {
@@ -4682,8 +4723,8 @@
KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
KeyEvent event;
- event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags,
- entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
+ event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action,
+ entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
entry.downTime, entry.eventTime);
return event;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a4ba0de..d2aea80 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,6 +25,7 @@
#include "InputDispatcherPolicyInterface.h"
#include "InputState.h"
#include "InputTarget.h"
+#include "InputThread.h"
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
@@ -124,8 +125,7 @@
STALE,
};
- class InputDispatcherThread;
- sp<InputDispatcherThread> mThread;
+ std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
android::InputDispatcherConfiguration mConfig;
@@ -417,6 +417,9 @@
const CancelationOptions& options)
REQUIRES(mLock);
+ void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
+ REQUIRES(mLock);
+
// Splitting motion events across windows.
MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index c43e304..053598a 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -145,10 +145,13 @@
// Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
return true;
}
+
if (index >= 0) {
MotionMemento& memento = mMotionMementos[index];
- memento.setPointers(entry);
- return true;
+ if (memento.firstNewPointerIdx < 0) {
+ memento.setPointers(entry);
+ return true;
+ }
}
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion pointer up/down or move event: "
@@ -249,6 +252,17 @@
}
}
+void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ if (other.firstNewPointerIdx < 0) {
+ other.firstNewPointerIdx = other.pointerCount;
+ }
+ other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
+ other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+ other.pointerCount++;
+ }
+}
+
std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
std::vector<EventEntry*> events;
@@ -282,27 +296,87 @@
return events;
}
+std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
+ std::vector<EventEntry*> events;
+ for (MotionMemento& memento : mMotionMementos) {
+ if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
+ continue;
+ }
+
+ if (memento.firstNewPointerIdx < 0) {
+ continue;
+ }
+
+ uint32_t pointerCount = 0;
+ PointerProperties pointerProperties[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+
+ // We will deliver all pointers the target already knows about
+ for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
+ pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+ pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerCount++;
+ }
+
+ // We will send explicit events for all pointers the target doesn't know about
+ for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
+ i < memento.pointerCount; i++) {
+
+ pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+ pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerCount++;
+
+ // Down only if the first pointer, pointer down otherwise
+ const int32_t action = (pointerCount <= 1)
+ ? AMOTION_EVENT_ACTION_DOWN
+ : AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId,
+ memento.policyFlags, action, 0 /*actionButton*/,
+ memento.flags, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ pointerCount, pointerProperties,
+ pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
+ }
+
+ memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
+ }
+
+ return events;
+}
+
void InputState::clear() {
mKeyMementos.clear();
mMotionMementos.clear();
mFallbackKeys.clear();
}
-void InputState::copyPointerStateTo(InputState& other) const {
+void InputState::mergePointerStateTo(InputState& other) {
for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos[i];
+ MotionMemento& memento = mMotionMementos[i];
+ // Since we support split pointers we need to merge touch events
+ // from the same source + device + screen.
if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
- for (size_t j = 0; j < other.mMotionMementos.size();) {
- const MotionMemento& otherMemento = other.mMotionMementos[j];
+ bool merged = false;
+ for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
+ MotionMemento& otherMemento = other.mMotionMementos[j];
if (memento.deviceId == otherMemento.deviceId &&
memento.source == otherMemento.source &&
memento.displayId == otherMemento.displayId) {
- other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
- } else {
- j += 1;
+ memento.mergePointerStateTo(otherMemento);
+ merged = true;
+ break;
}
}
- other.mMotionMementos.push_back(memento);
+ if (!merged) {
+ memento.firstNewPointerIdx = 0;
+ other.mMotionMementos.push_back(memento);
+ }
}
}
}
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index a93f486..08266ae 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -24,6 +24,8 @@
namespace android::inputdispatcher {
+static constexpr int32_t INVALID_POINTER_INDEX = -1;
+
/* Tracks dispatched key and motion event state so that cancellation events can be
* synthesized when events are dropped. */
class InputState {
@@ -52,11 +54,14 @@
std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
const CancelationOptions& options);
+ // Synthesizes down events for the current state.
+ std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
+
// Clears the current state.
void clear();
- // Copies pointer-related parts of the input state to another instance.
- void copyPointerStateTo(InputState& other) const;
+ // Merges pointer-related parts of the input state into another instance.
+ void mergePointerStateTo(InputState& other);
// Gets the fallback key associated with a keycode.
// Returns -1 if none.
@@ -97,10 +102,13 @@
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
+ // Track for which pointers the target doesn't know about.
+ int32_t firstNewPointerIdx = INVALID_POINTER_INDEX;
bool hovering;
uint32_t policyFlags;
void setPointers(const MotionEntry& entry);
+ void mergePointerStateTo(MotionMemento& other) const;
};
std::vector<KeyMemento> mKeyMementos;
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_INPUT_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+ explicit InputThread(std::string name, std::function<void()> loop,
+ std::function<void()> wake = nullptr);
+ virtual ~InputThread();
+
+ bool isCallingThread();
+
+private:
+ std::string mName;
+ std::function<void()> mThreadWake;
+ sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..2023c6e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -49,25 +49,6 @@
namespace android {
-// --- InputReader::InputReaderThread ---
-
-/* Thread that reads raw events from the event hub and processes them, endlessly. */
-class InputReader::InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(InputReader* reader)
- : Thread(/* canCallJava */ true), mReader(reader) {}
-
- ~InputReaderThread() {}
-
-private:
- InputReader* mReader;
-
- bool threadLoop() override {
- mReader->loopOnce();
- return true;
- }
-};
-
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -83,7 +64,6 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
- mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -100,25 +80,21 @@
}
status_t InputReader::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+ return OK;
}
status_t InputReader::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputReader cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mEventHub is waiting for a long timeout.
- mThread->requestExit();
- mEventHub->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputReader::loopOnce() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5024906..02957cd 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,6 +21,7 @@
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
+#include "InputThread.h"
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -116,8 +117,7 @@
friend class ContextImpl;
private:
- class InputReaderThread;
- sp<InputReaderThread> mThread;
+ std::unique_ptr<InputThread> mThread;
Mutex mLock;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index b66caca..3b20173 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -761,7 +761,11 @@
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr || viewportChanged) {
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
- mPointerController->setDisplayViewport(mViewport);
+ // Set the DisplayViewport for the PointerController to the default pointer display
+ // that is recommended by the configuration before using it.
+ std::optional<DisplayViewport> defaultViewport =
+ mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+ mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport));
}
} else {
mPointerController.clear();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 98ebf50..27db8f5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -225,18 +225,18 @@
KeyEvent event;
// Rejects undefined key actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- /*action*/ -1, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+ ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- AKEY_EVENT_ACTION_MULTIPLE, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -260,10 +260,10 @@
constexpr MotionClassification classification = MotionClassification::NONE;
// Rejects undefined motion actions.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -271,24 +271,24 @@
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too large.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -296,24 +296,24 @@
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too large.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -321,20 +321,20 @@
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with 0 pointers.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -343,10 +343,10 @@
// Rejects motion events with invalid pointer ids.
pointerProperties[0].id = -1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -354,10 +354,10 @@
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -367,10 +367,10 @@
// Rejects motion events with duplicate pointer ids.
pointerProperties[0].id = 1;
pointerProperties[1].id = 1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -594,12 +594,40 @@
expectedFlags);
}
- void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
+ expectedFlags);
+ }
+
+ void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
+ expectedFlags);
+ }
+
+ void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
}
- void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionPointerDown(int32_t pointerIdx,
+ int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
+ | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
expectedFlags);
}
@@ -645,9 +673,9 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key down event.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC,
+ AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+ /* repeatCount */ 0, currentTime, currentTime);
// Inject event until dispatch out.
return dispatcher->injectInputEvent(
@@ -674,10 +702,12 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
- event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0,
+ event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0,
+ /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
- /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime,
+ /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
+ /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+ currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
// Inject event until dispatch out.
@@ -921,6 +951,161 @@
0 /*expectedFlags*/);
}
+TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+ // Create a couple of windows
+ sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+ "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+ "Second Window", ADISPLAY_ID_DEFAULT);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+ // Send down to the first window
+ NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&downMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Transfer touch focus to the second window
+ mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // The first window gets cancel and the second gets down
+ firstWindow->consumeMotionCancel();
+ secondWindow->consumeMotionDown();
+
+ // Send up event to the second window
+ NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets no events and the second gets up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+ PointF touchPoint = {10, 10};
+
+ // Create a couple of windows
+ sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+ "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+ "Second Window", ADISPLAY_ID_DEFAULT);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+ // Send down to the first window
+ NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
+ mDispatcher->notifyMotion(&downMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send pointer down to the first window
+ NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+ mDispatcher->notifyMotion(&pointerDownMotionArgs);
+ // Only the first window should get the pointer down event
+ firstWindow->consumeMotionPointerDown(1);
+ secondWindow->assertNoEvents();
+
+ // Transfer touch focus to the second window
+ mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // The first window gets cancel and the second gets down and pointer down
+ firstWindow->consumeMotionCancel();
+ secondWindow->consumeMotionDown();
+ secondWindow->consumeMotionPointerDown(1);
+
+ // Send pointer up to the second window
+ NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+ | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets nothing and the second gets pointer up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionPointerUp(1);
+
+ // Send up event to the second window
+ NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets nothing and the second gets up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+ "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setFrame(Rect(0, 0, 600, 400));
+ firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+ | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+ "Second Window", ADISPLAY_ID_DEFAULT);
+ secondWindow->setFrame(Rect(0, 400, 600, 800));
+ secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+ | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+ PointF pointInFirst = {300, 200};
+ PointF pointInSecond = {300, 600};
+
+ // Send down to the first window
+ NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
+ mDispatcher->notifyMotion(&firstDownMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send down to the second window
+ NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&secondDownMotionArgs);
+ // The first window gets a move and the second a down
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionDown();
+
+ // Transfer touch focus to the second window
+ mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // The first window gets cancel and the new gets pointer down (it already saw down)
+ firstWindow->consumeMotionCancel();
+ secondWindow->consumeMotionPointerDown(1);
+
+ // Send pointer up to the second window
+ NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+ | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets nothing and the second gets pointer up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionPointerUp(1);
+
+ // Send up event to the second window
+ NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets nothing and the second gets up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionUp();
+}
+
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1c9a4af..5246c78 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -42,6 +42,7 @@
"libbinder",
"libsensor",
"libsensorprivacy",
+ "libprotoutil",
"libcrypto",
"libbase",
"libhidlbase",
@@ -52,6 +53,8 @@
static_libs: ["android.hardware.sensors@1.0-convert"],
+ generated_headers: ["framework-cppstream-protos"],
+
// our public headers depend on libsensor and libsensorprivacy
export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
}
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 207b097..d7ca6e1 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -17,6 +17,8 @@
#include "RecentEventLogger.h"
#include "SensorServiceUtils.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <utils/Timers.h>
#include <inttypes.h>
@@ -84,6 +86,40 @@
return std::string(buffer.string());
}
+/**
+ * Dump debugging information as android.service.SensorEventsProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void RecentEventLogger::dump(util::ProtoOutputStream* proto) const {
+ using namespace service::SensorEventsProto;
+ std::lock_guard<std::mutex> lk(mLock);
+
+ proto->write(RecentEventsLog::RECENT_EVENTS_COUNT, int(mRecentEvents.size()));
+ for (int i = mRecentEvents.size() - 1; i >= 0; --i) {
+ const auto& ev = mRecentEvents[i];
+ const uint64_t token = proto->start(RecentEventsLog::EVENTS);
+ proto->write(Event::TIMESTAMP_SEC, float(ev.mEvent.timestamp) / 1e9f);
+ proto->write(Event::WALL_TIMESTAMP_MS, ev.mWallTime.tv_sec * 1000LL
+ + ns2ms(ev.mWallTime.tv_nsec));
+
+ if (mMaskData) {
+ proto->write(Event::MASKED, true);
+ } else {
+ if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
+ proto->write(Event::INT64_DATA, int64_t(ev.mEvent.u64.step_counter));
+ } else {
+ for (size_t k = 0; k < mEventSize; ++k) {
+ proto->write(Event::FLOAT_ARRAY, ev.mEvent.data[k]);
+ }
+ }
+ }
+ proto->end(token);
+ }
+}
+
void RecentEventLogger::setFormat(std::string format) {
if (format == "mask_data" ) {
mMaskData = true;
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index 67378b7..3a2ae77 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -48,6 +48,7 @@
// Dumpable interface
virtual std::string dump() const override;
+ virtual void dump(util::ProtoOutputStream* proto) const override;
virtual void setFormat(std::string format) override;
protected:
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index c7a8f5b..33f940f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -21,6 +21,8 @@
#include "SensorService.h"
#include <android-base/logging.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <sensors/convert.h>
#include <cutils/atomic.h>
#include <utils/Errors.h>
@@ -39,6 +41,7 @@
using android::hardware::hidl_vec;
using android::hardware::Return;
using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
+using android::util::ProtoOutputStream;
namespace android {
// ---------------------------------------------------------------------------
@@ -396,6 +399,43 @@
return result.string();
}
+/**
+ * Dump debugging information as android.service.SensorDeviceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorDevice::dump(ProtoOutputStream* proto) const {
+ using namespace service::SensorDeviceProto;
+ if (mSensors == nullptr) {
+ proto->write(INITIALIZED , false);
+ return;
+ }
+ proto->write(INITIALIZED , true);
+ proto->write(TOTAL_SENSORS , int(mSensorList.size()));
+ proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+
+ Mutex::Autolock _l(mLock);
+ for (const auto & s : mSensorList) {
+ int32_t handle = s.handle;
+ const Info& info = mActivationCount.valueFor(handle);
+ if (info.numActiveClients() == 0) continue;
+
+ uint64_t token = proto->start(SENSORS);
+ proto->write(SensorProto::HANDLE , handle);
+ proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ const BatchParams& params = info.batchParams[j];
+ proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+ }
+ proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+ proto->end(token);
+ }
+}
+
ssize_t SensorDevice::getSensorList(sensor_t const** list) {
*list = &mSensorList[0];
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d2c6994..33aa7d6 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -123,7 +123,8 @@
bool isSensorActive(int handle) const;
// Dumpable
- virtual std::string dump() const;
+ virtual std::string dump() const override;
+ virtual void dump(util::ProtoOutputStream* proto) const override;
private:
friend class Singleton<SensorDevice>;
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index cd0ea5d..106efd6 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -16,12 +16,16 @@
#include "SensorDevice.h"
#include "SensorDirectConnection.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <hardware/sensors.h>
#define UNUSED(x) (void)(x)
namespace android {
+using util::ProtoOutputStream;
+
SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
const String16& opPackageName)
@@ -64,6 +68,27 @@
}
}
+/**
+ * Dump debugging information as android.service.SensorDirectConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorDirectConnection::dump(ProtoOutputStream* proto) const {
+ using namespace service::SensorDirectConnectionProto;
+ Mutex::Autolock _l(mConnectionLock);
+ proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).string()));
+ proto->write(HAL_CHANNEL_HANDLE, getHalChannelHandle());
+ proto->write(NUM_SENSOR_ACTIVATED, int(mActivated.size()));
+ for (auto &i : mActivated) {
+ uint64_t token = proto->start(SENSORS);
+ proto->write(SensorProto::SENSOR, i.first);
+ proto->write(SensorProto::RATE, i.second);
+ proto->end(token);
+ }
+}
+
sp<BitTube> SensorService::SensorDirectConnection::getSensorChannel() const {
return nullptr;
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index 5c398a8..ead08d3 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -40,6 +40,7 @@
const sensors_direct_mem_t *mem, int32_t halChannelHandle,
const String16& opPackageName);
void dump(String8& result) const;
+ void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
int32_t getHalChannelHandle() const;
bool isEquivalent(const sensors_direct_mem_t *mem) const;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0e40940..9a13c00 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -17,6 +17,8 @@
#include <sys/socket.h>
#include <utils/threads.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <sensor/SensorEventQueue.h>
#include "vec.h"
@@ -110,6 +112,51 @@
#endif
}
+/**
+ * Dump debugging information as android.service.SensorEventConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) const {
+ using namespace service::SensorEventConnectionProto;
+ Mutex::Autolock _l(mConnectionLock);
+
+ if (!mService->isWhiteListedPackage(getPackageName())) {
+ proto->write(OPERATING_MODE, OP_MODE_RESTRICTED);
+ } else if (mDataInjectionMode) {
+ proto->write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+ } else {
+ proto->write(OPERATING_MODE, OP_MODE_NORMAL);
+ }
+ proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+ proto->write(WAKE_LOCK_REF_COUNT, int32_t(mWakeLockRefCount));
+ proto->write(UID, int32_t(mUid));
+ proto->write(CACHE_SIZE, int32_t(mCacheSize));
+ proto->write(MAX_CACHE_SIZE, int32_t(mMaxCacheSize));
+ for (size_t i = 0; i < mSensorInfo.size(); ++i) {
+ const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
+ const uint64_t token = proto->start(FLUSH_INFOS);
+ proto->write(FlushInfoProto::SENSOR_NAME,
+ std::string(mService->getSensorName(mSensorInfo.keyAt(i))));
+ proto->write(FlushInfoProto::SENSOR_HANDLE, mSensorInfo.keyAt(i));
+ proto->write(FlushInfoProto::FIRST_FLUSH_PENDING, flushInfo.mFirstFlushPending);
+ proto->write(FlushInfoProto::PENDING_FLUSH_EVENTS_TO_SEND,
+ flushInfo.mPendingFlushEventsToSend);
+ proto->end(token);
+ }
+#if DEBUG_CONNECTIONS
+ proto->write(EVENTS_RECEIVED, mEventsReceived);
+ proto->write(EVENTS_SENT, mEventsSent);
+ proto->write(EVENTS_CACHE, mEventsSentFromCache);
+ proto->write(EVENTS_DROPPED, mEventsReceived - (mEventsSentFromCache + mEventsSent +
+ mCacheSize));
+ proto->write(TOTAL_ACKS_NEEDED, mTotalAcksNeeded);
+ proto->write(TOTAL_ACKS_RECEIVED, mTotalAcksReceived);
+#endif
+}
+
bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
Mutex::Autolock _l(mConnectionLock);
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index fd881cb..caf5d7c 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -62,6 +62,7 @@
bool removeSensor(int32_t handle);
void setFirstFlushPending(int32_t handle, bool value);
void dump(String8& result);
+ void dump(util::ProtoOutputStream* proto) const;
bool needsWakeLock();
void resetWakeLockRefCount();
String8 getPackageName() const;
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 414f673..e27b52b 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -18,6 +18,9 @@
#include "SensorFusion.h"
#include "SensorService.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+
namespace android {
// ---------------------------------------------------------------------------
@@ -183,7 +186,7 @@
return mAcc.getMinDelay();
}
-void SensorFusion::dump(String8& result) {
+void SensorFusion::dump(String8& result) const {
const Fusion& fusion_9axis(mFusions[FUSION_9AXIS]);
result.appendFormat("9-axis fusion %s (%zd clients), gyro-rate=%7.2fHz, "
"q=< %g, %g, %g, %g > (%g), "
@@ -235,5 +238,42 @@
fusion_nogyro.getBias().z);
}
+void SensorFusion::dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const {
+ using namespace service::SensorFusionProto::FusionProto;
+ const Fusion& fusion(mFusions[mode]);
+ proto->write(ENABLED, mEnabled[mode]);
+ proto->write(NUM_CLIENTS, (int)mClients[mode].size());
+ proto->write(ESTIMATED_GYRO_RATE, mEstimatedGyroRate);
+ proto->write(ATTITUDE_X, fusion.getAttitude().x);
+ proto->write(ATTITUDE_Y, fusion.getAttitude().y);
+ proto->write(ATTITUDE_Z, fusion.getAttitude().z);
+ proto->write(ATTITUDE_W, fusion.getAttitude().w);
+ proto->write(ATTITUDE_LENGTH, length(fusion.getAttitude()));
+ proto->write(BIAS_X, fusion.getBias().x);
+ proto->write(BIAS_Y, fusion.getBias().y);
+ proto->write(BIAS_Z, fusion.getBias().z);
+}
+
+/**
+ * Dump debugging information as android.service.SensorFusionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorFusion::dump(util::ProtoOutputStream* proto) const {
+ uint64_t token = proto->start(service::SensorFusionProto::FUSION_9AXIS);
+ dumpFusion(FUSION_9AXIS, proto);
+ proto->end(token);
+
+ token = proto->start(service::SensorFusionProto::FUSION_NOMAG);
+ dumpFusion(FUSION_NOMAG, proto);
+ proto->end(token);
+
+ token = proto->start(service::SensorFusionProto::FUSION_NOGYRO);
+ dumpFusion(FUSION_NOGYRO, proto);
+ proto->end(token);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index 8c0fbf9..66a7290 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -90,7 +90,9 @@
float getPowerUsage(int mode=FUSION_9AXIS) const;
int32_t getMinDelay() const;
- void dump(String8& result);
+ void dump(String8& result) const;
+ void dump(util::ProtoOutputStream* proto) const;
+ void dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const;
};
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index aa306d8..0ce32cc 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -16,6 +16,8 @@
#include "SensorList.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <hardware/sensors.h>
#include <utils/String8.h>
@@ -203,6 +205,64 @@
return std::string(result.string());
}
+/**
+ * Dump debugging information as android.service.SensorListProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorList::dump(util::ProtoOutputStream* proto) const {
+ using namespace service::SensorListProto;
+ using namespace service::SensorListProto::SensorProto;
+
+ forEachSensor([&proto] (const Sensor& s) -> bool {
+ const uint64_t token = proto->start(SENSORS);
+ proto->write(HANDLE, s.getHandle());
+ proto->write(NAME, std::string(s.getName().string()));
+ proto->write(VENDOR, std::string(s.getVendor().string()));
+ proto->write(VERSION, s.getVersion());
+ proto->write(STRING_TYPE, std::string(s.getStringType().string()));
+ proto->write(TYPE, s.getType());
+ proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ?
+ s.getRequiredPermission().string() : ""));
+ proto->write(FLAGS, int(s.getFlags()));
+ switch (s.getReportingMode()) {
+ case AREPORTING_MODE_CONTINUOUS:
+ proto->write(REPORTING_MODE, RM_CONTINUOUS);
+ break;
+ case AREPORTING_MODE_ON_CHANGE:
+ proto->write(REPORTING_MODE, RM_ON_CHANGE);
+ break;
+ case AREPORTING_MODE_ONE_SHOT:
+ proto->write(REPORTING_MODE, RM_ONE_SHOT);
+ break;
+ case AREPORTING_MODE_SPECIAL_TRIGGER:
+ proto->write(REPORTING_MODE, RM_SPECIAL_TRIGGER);
+ break;
+ default:
+ proto->write(REPORTING_MODE, RM_UNKNOWN);
+ }
+ proto->write(MAX_DELAY_US, s.getMaxDelay());
+ proto->write(MIN_DELAY_US, s.getMinDelay());
+ proto->write(FIFO_MAX_EVENT_COUNT, int(s.getFifoMaxEventCount()));
+ proto->write(FIFO_RESERVED_EVENT_COUNT, int(s.getFifoReservedEventCount()));
+ proto->write(IS_WAKEUP, s.isWakeUpSensor());
+ proto->write(DATA_INJECTION_SUPPORTED, s.isDataInjectionSupported());
+ proto->write(IS_DYNAMIC, s.isDynamicSensor());
+ proto->write(HAS_ADDITIONAL_INFO, s.hasAdditionalInfo());
+ proto->write(HIGHEST_RATE_LEVEL, s.getHighestDirectReportRateLevel());
+ proto->write(ASHMEM, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM));
+ proto->write(GRALLOC, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC));
+ proto->write(MIN_VALUE, s.getMinValue());
+ proto->write(MAX_VALUE, s.getMaxValue());
+ proto->write(RESOLUTION, s.getResolution());
+ proto->write(POWER_USAGE, s.getPowerUsage());
+ proto->end(token);
+ return true;
+ });
+}
+
SensorList::~SensorList() {
}
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 6b90ad9..8424b22 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -71,6 +71,7 @@
// Dumpable interface
virtual std::string dump() const override;
+ virtual void dump(util::ProtoOutputStream* proto) const override;
virtual ~SensorList();
private:
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index 5411515..a34a65b 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -17,10 +17,14 @@
#ifndef ANDROID_SENSOR_REGISTRATION_INFO_H
#define ANDROID_SENSOR_REGISTRATION_INFO_H
-#include "SensorServiceUtils.h"
-#include <utils/Thread.h>
+#include <ctime>
#include <iomanip>
#include <sstream>
+#include <utils/Thread.h>
+
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+#include "SensorServiceUtils.h"
namespace android {
@@ -30,7 +34,7 @@
public:
SensorRegistrationInfo() : mPackageName() {
mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
- mHour = mMin = mSec = INT8_MIN;
+ mRealtimeSec = 0;
mActivated = false;
}
@@ -47,25 +51,26 @@
mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
- time_t rawtime = time(nullptr);
- struct tm * timeinfo = localtime(&rawtime);
- mHour = static_cast<int8_t>(timeinfo->tm_hour);
- mMin = static_cast<int8_t>(timeinfo->tm_min);
- mSec = static_cast<int8_t>(timeinfo->tm_sec);
+ timespec curTime;
+ clock_gettime(CLOCK_REALTIME_COARSE, &curTime);
+ mRealtimeSec = curTime.tv_sec;
}
static bool isSentinel(const SensorRegistrationInfo& info) {
- return (info.mHour == INT8_MIN &&
- info.mMin == INT8_MIN &&
- info.mSec == INT8_MIN);
+ return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0);
}
// Dumpable interface
virtual std::string dump() const override {
+ struct tm* timeinfo = localtime(&mRealtimeSec);
+ const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour);
+ const int8_t min = static_cast<int8_t>(timeinfo->tm_min);
+ const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec);
+
std::ostringstream ss;
- ss << std::setfill('0') << std::setw(2) << static_cast<int>(mHour) << ":"
- << std::setw(2) << static_cast<int>(mMin) << ":"
- << std::setw(2) << static_cast<int>(mSec)
+ ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":"
+ << std::setw(2) << static_cast<int>(min) << ":"
+ << std::setw(2) << static_cast<int>(sec)
<< (mActivated ? " +" : " -")
<< " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
<< std::setfill(' ') << " pid=" << std::setw(5) << mPid
@@ -77,6 +82,25 @@
return ss.str();
}
+ /**
+ * Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message
+ * using ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+ virtual void dump(util::ProtoOutputStream* proto) const override {
+ using namespace service::SensorRegistrationInfoProto;
+ proto->write(TIMESTAMP_SEC, int64_t(mRealtimeSec));
+ proto->write(SENSOR_HANDLE, mSensorHandle);
+ proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+ proto->write(PID, int32_t(mPid));
+ proto->write(UID, int32_t(mUid));
+ proto->write(SAMPLING_RATE_US, mSamplingRateUs);
+ proto->write(MAX_REPORT_LATENCY_US, mMaxReportLatencyUs);
+ proto->write(ACTIVATED, mActivated);
+ }
+
private:
int32_t mSensorHandle;
String8 mPackageName;
@@ -85,8 +109,7 @@
int64_t mSamplingRateUs;
int64_t mMaxReportLatencyUs;
bool mActivated;
- int8_t mHour, mMin, mSec;
-
+ time_t mRealtimeSec;
};
} // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c2e1204..914a4cb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <android/content/pm/IPackageManagerNative.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <binder/ActivityManager.h>
#include <binder/BinderService.h>
#include <binder/IServiceManager.h>
@@ -404,6 +406,8 @@
// Transition to data injection mode supported only from NORMAL mode.
return INVALID_OPERATION;
}
+ } else if (args.size() == 1 && args[0] == String16("--proto")) {
+ return dumpProtoLocked(fd, &connLock);
} else if (!mSensors.hasAnySensor()) {
result.append("No Sensors on the device\n");
result.appendFormat("devInitCheck : %d\n", SensorDevice::getInstance().initCheck());
@@ -506,6 +510,128 @@
return NO_ERROR;
}
+/**
+ * Dump debugging information as android.service.SensorServiceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const {
+ using namespace service::SensorServiceProto;
+ util::ProtoOutputStream proto;
+ proto.write(INIT_STATUS, int(SensorDevice::getInstance().initCheck()));
+ if (!mSensors.hasAnySensor()) {
+ return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+ }
+ const bool privileged = IPCThreadState::self()->getCallingUid() == 0;
+
+ timespec curTime;
+ clock_gettime(CLOCK_REALTIME, &curTime);
+ proto.write(CURRENT_TIME_MS, curTime.tv_sec * 1000 + ns2ms(curTime.tv_nsec));
+
+ // Write SensorDeviceProto
+ uint64_t token = proto.start(SENSOR_DEVICE);
+ SensorDevice::getInstance().dump(&proto);
+ proto.end(token);
+
+ // Write SensorListProto
+ token = proto.start(SENSORS);
+ mSensors.dump(&proto);
+ proto.end(token);
+
+ // Write SensorFusionProto
+ token = proto.start(FUSION_STATE);
+ SensorFusion::getInstance().dump(&proto);
+ proto.end(token);
+
+ // Write SensorEventsProto
+ token = proto.start(SENSOR_EVENTS);
+ for (auto&& i : mRecentEvent) {
+ sp<SensorInterface> s = mSensors.getInterface(i.first);
+ if (!i.second->isEmpty()) {
+ i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
+ "normal" : "mask_data");
+ const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
+ proto.write(service::SensorEventsProto::RecentEventsLog::NAME,
+ std::string(s->getSensor().getName().string()));
+ i.second->dump(&proto);
+ proto.end(mToken);
+ }
+ }
+ proto.end(token);
+
+ // Write ActiveSensorProto
+ SensorDevice& dev = SensorDevice::getInstance();
+ for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
+ int handle = mActiveSensors.keyAt(i);
+ if (dev.isSensorActive(handle)) {
+ token = proto.start(ACTIVE_SENSORS);
+ proto.write(service::ActiveSensorProto::NAME,
+ std::string(getSensorName(handle).string()));
+ proto.write(service::ActiveSensorProto::HANDLE, handle);
+ proto.write(service::ActiveSensorProto::NUM_CONNECTIONS,
+ int(mActiveSensors.valueAt(i)->getNumConnections()));
+ proto.end(token);
+ }
+ }
+
+ proto.write(SOCKET_BUFFER_SIZE, int(mSocketBufferSize));
+ proto.write(SOCKET_BUFFER_SIZE_IN_EVENTS, int(mSocketBufferSize / sizeof(sensors_event_t)));
+ proto.write(WAKE_LOCK_ACQUIRED, mWakeLockAcquired);
+
+ switch(mCurrentOperatingMode) {
+ case NORMAL:
+ proto.write(OPERATING_MODE, OP_MODE_NORMAL);
+ break;
+ case RESTRICTED:
+ proto.write(OPERATING_MODE, OP_MODE_RESTRICTED);
+ proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+ break;
+ case DATA_INJECTION:
+ proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+ proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+ break;
+ default:
+ proto.write(OPERATING_MODE, OP_MODE_UNKNOWN);
+ }
+ proto.write(SENSOR_PRIVACY, mSensorPrivacyPolicy->isSensorPrivacyEnabled());
+
+ // Write repeated SensorEventConnectionProto
+ const auto& activeConnections = connLock->getActiveConnections();
+ for (size_t i = 0; i < activeConnections.size(); i++) {
+ token = proto.start(ACTIVE_CONNECTIONS);
+ activeConnections[i]->dump(&proto);
+ proto.end(token);
+ }
+
+ // Write repeated SensorDirectConnectionProto
+ const auto& directConnections = connLock->getDirectConnections();
+ for (size_t i = 0 ; i < directConnections.size() ; i++) {
+ token = proto.start(DIRECT_CONNECTIONS);
+ directConnections[i]->dump(&proto);
+ proto.end(token);
+ }
+
+ // Write repeated SensorRegistrationInfoProto
+ const int startIndex = mNextSensorRegIndex;
+ int curr = startIndex;
+ do {
+ const SensorRegistrationInfo& reg_info = mLastNSensorRegistrations[curr];
+ if (SensorRegistrationInfo::isSentinel(reg_info)) {
+ // Ignore sentinel, proceed to next item.
+ curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+ continue;
+ }
+ token = proto.start(PREVIOUS_REGISTRATIONS);
+ reg_info.dump(&proto);
+ proto.end(token);
+ curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+ } while (startIndex != curr);
+
+ return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+}
+
void SensorService::disableAllSensors() {
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
disableAllSensorsLocked(&connLock);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index fa23da0..7d17dda 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -286,6 +286,7 @@
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
virtual status_t dump(int fd, const Vector<String16>& args);
+ status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
String8 getSensorName(int handle) const;
bool isVirtualSensor(int handle) const;
sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
diff --git a/services/sensorservice/SensorServiceUtils.h b/services/sensorservice/SensorServiceUtils.h
index 1558feb..49457cf 100644
--- a/services/sensorservice/SensorServiceUtils.h
+++ b/services/sensorservice/SensorServiceUtils.h
@@ -21,11 +21,17 @@
#include <string>
namespace android {
+
+namespace util {
+class ProtoOutputStream;
+}
+
namespace SensorServiceUtil {
class Dumpable {
public:
virtual std::string dump() const = 0;
+ virtual void dump(util::ProtoOutputStream*) const {}
virtual void setFormat(std::string ) {}
virtual ~Dumpable() {}
};
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
new file mode 100644
index 0000000..1ce0524
--- /dev/null
+++ b/services/stats/Android.bp
@@ -0,0 +1,22 @@
+cc_library_shared {
+ name: "libstatshidl",
+ srcs: [
+ "StatsHal.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "android.frameworks.stats@1.0",
+ "libhidlbase",
+ "liblog",
+ "libstatslog",
+ "libstatssocket",
+ "libutils",
+ ],
+ export_include_dirs: [
+ "include/",
+ ],
+ local_include_dirs: [
+ "include/stats",
+ ],
+ vintf_fragments: ["android.frameworks.stats@1.0-service.xml"]
+}
diff --git a/services/stats/OWNERS b/services/stats/OWNERS
new file mode 100644
index 0000000..a61babf
--- /dev/null
+++ b/services/stats/OWNERS
@@ -0,0 +1,9 @@
+jeffreyhuang@google.com
+joeo@google.com
+jtnguyen@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
new file mode 100644
index 0000000..80c3b65
--- /dev/null
+++ b/services/stats/StatsHal.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#define LOG_TAG "StatsHal"
+
+#include <log/log.h>
+#include <statslog.h>
+
+#include "StatsHal.h"
+
+namespace android {
+namespace frameworks {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+StatsHal::StatsHal() {}
+
+hardware::Return<void> StatsHal::reportSpeakerImpedance(
+ const SpeakerImpedance& speakerImpedance) {
+ android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
+ speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
+ android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
+ hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) {
+ android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
+ int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
+ physicalDropDetected.freefallDuration);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCycles) {
+ std::vector<int32_t> buckets = chargeCycles.cycleBucket;
+ int initialSize = buckets.size();
+ for (int i = 0; i < 10 - initialSize; i++) {
+ buckets.push_back(-1); // Push -1 for buckets that do not exist.
+ }
+ android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
+ buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
+ buckets[9]);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
+ int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
+ batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
+ batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
+ batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) {
+ android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
+ batteryCausedShutdown.voltageMicroV);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
+ const UsbPortOverheatEvent& usbPortOverheatEvent) {
+ android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+ usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
+ usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
+ usbPortOverheatEvent.timeToInactive);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) {
+ android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
+ speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
+ speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
+ std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
+ if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
+ ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+ return hardware::Void();
+ }
+ if (reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ return hardware::Void();
+ }
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, vendorAtom.atomId);
+ AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
+ for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
+ switch (vendorAtom.values[i].getDiscriminator()) {
+ case VendorAtom::Value::hidl_discriminator::intValue:
+ AStatsEvent_writeInt32(event, vendorAtom.values[i].intValue());
+ break;
+ case VendorAtom::Value::hidl_discriminator::longValue:
+ AStatsEvent_writeInt64(event, vendorAtom.values[i].longValue());
+ break;
+ case VendorAtom::Value::hidl_discriminator::floatValue:
+ AStatsEvent_writeFloat(event, vendorAtom.values[i].floatValue());
+ break;
+ case VendorAtom::Value::hidl_discriminator::stringValue:
+ AStatsEvent_writeString(event, vendorAtom.values[i].stringValue().c_str());
+ break;
+ }
+ }
+ AStatsEvent_build(event);
+ AStatsEvent_write(event);
+ AStatsEvent_release(event);
+
+ return hardware::Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace stats
+} // namespace frameworks
+} // namespace android
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
new file mode 100644
index 0000000..bb02f66
--- /dev/null
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.stats</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IStats</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
new file mode 100644
index 0000000..071e54f
--- /dev/null
+++ b/services/stats/include/stats/StatsHal.h
@@ -0,0 +1,99 @@
+/*
+ * 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/frameworks/stats/1.0/IStats.h>
+#include <android/frameworks/stats/1.0/types.h>
+
+#include <stats_event.h>
+
+using namespace android::frameworks::stats::V1_0;
+
+namespace android {
+namespace frameworks {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+using android::hardware::Return;
+
+/**
+* Implements the Stats HAL
+*/
+class StatsHal : public IStats {
+public:
+ StatsHal();
+
+ /**
+ * Binder call to get SpeakerImpedance atom.
+ */
+ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
+
+ /**
+ * Binder call to get HardwareFailed atom.
+ */
+ virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
+
+ /**
+ * Binder call to get PhysicalDropDetected atom.
+ */
+ virtual Return<void> reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) override;
+
+ /**
+ * Binder call to get ChargeCyclesReported atom.
+ */
+ virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+
+ /**
+ * Binder call to get BatteryHealthSnapshot atom.
+ */
+ virtual Return<void> reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
+
+ /**
+ * Binder call to get SlowIo atom.
+ */
+ virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
+
+ /**
+ * Binder call to get BatteryCausedShutdown atom.
+ */
+ virtual Return<void> reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) override;
+
+ /**
+ * Binder call to get UsbPortOverheatEvent atom.
+ */
+ virtual Return<void> reportUsbPortOverheatEvent(
+ const UsbPortOverheatEvent& usbPortOverheatEvent) override;
+
+ /**
+ * Binder call to get Speech DSP state atom.
+ */
+ virtual Return<void> reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) override;
+
+ /**
+ * Binder call to get vendor atom.
+ */
+ virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace stats
+} // namespace frameworks
+} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 35d0215..f4f45be 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -26,8 +26,6 @@
#include "BufferLayer.h"
#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -66,8 +64,7 @@
BufferLayer::BufferLayer(const LayerCreationArgs& args)
: Layer(args),
mTextureName(args.textureName),
- mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
- compositionengine::LayerCreationArgs{this})} {
+ mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {
ALOGV("Creating Layer %s", getDebugName());
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
@@ -184,7 +181,7 @@
bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
(isSecure() && !targetSettings.isSecure);
const State& s(getDrawingState());
- LayerFE::LayerSettings& layer = *result;
+ compositionengine::LayerFE::LayerSettings& layer = *result;
if (!blackOutLayer) {
layer.source.buffer.buffer = mBufferInfo.mBuffer;
layer.source.buffer.isOpaque = isOpaque(s);
@@ -282,17 +279,29 @@
mBufferInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
}
-void BufferLayer::latchPerFrameState(
- compositionengine::LayerFECompositionState& compositionState) const {
- Layer::latchPerFrameState(compositionState);
+sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
+ return asLayerFE();
+}
+
+compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() {
+ return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const {
+ return mCompositionState.get();
+}
+
+void BufferLayer::preparePerFrameCompositionState() {
+ Layer::preparePerFrameCompositionState();
// Sideband layers
- if (compositionState.sidebandStream.get()) {
- compositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ auto* compositionState = editCompositionState();
+ if (compositionState->sidebandStream.get()) {
+ compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
} else {
// Normal buffer layers
- compositionState.hdrMetadata = mBufferInfo.mHdrMetadata;
- compositionState.compositionType = mPotentialCursor
+ compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
+ compositionState->compositionType = mPotentialCursor
? Hwc2::IComposerClient::Composition::CURSOR
: Hwc2::IComposerClient::Composition::DEVICE;
}
@@ -641,10 +650,6 @@
return Rect(bufWidth, bufHeight);
}
-std::shared_ptr<compositionengine::Layer> BufferLayer::getCompositionLayer() const {
- return mCompositionLayer;
-}
-
FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
const State& s(getDrawingState());
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b2398a8..4085b52 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -54,7 +54,8 @@
// Overriden from Layer
// -----------------------------------------------------------------------
public:
- std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+ sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+ compositionengine::LayerFECompositionState* editCompositionState() override;
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
@@ -175,8 +176,9 @@
/*
* compositionengine::LayerFE overrides
*/
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(nsecs_t) override;
- void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ void preparePerFrameCompositionState() override;
std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
@@ -210,7 +212,7 @@
// and its parent layer is not bounded
Rect getBufferSize(const State& s) const override;
- std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+ std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
};
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e85281d..e65064b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -23,7 +23,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BufferQueueLayer.h"
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFECompositionState.h>
#include <gui/BufferQueueConsumer.h>
#include <system/window.h>
@@ -117,23 +116,24 @@
"relative to expectedPresent %" PRId64,
getDebugName(), addedTime, expectedPresentTime);
+ if (!isPlausible) {
+ mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence());
+ }
+
const bool isDue = addedTime < expectedPresentTime;
return isDue || !isPlausible;
}
-bool BufferQueueLayer::setFrameRate(float frameRate) {
+bool BufferQueueLayer::setFrameRate(FrameRate frameRate) {
float oldFrameRate = 0.f;
status_t result = mConsumer->getFrameRate(&oldFrameRate);
- bool frameRateChanged = result < 0 || frameRate != oldFrameRate;
- mConsumer->setFrameRate(frameRate);
+ bool frameRateChanged = result < 0 || frameRate.rate != oldFrameRate;
+ mConsumer->setFrameRate(frameRate.rate);
return frameRateChanged;
}
-std::optional<float> BufferQueueLayer::getFrameRate() const {
- if (mLatchedFrameRate > 0.f || mLatchedFrameRate == FRAME_RATE_NO_VOTE)
- return mLatchedFrameRate;
-
- return {};
+Layer::FrameRate BufferQueueLayer::getFrameRate() const {
+ return FrameRate(mLatchedFrameRate, Layer::FrameRateCompatibility::Default);
}
// -----------------------------------------------------------------------
@@ -157,7 +157,14 @@
// able to be latched. To avoid this, grab this buffer anyway.
return true;
}
- return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ const bool fenceSignaled =
+ mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ if (!fenceSignaled) {
+ mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+ TimeStats::LatchSkipReason::LateAcquire);
+ }
+
+ return fenceSignaled;
}
bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
@@ -215,9 +222,9 @@
if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
// mSidebandStreamChanged was changed to false
mSidebandStream = mConsumer->getSidebandStream();
- auto& layerCompositionState = getCompositionLayer()->editFEState();
- layerCompositionState.sidebandStream = mSidebandStream;
- if (layerCompositionState.sidebandStream != nullptr) {
+ auto* layerCompositionState = editCompositionState();
+ layerCompositionState->sidebandStream = mSidebandStream;
+ if (layerCompositionState->sidebandStream != nullptr) {
setTransactionFlags(eTransactionNeeded);
mFlinger->setTransactionFlags(eTraversalNeeded);
}
@@ -351,8 +358,8 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer =
mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
- auto& layerCompositionState = getCompositionLayer()->editFEState();
- layerCompositionState.buffer = mBufferInfo.mBuffer;
+ auto* layerCompositionState = editCompositionState();
+ layerCompositionState->buffer = mBufferInfo.mBuffer;
if (mBufferInfo.mBuffer == nullptr) {
// this can only happen if the very first buffer was rejected.
@@ -372,18 +379,19 @@
return NO_ERROR;
}
-void BufferQueueLayer::latchPerFrameState(
- compositionengine::LayerFECompositionState& compositionState) const {
- BufferLayer::latchPerFrameState(compositionState);
- if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+void BufferQueueLayer::preparePerFrameCompositionState() {
+ BufferLayer::preparePerFrameCompositionState();
+
+ auto* compositionState = editCompositionState();
+ if (compositionState->compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
return;
}
- compositionState.buffer = mBufferInfo.mBuffer;
- compositionState.bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+ compositionState->buffer = mBufferInfo.mBuffer;
+ compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
? 0
: mBufferInfo.mBufferSlot;
- compositionState.acquireFence = mBufferInfo.mFence;
+ compositionState->acquireFence = mBufferInfo.mFence;
}
// -----------------------------------------------------------------------
@@ -433,6 +441,7 @@
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", getDebugName());
+ break;
}
}
@@ -461,6 +470,7 @@
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", getDebugName());
+ break;
}
}
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 2bd1e3d..626af4b 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -56,8 +56,8 @@
bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
- bool setFrameRate(float frameRate) override;
- std::optional<float> getFrameRate() const override;
+ bool setFrameRate(FrameRate frameRate) override;
+ FrameRate getFrameRate() const override;
// -----------------------------------------------------------------------
@@ -85,7 +85,7 @@
status_t updateActiveBuffer() override;
status_t updateFrameNumber(nsecs_t latchTime) override;
- void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ void preparePerFrameCompositionState() override;
sp<Layer> createClone() override;
void onFrameAvailable(const BufferItem& item);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index cd4227c..371b802 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -27,7 +27,6 @@
#include <limits>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFECompositionState.h>
#include <gui/BufferQueue.h>
#include <private/gui/SyncFeatures.h>
@@ -98,6 +97,8 @@
}
}
+ mPreviousReleaseFence = releaseFence;
+
// Prevent tracing the same release multiple times.
if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
@@ -111,15 +112,34 @@
mTransformHint = orientation;
}
-void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
+void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->transformHint = mTransformHint;
+ handle->dequeueReadyTime = dequeueReadyTime;
}
mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
mDrawingState.callbackHandles);
mDrawingState.callbackHandles = {};
+
+ const sp<Fence>& releaseFence(mPreviousReleaseFence);
+ std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (mPreviousFrameNumber != 0) {
+ mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+ std::move(releaseFenceTime));
+ }
+ }
+}
+
+void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+ const CompositorTiming& compositorTiming) {
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->gpuCompositionDoneFence = glDoneFence;
+ handle->compositorTiming = compositorTiming;
+ }
}
bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
@@ -138,6 +158,8 @@
!mLayerDetached;
}
+/* TODO: vhau uncomment once deferred transaction migration complete in
+ * WindowManager
void BufferStateLayer::pushPendingState() {
if (!mCurrentState.modified) {
return;
@@ -145,13 +167,12 @@
mPendingStates.push_back(mCurrentState);
ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
}
+*/
bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
- const bool stateUpdateAvailable = !mPendingStates.empty();
- while (!mPendingStates.empty()) {
- popPendingState(stateToCommit);
- }
- mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
+ mCurrentStateModified = mCurrentState.modified;
+ bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+ mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
mCurrentState.modified = false;
return stateUpdateAvailable;
}
@@ -233,13 +254,27 @@
return true;
}
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
- nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
+bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+ nsecs_t desiredPresentTime) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mAcquireTimeline.updateSignalTimes();
+ std::shared_ptr<FenceTime> acquireFenceTime =
+ std::make_shared<FenceTime>((acquireFence ? acquireFence : Fence::NO_FENCE));
+ NewFrameEventsEntry newTimestamps = {mCurrentState.frameNumber, postedTime, desiredPresentTime,
+ acquireFenceTime};
+ mFrameEventHistory.setProducerWantsEvents();
+ mFrameEventHistory.addQueue(newTimestamps);
+ return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
+ nsecs_t postTime, nsecs_t desiredPresentTime,
+ const client_cache_t& clientCacheId) {
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
}
- mFrameCounter++;
+ mCurrentState.frameNumber++;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
@@ -247,15 +282,17 @@
setTransactionFlags(eTransactionNeeded);
const int32_t layerId = getSequence();
- mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+ mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+ postTime);
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
- FrameTracer::FrameEvent::POST);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
+ postTime, FrameTracer::FrameEvent::POST);
+ desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this,
- desiredPresentTime <= 0 ? 0 : desiredPresentTime);
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+ addFrameEvent(acquireFence, postTime, desiredPresentTime);
return true;
}
@@ -402,7 +439,14 @@
return true;
}
- return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+ const bool fenceSignaled =
+ getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+ if (!fenceSignaled) {
+ mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+ TimeStats::LatchSkipReason::LateAcquire);
+ }
+
+ return fenceSignaled;
}
bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
@@ -413,8 +457,15 @@
return mCurrentState.desiredPresentTime <= expectedPresentTime;
}
+bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->refreshStartTime = refreshStartTime;
+ }
+ return BufferLayer::onPreComposition(refreshStartTime);
+}
+
uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
- return mFrameNumber;
+ return mDrawingState.frameNumber;
}
bool BufferStateLayer::getAutoRefresh() const {
@@ -430,9 +481,8 @@
if (mSidebandStreamChanged.exchange(false)) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
- LOG_ALWAYS_FATAL_IF(!getCompositionLayer());
mSidebandStream = s.sidebandStream;
- getCompositionLayer()->editFEState().sidebandStream = mSidebandStream;
+ editCompositionState()->sidebandStream = mSidebandStream;
if (mSidebandStream != nullptr) {
setTransactionFlags(eTransactionNeeded);
mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -491,16 +541,15 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
return BAD_VALUE;
}
for (auto& handle : mDrawingState.callbackHandles) {
handle->latchTime = latchTime;
+ handle->frameNumber = mDrawingState.frameNumber;
}
- mFrameNumber = mFrameCounter;
-
if (!SyncFeatures::getInstance().useNativeFenceSync()) {
// Bind the new buffer to the GL texture.
//
@@ -517,11 +566,13 @@
}
const uint64_t bufferID = getCurrentBufferId();
- mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+ mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime,
FrameTracer::FrameEvent::ACQUIRE_FENCE);
- mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
+ mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
mCurrentStateModified = false;
@@ -539,29 +590,33 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer = s.buffer;
mBufferInfo.mFence = s.acquireFence;
- auto& layerCompositionState = getCompositionLayer()->editFEState();
- layerCompositionState.buffer = mBufferInfo.mBuffer;
+ editCompositionState()->buffer = mBufferInfo.mBuffer;
return NO_ERROR;
}
-status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+status_t BufferStateLayer::updateFrameNumber(nsecs_t latchTime) {
// TODO(marissaw): support frame history events
mPreviousFrameNumber = mCurrentFrameNumber;
- mCurrentFrameNumber = mFrameNumber;
+ mCurrentFrameNumber = mDrawingState.frameNumber;
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+ }
return NO_ERROR;
}
-void BufferStateLayer::latchPerFrameState(
- compositionengine::LayerFECompositionState& compositionState) const {
- BufferLayer::latchPerFrameState(compositionState);
- if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+void BufferStateLayer::preparePerFrameCompositionState() {
+ BufferLayer::preparePerFrameCompositionState();
+
+ auto* compositionState = editCompositionState();
+ if (compositionState->compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
return;
}
- compositionState.buffer = mBufferInfo.mBuffer;
- compositionState.bufferSlot = mBufferInfo.mBufferSlot;
- compositionState.acquireFence = mBufferInfo.mFence;
+ compositionState->buffer = mBufferInfo.mBuffer;
+ compositionState->bufferSlot = mBufferInfo.mBufferSlot;
+ compositionState->acquireFence = mBufferInfo.mFence;
}
void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
@@ -687,6 +742,33 @@
layer->setInitialValuesForClone(this);
return layer;
}
+
+Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerState();
+ if (parentState.radius > 0) {
+ ui::Transform t = getActiveTransform(getDrawingState());
+ t = t.inverse();
+ parentState.cropRect = t.transform(parentState.cropRect);
+ // The rounded corners shader only accepts 1 corner radius for performance reasons,
+ // but a transform matrix can define horizontal and vertical scales.
+ // Let's take the average between both of them and pass into the shader, practically we
+ // never do this type of transformation on windows anyway.
+ parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+ return parentState;
+ }
+ }
+ const float radius = getDrawingState().cornerRadius;
+ const State& s(getDrawingState());
+ if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
+ return RoundedCornerState();
+ return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
+ static_cast<float>(s.active.transform.ty()),
+ static_cast<float>(s.active.transform.tx() + s.active.w),
+ static_cast<float>(s.active.transform.ty() + s.active.h)),
+ radius);
+}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 9427283..539442a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -45,12 +45,17 @@
void setTransformHint(uint32_t orientation) const override;
void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
+ void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+ const CompositorTiming& compositorTiming) override;
+
bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
return flags;
}
- void pushPendingState() override;
+ /*TODO:vhau return to using BufferStateLayer override once WM
+ * has removed deferred transactions!
+ void pushPendingState() override;*/
bool applyPendingStates(Layer::State* stateToCommit) override;
uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
@@ -68,8 +73,8 @@
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
bool setFrame(const Rect& frame) override;
- bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, nsecs_t desiredPresentTime,
- const client_cache_t& clientCacheId) override;
+ bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
+ nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -78,6 +83,8 @@
bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
void forceSendCallbacks() override;
+ bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+ nsecs_t requestedPresentTime) override;
// Override to ignore legacy layer state properties that are not used by BufferStateLayer
bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
@@ -96,6 +103,7 @@
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+ Layer::RoundedCornerState getRoundedCornerState() const override;
// -----------------------------------------------------------------------
@@ -104,11 +112,15 @@
// -----------------------------------------------------------------------
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
+ bool onPreComposition(nsecs_t refreshStartTime) override;
protected:
void gatherBufferInfo() override;
private:
+ bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
+ nsecs_t requestedPresentTime);
+
uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
bool getAutoRefresh() const override;
@@ -125,7 +137,7 @@
status_t updateActiveBuffer() override;
status_t updateFrameNumber(nsecs_t latchTime) override;
- void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ void preparePerFrameCompositionState() override;
sp<Layer> createClone() override;
// Crop that applies to the buffer
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 04854d0..83050c4 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -29,8 +29,6 @@
#include <sys/types.h>
#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <renderengine/RenderEngine.h>
#include <ui/GraphicBuffer.h>
@@ -45,8 +43,7 @@
ColorLayer::ColorLayer(const LayerCreationArgs& args)
: Layer(args),
- mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
- compositionengine::LayerCreationArgs{this})} {}
+ mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {}
ColorLayer::~ColorLayer() = default;
@@ -61,7 +58,7 @@
}
bool ColorLayer::isVisible() const {
- return !isHiddenByPolicy() && getAlpha() > 0.0f;
+ return !isHiddenByPolicy() && getAlpha() > 0.0_hf;
}
bool ColorLayer::setColor(const half3& color) {
@@ -91,20 +88,30 @@
return true;
}
-void ColorLayer::latchPerFrameState(
- compositionengine::LayerFECompositionState& compositionState) const {
- Layer::latchPerFrameState(compositionState);
+void ColorLayer::preparePerFrameCompositionState() {
+ Layer::preparePerFrameCompositionState();
- compositionState.color = getColor();
- compositionState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ auto* compositionState = editCompositionState();
+ compositionState->color = getColor();
+ compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
}
-std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const {
- return mCompositionLayer;
+sp<compositionengine::LayerFE> ColorLayer::getCompositionEngineLayerFE() const {
+ return asLayerFE();
+}
+
+compositionengine::LayerFECompositionState* ColorLayer::editCompositionState() {
+ return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* ColorLayer::getCompositionState() const {
+ return mCompositionState.get();
}
bool ColorLayer::isOpaque(const Layer::State& s) const {
- return (s.flags & layer_state_t::eLayerOpaque) != 0;
+ // Consider the layer to be opaque if its opaque flag is set or its effective
+ // alpha (considering the alpha of its parents as well) is 1.0;
+ return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
}
ui::Dataspace ColorLayer::getDataSpace() const {
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 9246eb2..4deb162 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -28,7 +28,8 @@
explicit ColorLayer(const LayerCreationArgs&);
~ColorLayer() override;
- std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+ sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+ compositionengine::LayerFECompositionState* editCompositionState() override;
const char* getType() const override { return "ColorLayer"; }
bool isVisible() const override;
@@ -45,11 +46,12 @@
/*
* compositionengine::LayerFE overrides
*/
- void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ void preparePerFrameCompositionState() override;
std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
- std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+ std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
sp<Layer> createClone() override;
};
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index a634f2f..2792290 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -51,7 +51,6 @@
"src/DisplaySurface.cpp",
"src/DumpHelpers.cpp",
"src/HwcBufferCache.cpp",
- "src/Layer.cpp",
"src/LayerFECompositionState.cpp",
"src/Output.cpp",
"src/OutputCompositionState.cpp",
@@ -71,7 +70,6 @@
"mock/Display.cpp",
"mock/DisplayColorProfile.cpp",
"mock/DisplaySurface.cpp",
- "mock/Layer.cpp",
"mock/LayerFE.cpp",
"mock/NativeWindow.cpp",
"mock/Output.cpp",
@@ -96,7 +94,6 @@
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
"tests/HwcBufferCacheTest.cpp",
- "tests/LayerTest.cpp",
"tests/MockHWC2.cpp",
"tests/MockHWComposer.cpp",
"tests/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index e3650f3..3faa068 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -32,11 +32,11 @@
namespace compositionengine {
class Display;
-class Layer;
struct CompositionRefreshArgs;
struct DisplayCreationArgs;
struct LayerCreationArgs;
+struct LayerFECompositionState;
/**
* Encapsulates all the interfaces and implementation details for performing
@@ -48,7 +48,8 @@
// Create a composition Display
virtual std::shared_ptr<Display> createDisplay(const DisplayCreationArgs&) = 0;
- virtual std::shared_ptr<Layer> createLayer(const LayerCreationArgs&) = 0;
+ virtual std::unique_ptr<compositionengine::LayerFECompositionState>
+ createLayerFECompositionState() = 0;
virtual HWComposer& getHwComposer() const = 0;
virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 90158c7..4a0d6ee 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -21,15 +21,14 @@
#include <vector>
#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputColorSetting.h>
#include <math/mat4.h>
namespace android::compositionengine {
-using Layers = std::vector<std::shared_ptr<compositionengine::Layer>>;
+using Layers = std::vector<sp<compositionengine::LayerFE>>;
using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
-using RawLayers = std::vector<compositionengine::Layer*>;
/**
* A parameter object for refreshing a set of outputs
@@ -44,7 +43,7 @@
Layers layers;
// All the layers that have queued updates.
- RawLayers layersWithQueuedFrames;
+ Layers layersWithQueuedFrames;
// If true, forces the entire display to be considered dirty and repainted
bool repaintEverything{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
deleted file mode 100644
index 1259c52..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
+++ /dev/null
@@ -1,55 +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 <cstdint>
-#include <string>
-
-#include <utils/StrongPointer.h>
-
-namespace android::compositionengine {
-
-class Display;
-class LayerFE;
-
-struct LayerFECompositionState;
-
-/**
- * A layer contains the output-independent composition state for a front-end
- * Layer
- */
-class Layer {
-public:
- virtual ~Layer();
-
- // Gets the front-end interface for this layer. Can return nullptr if the
- // front-end layer no longer exists.
- virtual sp<LayerFE> getLayerFE() const = 0;
-
- // Gets the raw front-end composition state data for the layer
- // TODO(lpique): Make this protected once it is only internally called.
- virtual const LayerFECompositionState& getFEState() const = 0;
-
- // Allows mutable access to the raw front-end composition state
- // TODO(lpique): Make this protected once it is only internally called.
- virtual LayerFECompositionState& editFEState() = 0;
-
- // Debugging
- virtual void dump(std::string& result) const = 0;
-};
-
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
deleted file mode 100644
index db3312b..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
+++ /dev/null
@@ -1,35 +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 <utils/RefBase.h>
-
-namespace android::compositionengine {
-
-class CompositionEngine;
-class LayerFE;
-
-/**
- * A parameter object for creating Layer instances
- */
-struct LayerCreationArgs {
- // A weak pointer to the front-end layer instance that the new layer will
- // represent.
- wp<LayerFE> layerFE;
-};
-
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 26442d9..912dffd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -44,6 +44,9 @@
// of the front-end layer
class LayerFE : public virtual RefBase {
public:
+ // Gets the raw front-end composition state data for the layer
+ virtual const LayerFECompositionState* getCompositionState() const = 0;
+
// Called before composition starts. Should return true if this layer has
// pending updates which would require an extra display refresh cycle to
// process.
@@ -60,19 +63,18 @@
// content (buffer or color) state for the layer.
GeometryAndContent,
- // Gets the per frame content (buffer or color) state the layer.
+ // Gets the per frame content (buffer or color) state for the layer.
Content,
+
+ // Gets the cursor state for the layer.
+ Cursor,
};
- // Latches the output-independent composition state for the layer. The
+ // Prepares the output-independent composition state for the layer. The
// StateSubset argument selects what portion of the state is actually needed
// by the CompositionEngine code, since computing everything may be
// expensive.
- virtual void latchCompositionState(LayerFECompositionState&, StateSubset) const = 0;
-
- // Latches the minimal bit of state for the cursor for a fast asynchronous
- // update.
- virtual void latchCursorCompositionState(LayerFECompositionState&) const = 0;
+ virtual void prepareCompositionState(StateSubset) = 0;
struct ClientCompositionTargetSettings {
// The clip region, or visible region that is being rendered to
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 1af99c5..40cd3e0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -162,8 +162,10 @@
// The output-independent frame for the cursor
Rect cursorFrame;
+ virtual ~LayerFECompositionState();
+
// Debugging
- void dump(std::string& out) const;
+ virtual void dump(std::string& out) const;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a5da0b1..9622e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -41,7 +41,6 @@
namespace android::compositionengine {
class DisplayColorProfile;
-class Layer;
class LayerFE;
class RenderSurface;
class OutputLayer;
@@ -163,7 +162,8 @@
// Sets the projection state to use
virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+ const Rect& viewport, const Rect& sourceClip,
+ const Rect& destinationClip, bool needsFiltering) = 0;
// Sets the bounds to use
virtual void setBounds(const ui::Size&) = 0;
@@ -215,18 +215,17 @@
virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
// Determines if a layer belongs to the output.
- virtual bool belongsInOutput(const Layer*) const = 0;
+ virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
// Returns a pointer to the output layer corresponding to the given layer on
// this output, or nullptr if the layer does not have one
- virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
+ virtual OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const = 0;
// Immediately clears all layers from the output.
virtual void clearOutputLayers() = 0;
// For tests use only. Creates and appends an OutputLayer into the output.
- virtual OutputLayer* injectOutputLayerForTest(const std::shared_ptr<Layer>&,
- const sp<LayerFE>&) = 0;
+ virtual OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
// Gets the count of output layers managed by this output
virtual size_t getOutputLayerCount() const = 0;
@@ -256,7 +255,7 @@
virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
- virtual void ensureOutputLayerIfVisible(std::shared_ptr<Layer>, CoverageState&) = 0;
+ virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index a466561..007b0e8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -41,7 +41,6 @@
class CompositionEngine;
class Output;
-class Layer;
class LayerFE;
namespace impl {
@@ -61,9 +60,6 @@
// Gets the output which owns this output layer
virtual const Output& getOutput() const = 0;
- // Gets the display-independent layer which this output layer represents
- virtual Layer& getLayer() const = 0;
-
// Gets the front-end layer interface this output layer represents
virtual LayerFE& getLayerFE() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 450b9ca..386808d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -27,8 +27,8 @@
std::shared_ptr<compositionengine::Display> createDisplay(
const compositionengine::DisplayCreationArgs&) override;
- std::shared_ptr<compositionengine::Layer> createLayer(
- const compositionengine::LayerCreationArgs&) override;
+ std::unique_ptr<compositionengine::LayerFECompositionState> createLayerFECompositionState()
+ override;
HWComposer& getHwComposer() const override;
void setHwComposer(std::unique_ptr<HWComposer>) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 39acb37..fb597ce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -73,8 +73,7 @@
virtual void applyLayerRequestsToLayers(const LayerRequests&);
// Internal
- std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const;
+ std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
private:
const bool mIsVirtual;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
deleted file mode 100644
index 46489fb..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
+++ /dev/null
@@ -1,84 +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 <memory>
-
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
-#include <utils/StrongPointer.h>
-
-namespace android::compositionengine {
-
-struct LayerCreationArgs;
-
-namespace impl {
-
-// The implementation class contains the common implementation, but does not
-// actually contain the final layer state.
-class Layer : public virtual compositionengine::Layer {
-public:
- ~Layer() override;
-
- // compositionengine::Layer overrides
- void dump(std::string&) const override;
-
-protected:
- // Implemented by the final implementation for the final state it uses.
- virtual void dumpFEState(std::string&) const = 0;
-};
-
-// This template factory function standardizes the implementation details of the
-// final class using the types actually required by the implementation. This is
-// not possible to do in the base class as those types may not even be visible
-// to the base code.
-template <typename BaseLayer, typename LayerCreationArgs>
-std::shared_ptr<BaseLayer> createLayerTemplated(const LayerCreationArgs& args) {
- class Layer final : public BaseLayer {
- public:
-// Clang incorrectly complains that these are unused.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-local-typedef"
- using LayerFE = std::remove_pointer_t<decltype(
- std::declval<decltype(std::declval<LayerCreationArgs>().layerFE)>().unsafe_get())>;
- using LayerFECompositionState = std::remove_const_t<
- std::remove_reference_t<decltype(std::declval<BaseLayer>().getFEState())>>;
-#pragma clang diagnostic pop
-
- explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {}
- ~Layer() override = default;
-
- private:
- // compositionengine::Layer overrides
- sp<compositionengine::LayerFE> getLayerFE() const override { return mLayerFE.promote(); }
- const LayerFECompositionState& getFEState() const override { return mFrontEndState; }
- LayerFECompositionState& editFEState() override { return mFrontEndState; }
-
- // compositionengine::impl::Layer overrides
- void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); }
-
- const wp<LayerFE> mLayerFE;
- LayerFECompositionState mFrontEndState;
- };
-
- return std::make_shared<Layer>(args);
-}
-
-std::shared_ptr<Layer> createLayer(const LayerCreationArgs&);
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f469e62..d41337c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -39,7 +39,8 @@
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+ const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
+ bool needsFiltering) override;
void setBounds(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
@@ -59,10 +60,9 @@
Region getDirtyRegion(bool repaintEverything) const override;
bool belongsInOutput(std::optional<uint32_t>, bool) const override;
- bool belongsInOutput(const compositionengine::Layer*) const override;
+ bool belongsInOutput(const sp<LayerFE>&) const override;
- compositionengine::OutputLayer* getOutputLayerForLayer(
- compositionengine::Layer*) const override;
+ compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
void setReleasedLayers(ReleasedLayers&&) override;
@@ -72,7 +72,7 @@
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
- void ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer>,
+ void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
compositionengine::Output::CoverageState&) override;
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
@@ -93,9 +93,9 @@
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
protected:
- std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const;
- std::optional<size_t> findCurrentOutputLayerForLayer(compositionengine::Layer*) const;
+ std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
+ std::optional<size_t> findCurrentOutputLayerForLayer(
+ const sp<compositionengine::LayerFE>&) const;
void chooseCompositionStrategy() override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
@@ -107,11 +107,9 @@
void dumpBase(std::string&) const;
// Implemented by the final implementation for the final state it uses.
- virtual compositionengine::OutputLayer* ensureOutputLayer(
- std::optional<size_t>, const std::shared_ptr<compositionengine::Layer>&,
- const sp<LayerFE>&) = 0;
- virtual compositionengine::OutputLayer* injectOutputLayerForTest(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) = 0;
+ virtual compositionengine::OutputLayer* ensureOutputLayer(std::optional<size_t>,
+ const sp<LayerFE>&) = 0;
+ virtual compositionengine::OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
virtual void finalizePendingOutputLayers() = 0;
virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
virtual void dumpState(std::string& out) const = 0;
@@ -180,11 +178,10 @@
};
OutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex,
- const std::shared_ptr<compositionengine::Layer>& layer,
const sp<LayerFE>& layerFE) {
auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())
? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex])
- : BaseOutput::createOutputLayer(layer, layerFE);
+ : BaseOutput::createOutputLayer(layerFE);
auto result = outputLayer.get();
mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
return result;
@@ -201,10 +198,8 @@
void dumpState(std::string& out) const override { mState.dump(out); }
- OutputLayer* injectOutputLayerForTest(
- const std::shared_ptr<compositionengine::Layer>& layer,
- const sp<LayerFE>& layerFE) override {
- auto outputLayer = BaseOutput::createOutputLayer(layer, layerFE);
+ OutputLayer* injectOutputLayerForTest(const sp<LayerFE>& layerFE) override {
+ auto outputLayer = BaseOutput::createOutputLayer(layerFE);
auto result = outputLayer.get();
mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
return result;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index e700b76..66ed2b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -79,8 +79,11 @@
// The logical space user viewport rectangle
Rect viewport;
- // The physical space scissor rectangle
- Rect scissor;
+ // The physical space source clip rectangle
+ Rect sourceClip;
+
+ // The physical space destination clip rectangle
+ Rect destinationClip;
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 95c8afb..79df9b2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -81,7 +81,6 @@
// to the base code.
template <typename BaseOutputLayer>
std::unique_ptr<BaseOutputLayer> createOutputLayerTemplated(const Output& output,
- std::shared_ptr<Layer> layer,
sp<LayerFE> layerFE) {
class OutputLayer final : public BaseOutputLayer {
public:
@@ -93,21 +92,18 @@
std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getState())>>;
using Output = std::remove_const_t<
std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getOutput())>>;
- using Layer = std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayer())>;
using LayerFE =
std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayerFE())>;
#pragma clang diagnostic pop
- OutputLayer(const Output& output, const std::shared_ptr<Layer>& layer,
- const sp<LayerFE>& layerFE)
- : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+ OutputLayer(const Output& output, const sp<LayerFE>& layerFE)
+ : mOutput(output), mLayerFE(layerFE) {}
~OutputLayer() override = default;
private:
// compositionengine::OutputLayer overrides
const Output& getOutput() const override { return mOutput; }
- Layer& getLayer() const override { return *mLayer; }
LayerFE& getLayerFE() const override { return *mLayerFE; }
const OutputLayerCompositionState& getState() const override { return mState; }
OutputLayerCompositionState& editState() override { return mState; }
@@ -116,16 +112,14 @@
void dumpState(std::string& out) const override { mState.dump(out); }
const Output& mOutput;
- const std::shared_ptr<Layer> mLayer;
const sp<LayerFE> mLayerFE;
OutputLayerCompositionState mState;
};
- return std::make_unique<OutputLayer>(output, layer, layerFE);
+ return std::make_unique<OutputLayer>(output, layerFE);
}
std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output&,
- const std::shared_ptr<compositionengine::Layer>&,
const sp<LayerFE>&);
} // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 104e20d..f953d0b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -19,7 +19,7 @@
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
-#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <gmock/gmock.h>
#include <renderengine/RenderEngine.h>
@@ -33,7 +33,8 @@
~CompositionEngine() override;
MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(const DisplayCreationArgs&));
- MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(const LayerCreationArgs&));
+ MOCK_METHOD0(createLayerFECompositionState,
+ std::unique_ptr<compositionengine::LayerFECompositionState>());
MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
deleted file mode 100644
index 4f03cb4..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
+++ /dev/null
@@ -1,39 +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 <compositionengine/Layer.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <gmock/gmock.h>
-
-namespace android::compositionengine::mock {
-
-class Layer : public compositionengine::Layer {
-public:
- Layer();
- virtual ~Layer();
-
- MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
-
- MOCK_CONST_METHOD0(getFEState, const LayerFECompositionState&());
- MOCK_METHOD0(editFEState, LayerFECompositionState&());
-
- MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
-} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 163e302..5c2ad15 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -30,11 +30,11 @@
LayerFE();
virtual ~LayerFE();
+ MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
+
MOCK_METHOD1(onPreComposition, bool(nsecs_t));
- MOCK_CONST_METHOD2(latchCompositionState,
- void(LayerFECompositionState&, compositionengine::LayerFE::StateSubset));
- MOCK_CONST_METHOD1(latchCursorCompositionState, void(LayerFECompositionState&));
+ MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
MOCK_METHOD1(prepareClientComposition,
std::optional<LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index c41302d..346c2d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -18,7 +18,6 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
@@ -37,8 +36,9 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
- MOCK_METHOD6(setProjection,
- void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
+ MOCK_METHOD7(setProjection,
+ void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
+ const Rect&, bool));
MOCK_METHOD1(setBounds, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
@@ -61,14 +61,13 @@
MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
- MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
+ MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
MOCK_CONST_METHOD1(getOutputLayerForLayer,
- compositionengine::OutputLayer*(compositionengine::Layer*));
+ compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
MOCK_METHOD0(clearOutputLayers, void());
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(const std::shared_ptr<compositionengine::Layer>&,
- const sp<compositionengine::LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
@@ -83,8 +82,7 @@
void(const compositionengine::CompositionRefreshArgs&,
compositionengine::Output::CoverageState&));
MOCK_METHOD2(ensureOutputLayerIfVisible,
- void(std::shared_ptr<compositionengine::Layer>,
- compositionengine::Output::CoverageState&));
+ void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 631760a..2ecbad8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -17,7 +17,6 @@
#pragma once
#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
@@ -34,7 +33,6 @@
MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
- MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&());
MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index aeaa18a..6203dc6 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -16,10 +16,10 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/Layer.h>
#include <renderengine/RenderEngine.h>
#include <utils/Trace.h>
@@ -51,9 +51,9 @@
return compositionengine::impl::createDisplay(*this, args);
}
-std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(
- const LayerCreationArgs& args) {
- return compositionengine::impl::createLayer(args);
+std::unique_ptr<compositionengine::LayerFECompositionState>
+CompositionEngine::createLayerFECompositionState() {
+ return std::make_unique<compositionengine::LayerFECompositionState>();
}
HWComposer& CompositionEngine::getHwComposer() const {
@@ -120,8 +120,7 @@
for (auto* layer : output->getOutputLayersOrderedByZ()) {
if (layer->isHardwareCursor()) {
// Latch the cursor composition state from each front-end layer.
- layer->getLayerFE().latchCursorCompositionState(layer->getLayer().editFEState());
-
+ layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
layer->writeCursorPositionToHWC();
}
}
@@ -137,8 +136,7 @@
mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
for (auto& layer : args.layers) {
- sp<compositionengine::LayerFE> layerFE = layer->getLayerFE();
- if (layerFE && layerFE->onPreComposition(mRefreshStartTime)) {
+ if (layer->onPreComposition(mRefreshStartTime)) {
needsAnotherUpdate = true;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ccd6572..1d8a23f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -147,9 +147,8 @@
}
std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
- const std::shared_ptr<compositionengine::Layer>& layer,
const sp<compositionengine::LayerFE>& layerFE) const {
- auto result = impl::createOutputLayer(*this, layer, layerFE);
+ auto result = impl::createOutputLayer(*this, layerFE);
if (result && mId) {
auto& hwc = getCompositionEngine().getHwComposer();
@@ -184,16 +183,18 @@
// Any non-null entries in the current list of layers are layers that are no
// longer going to be visible
- for (auto* layer : getOutputLayersOrderedByZ()) {
- if (!layer) {
+ for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+ if (!outputLayer) {
continue;
}
- sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE());
+ compositionengine::LayerFE* layerFE = &outputLayer->getLayerFE();
const bool hasQueuedFrames =
- std::find(refreshArgs.layersWithQueuedFrames.cbegin(),
- refreshArgs.layersWithQueuedFrames.cend(),
- &layer->getLayer()) != refreshArgs.layersWithQueuedFrames.cend();
+ std::any_of(refreshArgs.layersWithQueuedFrames.cbegin(),
+ refreshArgs.layersWithQueuedFrames.cend(),
+ [layerFE](sp<compositionengine::LayerFE> layerWithQueuedFrames) {
+ return layerFE == layerWithQueuedFrames.get();
+ });
if (hasQueuedFrames) {
releasedLayers.emplace_back(layerFE);
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
deleted file mode 100644
index ecacaee..0000000
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ /dev/null
@@ -1,43 +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.
- */
-
-#include <android-base/stringprintf.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/impl/Layer.h>
-
-namespace android::compositionengine {
-
-Layer::~Layer() = default;
-
-namespace impl {
-
-std::shared_ptr<Layer> createLayer(const LayerCreationArgs& args) {
- return compositionengine::impl::createLayerTemplated<Layer>(args);
-}
-
-Layer::~Layer() = default;
-
-void Layer::dump(std::string& out) const {
- auto layerFE = getLayerFE();
- android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
- layerFE ? layerFE->getDebugName() : "<unknown>");
- out.append(" frontend:\n");
- dumpFEState(out);
-}
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 016084f..3e0f803 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -32,6 +32,8 @@
} // namespace
+LayerFECompositionState::~LayerFECompositionState() = default;
+
void LayerFECompositionState::dump(std::string& out) const {
out.append(" ");
dumpVal(out, "isSecure", isSecure);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 55371df..a389bf3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -20,7 +20,6 @@
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/RenderSurface.h>
@@ -107,11 +106,13 @@
}
void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+ const Rect& viewport, const Rect& sourceClip,
+ const Rect& destinationClip, bool needsFiltering) {
auto& outputState = editState();
outputState.transform = transform;
outputState.orientation = orientation;
- outputState.scissor = scissor;
+ outputState.sourceClip = sourceClip;
+ outputState.destinationClip = destinationClip;
outputState.frame = frame;
outputState.viewport = viewport;
outputState.needsFiltering = needsFiltering;
@@ -265,31 +266,26 @@
(!internalOnly || outputState.layerStackInternal);
}
-bool Output::belongsInOutput(const compositionengine::Layer* layer) const {
- if (!layer) {
- return false;
- }
-
- const auto& layerFEState = layer->getFEState();
- return belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly);
+bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+ const auto* layerFEState = layerFE->getCompositionState();
+ return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
}
std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
- const std::shared_ptr<compositionengine::Layer>& layer, const sp<LayerFE>& layerFE) const {
- return impl::createOutputLayer(*this, layer, layerFE);
+ const sp<LayerFE>& layerFE) const {
+ return impl::createOutputLayer(*this, layerFE);
}
-compositionengine::OutputLayer* Output::getOutputLayerForLayer(
- compositionengine::Layer* layer) const {
- auto index = findCurrentOutputLayerForLayer(layer);
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(const sp<LayerFE>& layerFE) const {
+ auto index = findCurrentOutputLayerForLayer(layerFE);
return index ? getOutputLayerOrderedByZByIndex(*index) : nullptr;
}
std::optional<size_t> Output::findCurrentOutputLayerForLayer(
- compositionengine::Layer* layer) const {
+ const sp<compositionengine::LayerFE>& layer) const {
for (size_t i = 0; i < getOutputLayerCount(); i++) {
auto outputLayer = getOutputLayerOrderedByZByIndex(i);
- if (outputLayer && &outputLayer->getLayer() == layer) {
+ if (outputLayer && &outputLayer->getLayerFE() == layer.get()) {
return i;
}
}
@@ -352,7 +348,7 @@
// Evaluate the layers from front to back to determine what is visible. This
// also incrementally calculates the coverage information for each layer as
// well as the entire output.
- for (auto& layer : reversed(refreshArgs.layers)) {
+ for (auto layer : reversed(refreshArgs.layers)) {
// Incrementally process the coverage for each layer
ensureOutputLayerIfVisible(layer, coverage);
@@ -371,28 +367,29 @@
}
}
-void Output::ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer> layer,
+void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
compositionengine::Output::CoverageState& coverage) {
- // Note: Converts a wp<LayerFE> to a sp<LayerFE>
- auto layerFE = layer->getLayerFE();
- if (layerFE == nullptr) {
- return;
- }
-
// Ensure we have a snapshot of the basic geometry layer state. Limit the
// snapshots to once per frame for each candidate layer, as layers may
// appear on multiple outputs.
if (!coverage.latchedLayers.count(layerFE)) {
coverage.latchedLayers.insert(layerFE);
- layerFE->latchCompositionState(layer->editFEState(),
- compositionengine::LayerFE::StateSubset::BasicGeometry);
+ layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
}
- // Obtain a read-only reference to the front-end layer state
- const auto& layerFEState = layer->getFEState();
-
// Only consider the layers on the given layer stack
- if (!belongsInOutput(layer.get())) {
+ if (!belongsInOutput(layerFE)) {
+ return;
+ }
+
+ // Obtain a read-only pointer to the front-end layer state
+ const auto* layerFEState = layerFE->getCompositionState();
+ if (CC_UNLIKELY(!layerFEState)) {
+ return;
+ }
+
+ // handle hidden surfaces by setting the visible region to empty
+ if (CC_UNLIKELY(!layerFEState->isVisible)) {
return;
}
@@ -430,23 +427,18 @@
*/
Region shadowRegion;
- // handle hidden surfaces by setting the visible region to empty
- if (CC_UNLIKELY(!layerFEState.isVisible)) {
- return;
- }
-
- const ui::Transform& tr = layerFEState.geomLayerTransform;
+ const ui::Transform& tr = layerFEState->geomLayerTransform;
// Get the visible region
// TODO(b/121291683): Is it worth creating helper methods on LayerFEState
// for computations like this?
- const Rect visibleRect(tr.transform(layerFEState.geomLayerBounds));
+ const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
visibleRegion.set(visibleRect);
- if (layerFEState.shadowRadius > 0.0f) {
+ if (layerFEState->shadowRadius > 0.0f) {
// if the layer casts a shadow, offset the layers visible region and
// calculate the shadow region.
- const auto inset = static_cast<int32_t>(ceilf(layerFEState.shadowRadius) * -1.0f);
+ const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
Rect visibleRectWithShadows(visibleRect);
visibleRectWithShadows.inset(inset, inset, inset, inset);
visibleRegion.set(visibleRectWithShadows);
@@ -458,10 +450,10 @@
}
// Remove the transparent area from the visible region
- if (!layerFEState.isOpaque) {
+ if (!layerFEState->isOpaque) {
if (tr.preserveRects()) {
// transform the transparent region
- transparentRegion = tr.transform(layerFEState.transparentRegionHint);
+ transparentRegion = tr.transform(layerFEState->transparentRegionHint);
} else {
// transformation too complex, can't do the
// transparent region optimization.
@@ -471,7 +463,7 @@
// compute the opaque region
const auto layerOrientation = tr.getOrientation();
- if (layerFEState.isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
+ if (layerFEState->isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
// If we one of the simple category of transforms (0/90/180/270 rotation
// + any flip), then the opaque region is the layer's footprint.
// Otherwise we don't try and compute the opaque region since there may
@@ -495,7 +487,7 @@
// Get coverage information for the layer as previously displayed,
// also taking over ownership from mOutputLayersorderedByZ.
- auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layer.get());
+ auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layerFE);
auto prevOutputLayer =
prevOutputLayerIndex ? getOutputLayerOrderedByZByIndex(*prevOutputLayerIndex) : nullptr;
@@ -509,7 +501,7 @@
// compute this layer's dirty region
Region dirty;
- if (layerFEState.contentDirty) {
+ if (layerFEState->contentDirty) {
// we need to invalidate the whole region
dirty = visibleRegion;
// as well, as the old visible region
@@ -556,7 +548,7 @@
// The layer is visible. Either reuse the existing outputLayer if we have
// one, or create a new one if we do not.
- auto result = ensureOutputLayer(prevOutputLayerIndex, layer, layerFE);
+ auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
// Store the layer coverage information into the layer state as some of it
// is useful later.
@@ -575,10 +567,9 @@
void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
for (auto* layer : getOutputLayersOrderedByZ()) {
- layer->getLayerFE().latchCompositionState(layer->getLayer().editFEState(),
- args.updatingGeometryThisFrame
- ? LayerFE::StateSubset::GeometryAndContent
- : LayerFE::StateSubset::Content);
+ layer->getLayerFE().prepareCompositionState(
+ args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
+ : LayerFE::StateSubset::Content);
}
}
@@ -611,7 +602,7 @@
compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
- if (layer->getLayer().getFEState().backgroundBlurRadius > 0) {
+ if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
layerRequestingBgComposition = layer;
}
}
@@ -637,7 +628,7 @@
*outHdrDataSpace = ui::Dataspace::UNKNOWN;
for (const auto* layer : getOutputLayersOrderedByZ()) {
- switch (layer->getLayer().getFEState().dataspace) {
+ switch (layer->getLayerFE().getCompositionState()->dataspace) {
case ui::Dataspace::V0_SCRGB:
case ui::Dataspace::V0_SCRGB_LINEAR:
case ui::Dataspace::BT2020:
@@ -653,7 +644,8 @@
case ui::Dataspace::BT2020_ITU_PQ:
bestDataSpace = ui::Dataspace::DISPLAY_P3;
*outHdrDataSpace = ui::Dataspace::BT2020_PQ;
- *outIsHdrClientComposition = layer->getLayer().getFEState().forceClientComposition;
+ *outIsHdrClientComposition =
+ layer->getLayerFE().getCompositionState()->forceClientComposition;
break;
case ui::Dataspace::BT2020_HLG:
case ui::Dataspace::BT2020_ITU_HLG:
@@ -834,8 +826,8 @@
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.scissor;
- clientCompositionDisplay.clip = outputState.scissor;
+ clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
+ clientCompositionDisplay.clip = outputState.sourceClip;
clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4();
clientCompositionDisplay.orientation = outputState.orientation;
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
@@ -865,7 +857,7 @@
if (outputState.isSecure && supportsProtectedContent) {
auto layers = getOutputLayersOrderedByZ();
bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
- return layer->getLayer().getFEState().hasProtectedContent;
+ return layer->getLayerFE().getCompositionState()->hasProtectedContent;
});
if (needsProtected != renderEngine.isProtected()) {
renderEngine.useProtectedContext(needsProtected);
@@ -903,8 +895,7 @@
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
const bool expensiveRenderingExpected =
- clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 ||
- mLayerRequestingBackgroundBlur != nullptr;
+ clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(true);
}
@@ -954,7 +945,7 @@
for (auto* layer : getOutputLayersOrderedByZ()) {
const auto& layerState = layer->getState();
- const auto& layerFEState = layer->getLayer().getFEState();
+ const auto* layerFEState = layer->getLayerFE().getCompositionState();
auto& layerFE = layer->getLayerFE();
const Region clip(viewportRegion.intersect(layerState.visibleRegion));
@@ -973,7 +964,7 @@
// underneath. We also skip the first layer as the buffer target is
// guaranteed to start out cleared.
bool clearClientComposition =
- layerState.clearClientTarget && layerFEState.isOpaque && !firstLayer;
+ layerState.clearClientTarget && layerFEState->isOpaque && !firstLayer;
ALOGV(" Composition type: client %d clear %d", clientComposition, clearClientComposition);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 84d79f7..ca5be48 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -40,7 +40,8 @@
dumpVal(out, "frame", frame);
dumpVal(out, "viewport", viewport);
- dumpVal(out, "scissor", scissor);
+ dumpVal(out, "sourceClip", sourceClip);
+ dumpVal(out, "destinationClip", destinationClip);
dumpVal(out, "needsFiltering", needsFiltering);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index d92b7ef..b538d75 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,7 +16,6 @@
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
@@ -51,11 +50,9 @@
} // namespace
-std::unique_ptr<OutputLayer> createOutputLayer(
- const compositionengine::Output& output,
- const std::shared_ptr<compositionengine::Layer>& layer,
- const sp<compositionengine::LayerFE>& layerFE) {
- return createOutputLayerTemplated<OutputLayer>(output, layer, layerFE);
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output& output,
+ const sp<compositionengine::LayerFE>& layerFE) {
+ return createOutputLayerTemplated<OutputLayer>(output, layerFE);
}
OutputLayer::~OutputLayer() = default;
@@ -70,7 +67,7 @@
}
Rect OutputLayer::calculateInitialCrop() const {
- const auto& layerState = getLayer().getFEState();
+ const auto& layerState = *getLayerFE().getCompositionState();
// apply the projection's clipping to the window crop in
// layerstack space, and convert-back to layer space.
@@ -103,7 +100,7 @@
}
FloatRect OutputLayer::calculateOutputSourceCrop() const {
- const auto& layerState = getLayer().getFEState();
+ const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
if (!layerState.geomUsesSourceCrop) {
@@ -180,7 +177,7 @@
}
Rect OutputLayer::calculateOutputDisplayFrame() const {
- const auto& layerState = getLayer().getFEState();
+ const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
// apply the layer's transform, followed by the display's global transform
@@ -227,7 +224,7 @@
}
uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
- const auto& layerState = getLayer().getFEState();
+ const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
/*
@@ -267,7 +264,11 @@
} // namespace impl
void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) {
- const auto& layerFEState = getLayer().getFEState();
+ const auto* layerFEState = getLayerFE().getCompositionState();
+ if (!layerFEState) {
+ return;
+ }
+
const auto& outputState = getOutput().getState();
const auto& profile = *getOutput().getDisplayColorProfile();
auto& state = editState();
@@ -285,7 +286,7 @@
state.bufferTransform =
static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
- if ((layerFEState.isSecure && !outputState.isSecure) ||
+ if ((layerFEState->isSecure && !outputState.isSecure) ||
(state.bufferTransform & ui::Transform::ROT_INVALID)) {
state.forceClientComposition = true;
}
@@ -294,14 +295,14 @@
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
- state.dataspace = layerFEState.isColorspaceAgnostic &&
+ state.dataspace = layerFEState->isColorspaceAgnostic &&
outputState.targetDataspace != ui::Dataspace::UNKNOWN
? outputState.targetDataspace
- : layerFEState.dataspace;
+ : layerFEState->dataspace;
// These are evaluated every frame as they can potentially change at any
// time.
- if (layerFEState.forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
+ if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
forceClientComposition) {
state.forceClientComposition = true;
}
@@ -321,21 +322,25 @@
return;
}
- const auto& outputIndependentState = getLayer().getFEState();
- auto requestedCompositionType = outputIndependentState.compositionType;
+ const auto* outputIndependentState = getLayerFE().getCompositionState();
+ if (!outputIndependentState) {
+ return;
+ }
+
+ auto requestedCompositionType = outputIndependentState->compositionType;
if (includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), outputIndependentState);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
- writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), outputIndependentState);
+ writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
// Always set the layer color after setting the composition type.
- writeSolidColorStateToHWC(hwcLayer.get(), outputIndependentState);
+ writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(
@@ -546,10 +551,14 @@
return;
}
- const auto& layerFEState = getLayer().getFEState();
+ const auto* layerFEState = getLayerFE().getCompositionState();
+ if (!layerFEState) {
+ return;
+ }
+
const auto& outputState = getOutput().getState();
- Rect frame = layerFEState.cursorFrame;
+ Rect frame = layerFEState->cursorFrame;
frame.intersect(outputState.viewport, &frame);
Rect position = outputState.transform.transform(frame);
@@ -646,8 +655,7 @@
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
- StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, &getLayer(),
- getLayerFE().getDebugName());
+ StringAppendF(&out, " - Output Layer %p(%s)\n", this, getLayerFE().getDebugName());
dumpState(out);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index c1faa90..d889d74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -17,7 +17,6 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/CompositionEngine.h>
-#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <compositionengine/mock/OutputLayer.h>
@@ -50,11 +49,6 @@
std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
-
- std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
};
TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
@@ -134,48 +128,32 @@
struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
public:
+ struct Layer {
+ Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); }
+
+ StrictMock<mock::OutputLayer> outputLayer;
+ StrictMock<mock::LayerFE> layerFE;
+ LayerFECompositionState layerFEState;
+ };
+
CompositionEngineUpdateCursorAsyncTest() {
EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
- .WillRepeatedly(Return(&mOutput2OutputLayer1));
+ .WillRepeatedly(Return(&mOutput2Layer1.outputLayer));
EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
- .WillRepeatedly(Return(&mOutput3OutputLayer1));
+ .WillRepeatedly(Return(&mOutput3Layer1.outputLayer));
EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
- .WillRepeatedly(Return(&mOutput3OutputLayer2));
-
- EXPECT_CALL(mOutput2OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput2Layer1FE));
- EXPECT_CALL(mOutput3OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer1FE));
- EXPECT_CALL(mOutput3OutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer2FE));
-
- EXPECT_CALL(mOutput2OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput2Layer1));
- EXPECT_CALL(mOutput3OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer1));
- EXPECT_CALL(mOutput3OutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer2));
-
- EXPECT_CALL(mOutput2Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput2Layer1FEState));
- EXPECT_CALL(mOutput3Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer1FEState));
- EXPECT_CALL(mOutput3Layer2, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer2FEState));
+ .WillRepeatedly(Return(&mOutput3Layer2.outputLayer));
}
- StrictMock<mock::OutputLayer> mOutput2OutputLayer1;
- StrictMock<mock::OutputLayer> mOutput3OutputLayer1;
- StrictMock<mock::OutputLayer> mOutput3OutputLayer2;
-
- StrictMock<mock::LayerFE> mOutput2Layer1FE;
- StrictMock<mock::LayerFE> mOutput3Layer1FE;
- StrictMock<mock::LayerFE> mOutput3Layer2FE;
-
- StrictMock<mock::Layer> mOutput2Layer1;
- StrictMock<mock::Layer> mOutput3Layer1;
- StrictMock<mock::Layer> mOutput3Layer2;
-
- LayerFECompositionState mOutput2Layer1FEState;
- LayerFECompositionState mOutput3Layer1FEState;
- LayerFECompositionState mOutput3Layer2FEState;
+ Layer mOutput2Layer1;
+ Layer mOutput3Layer1;
+ Layer mOutput3Layer2;
};
TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoOutputs) {
@@ -183,9 +161,9 @@
}
TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoLayersBeingCursorLayers) {
- EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
@@ -195,23 +173,23 @@
TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorLayers) {
{
InSequence seq;
- EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput2Layer1FE, latchCursorCompositionState(Ref(mOutput2Layer1FEState)));
- EXPECT_CALL(mOutput2OutputLayer1, writeCursorPositionToHWC());
+ EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
}
{
InSequence seq;
- EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput3Layer1FE, latchCursorCompositionState(Ref(mOutput3Layer1FEState)));
- EXPECT_CALL(mOutput3OutputLayer1, writeCursorPositionToHWC());
+ EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
}
{
InSequence seq;
- EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput3Layer2FE, latchCursorCompositionState(Ref(mOutput3Layer2FEState)));
- EXPECT_CALL(mOutput3OutputLayer2, writeCursorPositionToHWC());
+ EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+ EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
}
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
@@ -224,14 +202,6 @@
*/
struct CompositionTestPreComposition : public CompositionEngineTest {
- CompositionTestPreComposition() {
- EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(Return(mLayer1FE));
- EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(Return(mLayer2FE));
- EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(Return(mLayer3FE));
- // getLayerFE() can return nullptr. Ensure that this is handled.
- EXPECT_CALL(*mLayer4, getLayerFE()).WillRepeatedly(Return(nullptr));
- }
-
sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
@@ -256,7 +226,7 @@
EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
mRefreshArgs.outputs = {mOutput1};
- mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+ mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
mEngine.preComposition(mRefreshArgs);
@@ -274,7 +244,7 @@
mEngine.setNeedsAnotherUpdateForTest(true);
mRefreshArgs.outputs = {mOutput1};
- mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+ mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
mEngine.preComposition(mRefreshArgs);
@@ -289,7 +259,7 @@
EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
mRefreshArgs.outputs = {mOutput1};
- mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+ mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
mEngine.preComposition(mRefreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index ae93969..16f7a4e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -25,7 +25,6 @@
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
#include <compositionengine/mock/DisplaySurface.h>
-#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/NativeWindow.h>
#include <compositionengine/mock/OutputLayer.h>
@@ -53,6 +52,27 @@
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+struct Layer {
+ Layer() {
+ EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+ EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+ }
+
+ sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+ StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+ StrictMock<HWC2::mock::Layer> hwc2Layer;
+};
+
+struct LayerNoHWC2Layer {
+ LayerNoHWC2Layer() {
+ EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+ EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(nullptr));
+ }
+
+ sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+ StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+};
+
struct DisplayTest : public testing::Test {
class Display : public impl::Display {
public:
@@ -71,28 +91,24 @@
DisplayTest() {
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
- EXPECT_CALL(*mLayer1, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer1));
- EXPECT_CALL(*mLayer2, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer2));
- EXPECT_CALL(*mLayer3, getHwcLayer()).WillRepeatedly(Return(nullptr));
mDisplay->injectOutputLayerForTest(
- std::unique_ptr<compositionengine::OutputLayer>(mLayer1));
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer1.outputLayer));
mDisplay->injectOutputLayerForTest(
- std::unique_ptr<compositionengine::OutputLayer>(mLayer2));
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
mDisplay->injectOutputLayerForTest(
- std::unique_ptr<compositionengine::OutputLayer>(mLayer3));
+ std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
}
StrictMock<android::mock::HWComposer> mHwComposer;
StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
- StrictMock<HWC2::mock::Layer> mHWC2Layer1;
- StrictMock<HWC2::mock::Layer> mHWC2Layer2;
- StrictMock<HWC2::mock::Layer> mHWC2LayerUnknown;
- mock::OutputLayer* mLayer1 = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* mLayer2 = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* mLayer3 = new StrictMock<mock::OutputLayer>();
+ Layer mLayer1;
+ Layer mLayer2;
+ LayerNoHWC2Layer mLayer3;
+ StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
+
std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
DisplayCreationArgsBuilder()
.setDisplayId(DEFAULT_DISPLAY_ID)
@@ -283,12 +299,11 @@
TEST_F(DisplayTest, createOutputLayerSetsHwcLayer) {
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
- auto layer = std::make_shared<StrictMock<mock::Layer>>();
StrictMock<HWC2::mock::Layer> hwcLayer;
EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
- auto outputLayer = mDisplay->createOutputLayer(layer, layerFE);
+ auto outputLayer = mDisplay->createOutputLayer(layerFE);
EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
@@ -305,7 +320,6 @@
impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
- mock::Layer layerXLayer;
{
Output::ReleasedLayers releasedLayers;
@@ -314,7 +328,7 @@
}
CompositionRefreshArgs refreshArgs;
- refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
+ refreshArgs.layersWithQueuedFrames.push_back(layerXLayerFE);
nonHwcDisplay->setReleasedLayers(refreshArgs);
@@ -339,34 +353,19 @@
}
TEST_F(DisplayTest, setReleasedLayers) {
- sp<mock::LayerFE> layer1LayerFE = new StrictMock<mock::LayerFE>();
- sp<mock::LayerFE> layer2LayerFE = new StrictMock<mock::LayerFE>();
- sp<mock::LayerFE> layer3LayerFE = new StrictMock<mock::LayerFE>();
- sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
- mock::Layer layer1Layer;
- mock::Layer layer2Layer;
- mock::Layer layer3Layer;
- mock::Layer layerXLayer;
-
- EXPECT_CALL(*mLayer1, getLayer()).WillRepeatedly(ReturnRef(layer1Layer));
- EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(ReturnRef(*layer1LayerFE.get()));
- EXPECT_CALL(*mLayer2, getLayer()).WillRepeatedly(ReturnRef(layer2Layer));
- EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(ReturnRef(*layer2LayerFE.get()));
- EXPECT_CALL(*mLayer3, getLayer()).WillRepeatedly(ReturnRef(layer3Layer));
- EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(ReturnRef(*layer3LayerFE.get()));
+ sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
CompositionRefreshArgs refreshArgs;
- refreshArgs.layersWithQueuedFrames.push_back(&layer1Layer);
- refreshArgs.layersWithQueuedFrames.push_back(&layer2Layer);
- refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
- refreshArgs.layersWithQueuedFrames.push_back(nullptr);
+ refreshArgs.layersWithQueuedFrames.push_back(mLayer1.layerFE);
+ refreshArgs.layersWithQueuedFrames.push_back(mLayer2.layerFE);
+ refreshArgs.layersWithQueuedFrames.push_back(unknownLayer);
mDisplay->setReleasedLayers(refreshArgs);
const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
ASSERT_EQ(2, releasedLayers.size());
- ASSERT_EQ(layer1LayerFE.get(), releasedLayers[0].promote().get());
- ASSERT_EQ(layer2LayerFE.get(), releasedLayers[1].promote().get());
+ ASSERT_EQ(mLayer1.layerFE.get(), releasedLayers[0].promote().get());
+ ASSERT_EQ(mLayer2.layerFE.get(), releasedLayers[1].promote().get());
}
/*
@@ -400,16 +399,12 @@
MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
compositionengine::OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(
- std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD2(ensureOutputLayer,
+ compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
MOCK_METHOD0(finalizePendingOutputLayers, void());
MOCK_METHOD0(clearOutputLayers, void());
MOCK_CONST_METHOD1(dumpState, void(std::string&));
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
const compositionengine::CompositionEngine& mCompositionEngine;
@@ -523,16 +518,16 @@
*/
TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsFalse) {
- EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
}
TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) {
- EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
EXPECT_TRUE(mDisplay->anyLayersRequireClientComposition());
}
@@ -542,16 +537,16 @@
*/
TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsTrue) {
- EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(true));
- EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true));
- EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(true));
EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
}
TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) {
- EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(true));
- EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_FALSE(mDisplay->allLayersRequireClientComposition());
}
@@ -565,17 +560,17 @@
}
TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) {
- EXPECT_CALL(*mLayer1,
+ EXPECT_CALL(*mLayer1.outputLayer,
applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
.Times(1);
- EXPECT_CALL(*mLayer2,
+ EXPECT_CALL(*mLayer2.outputLayer,
applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
.Times(1);
mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
- {&mHWC2Layer1, HWC2::Composition::Client},
- {&mHWC2Layer2, HWC2::Composition::Device},
- {&mHWC2LayerUnknown, HWC2::Composition::SolidColor},
+ {&mLayer1.hwc2Layer, HWC2::Composition::Client},
+ {&mLayer2.hwc2Layer, HWC2::Composition::Device},
+ {&hwc2LayerUnknown, HWC2::Composition::SolidColor},
});
}
@@ -616,25 +611,25 @@
*/
TEST_F(DisplayTest, applyLayerRequestsToLayersPreparesAllLayers) {
- EXPECT_CALL(*mLayer1, prepareForDeviceLayerRequests()).Times(1);
- EXPECT_CALL(*mLayer2, prepareForDeviceLayerRequests()).Times(1);
- EXPECT_CALL(*mLayer3, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
}
TEST_F(DisplayTest, applyLayerRequestsToLayers2) {
- EXPECT_CALL(*mLayer1, prepareForDeviceLayerRequests()).Times(1);
- EXPECT_CALL(*mLayer2, prepareForDeviceLayerRequests()).Times(1);
- EXPECT_CALL(*mLayer3, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+ EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
- EXPECT_CALL(*mLayer1,
+ EXPECT_CALL(*mLayer1.outputLayer,
applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET))
.Times(1);
mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests{
- {&mHWC2Layer1, HWC2::LayerRequest::ClearClientTarget},
- {&mHWC2LayerUnknown, HWC2::LayerRequest::ClearClientTarget},
+ {&mLayer1.hwc2Layer, HWC2::LayerRequest::ClearClientTarget},
+ {&hwc2LayerUnknown, HWC2::LayerRequest::ClearClientTarget},
});
}
@@ -660,9 +655,9 @@
EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mHWC2Layer1))
+ EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
.WillOnce(Return(layer1Fence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mHWC2Layer2))
+ EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
.WillOnce(Return(layer2Fence));
EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
@@ -671,10 +666,10 @@
EXPECT_EQ(presentFence, result.presentFence);
EXPECT_EQ(2u, result.layerFences.size());
- ASSERT_EQ(1, result.layerFences.count(&mHWC2Layer1));
- EXPECT_EQ(layer1Fence, result.layerFences[&mHWC2Layer1]);
- ASSERT_EQ(1, result.layerFences.count(&mHWC2Layer2));
- EXPECT_EQ(layer2Fence, result.layerFences[&mHWC2Layer2]);
+ ASSERT_EQ(1, result.layerFences.count(&mLayer1.hwc2Layer));
+ EXPECT_EQ(layer1Fence, result.layerFences[&mLayer1.hwc2Layer]);
+ ASSERT_EQ(1, result.layerFences.count(&mLayer2.hwc2Layer));
+ EXPECT_EQ(layer2Fence, result.layerFences[&mLayer2.hwc2Layer]);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
deleted file mode 100644
index 787f973..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
+++ /dev/null
@@ -1,61 +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.
- */
-
-#include <gtest/gtest.h>
-
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/impl/Layer.h>
-#include <compositionengine/mock/CompositionEngine.h>
-#include <compositionengine/mock/LayerFE.h>
-
-namespace android::compositionengine {
-namespace {
-
-using testing::StrictMock;
-
-struct LayerTest : public testing::Test {
- struct Layer final : public impl::Layer {
- explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {}
- ~Layer() override = default;
-
- // compositionengine::Layer overrides
- sp<LayerFE> getLayerFE() const { return mLayerFE.promote(); }
- const LayerFECompositionState& getFEState() const override { return mFrontEndState; }
- LayerFECompositionState& editFEState() override { return mFrontEndState; }
-
- // compositionengine::impl::Layer overrides
- void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); }
-
- const wp<LayerFE> mLayerFE;
- LayerFECompositionState mFrontEndState;
- };
-
- ~LayerTest() override = default;
-
- StrictMock<mock::CompositionEngine> mCompositionEngine;
- sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
- Layer mLayer{LayerCreationArgs{mLayerFE}};
-};
-
-/* ------------------------------------------------------------------------
- * Basic construction
- */
-
-TEST_F(LayerTest, canInstantiateLayer) {}
-
-} // namespace
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0e579fa..963062f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -18,7 +18,6 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
@@ -56,15 +55,12 @@
struct OutputLayerTest : public testing::Test {
struct OutputLayer final : public impl::OutputLayer {
- OutputLayer(const compositionengine::Output& output,
- std::shared_ptr<compositionengine::Layer> layer,
- sp<compositionengine::LayerFE> layerFE)
- : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+ OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
+ : mOutput(output), mLayerFE(layerFE) {}
~OutputLayer() override = default;
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
- compositionengine::Layer& getLayer() const override { return *mLayer; }
compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
const impl::OutputLayerCompositionState& getState() const override { return mState; }
impl::OutputLayerCompositionState& editState() override { return mState; }
@@ -73,7 +69,6 @@
void dumpState(std::string& out) const override { mState.dump(out); }
const compositionengine::Output& mOutput;
- std::shared_ptr<compositionengine::Layer> mLayer;
sp<compositionengine::LayerFE> mLayerFE;
impl::OutputLayerCompositionState mState;
};
@@ -82,16 +77,14 @@
EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
- EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
}
compositionengine::mock::Output mOutput;
- std::shared_ptr<compositionengine::mock::Layer> mLayer{
- new StrictMock<compositionengine::mock::Layer>()};
sp<compositionengine::mock::LayerFE> mLayerFE{
new StrictMock<compositionengine::mock::LayerFE>()};
- OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+ OutputLayer mOutputLayer{mOutput, mLayerFE};
LayerFECompositionState mLayerFEState;
impl::OutputCompositionState mOutputState;
@@ -436,9 +429,8 @@
struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
- std::shared_ptr<compositionengine::Layer> layer,
sp<compositionengine::LayerFE> layerFE)
- : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+ : mOutput(output), mLayerFE(layerFE) {}
// Mock everything called by updateCompositionState to simplify testing it.
MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
@@ -446,7 +438,6 @@
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
- compositionengine::Layer& getLayer() const override { return *mLayer; }
compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
const impl::OutputLayerCompositionState& getState() const override { return mState; }
impl::OutputLayerCompositionState& editState() override { return mState; }
@@ -455,7 +446,6 @@
MOCK_CONST_METHOD1(dumpState, void(std::string&));
const compositionengine::Output& mOutput;
- std::shared_ptr<compositionengine::Layer> mLayer;
sp<compositionengine::LayerFE> mLayerFE;
impl::OutputLayerCompositionState mState;
};
@@ -463,7 +453,6 @@
struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
public:
OutputLayerUpdateCompositionStateTest() {
- EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
EXPECT_CALL(mOutput, getDisplayColorProfile())
.WillRepeatedly(Return(&mDisplayColorProfile));
@@ -491,10 +480,16 @@
uint32_t mBufferTransform{21};
using OutputLayer = OutputLayerPartialMockForUpdateCompositionState;
- StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE};
+ StrictMock<OutputLayer> mOutputLayer{mOutput, mLayerFE};
StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
};
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+ mOutputLayer.updateCompositionState(true, false);
+}
+
TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
mLayerFEState.isSecure = true;
mOutputState.isSecure = true;
@@ -745,6 +740,12 @@
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
@@ -888,6 +889,12 @@
const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultDisplayViewport{0, 0, 1920, 1080};
const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, doesNothingIfNoFECompositionState) {
+ EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+ mOutputLayer.writeCursorPositionToHWC();
+}
+
TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCHandlesNoHwcState) {
mOutputLayer.editState().hwc.reset();
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6e8d3df..2b45046 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -23,7 +23,6 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
@@ -78,22 +77,48 @@
// not implemented by the base implementation class.
MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, compositionengine::OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&,
- const sp<LayerFE>&));
+ MOCK_METHOD2(ensureOutputLayer,
+ compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
MOCK_METHOD0(finalizePendingOutputLayers, void());
MOCK_METHOD0(clearOutputLayers, void());
MOCK_CONST_METHOD1(dumpState, void(std::string&));
MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(const std::shared_ptr<compositionengine::Layer>&,
- const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
impl::OutputCompositionState mState;
};
+struct InjectedLayer {
+ InjectedLayer() {
+ EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+ EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+ EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ }
+
+ mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
+ sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+ LayerFECompositionState layerFEState;
+ impl::OutputLayerCompositionState outputLayerState;
+};
+
+struct NonInjectedLayer {
+ NonInjectedLayer() {
+ EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+ EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+ EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ }
+
+ mock::OutputLayer outputLayer;
+ sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+ LayerFECompositionState layerFEState;
+ impl::OutputLayerCompositionState outputLayerState;
+};
+
struct OutputTest : public testing::Test {
class Output : public impl::Output {
public:
@@ -114,6 +139,14 @@
mOutput->editState().bounds = kDefaultDisplaySize;
}
+ void injectOutputLayer(InjectedLayer& layer) {
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.outputLayer));
+ }
+
+ void injectNullOutputLayer() {
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(nullptr));
+ }
+
static const Rect kDefaultDisplaySize;
StrictMock<mock::CompositionEngine> mCompositionEngine;
@@ -122,48 +155,6 @@
std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
};
-// Extension of the base test useful for checking interactions with the LayerFE
-// functions to latch composition state.
-struct OutputLatchFEStateTest : public OutputTest {
- OutputLatchFEStateTest() {
- EXPECT_CALL(*mOutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mLayer1));
- EXPECT_CALL(*mOutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mLayer2));
- EXPECT_CALL(*mOutputLayer3, getLayer()).WillRepeatedly(ReturnRef(mLayer3));
-
- EXPECT_CALL(*mOutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mLayer1FE));
- EXPECT_CALL(*mOutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mLayer2FE));
- EXPECT_CALL(*mOutputLayer3, getLayerFE()).WillRepeatedly(ReturnRef(mLayer3FE));
-
- EXPECT_CALL(mLayer1, editFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
- EXPECT_CALL(mLayer2, editFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
- EXPECT_CALL(mLayer3, editFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
-
- EXPECT_CALL(mLayer1, getFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
- EXPECT_CALL(mLayer2, getFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
- EXPECT_CALL(mLayer3, getFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
- }
-
- void injectLayer(std::unique_ptr<mock::OutputLayer> layer) {
- mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.release()));
- }
-
- std::unique_ptr<mock::OutputLayer> mOutputLayer1{new StrictMock<mock::OutputLayer>};
- std::unique_ptr<mock::OutputLayer> mOutputLayer2{new StrictMock<mock::OutputLayer>};
- std::unique_ptr<mock::OutputLayer> mOutputLayer3{new StrictMock<mock::OutputLayer>};
-
- StrictMock<mock::Layer> mLayer1;
- StrictMock<mock::Layer> mLayer2;
- StrictMock<mock::Layer> mLayer3;
-
- StrictMock<mock::LayerFE> mLayer1FE;
- StrictMock<mock::LayerFE> mLayer2FE;
- StrictMock<mock::LayerFE> mLayer3FE;
-
- LayerFECompositionState mLayer1FEState;
- LayerFECompositionState mLayer2FEState;
- LayerFECompositionState mLayer3FEState;
-};
-
const Rect OutputTest::kDefaultDisplaySize{100, 200};
using ColorProfile = compositionengine::Output::ColorProfile;
@@ -249,16 +240,19 @@
const int32_t orientation = 123;
const Rect frame{1, 2, 3, 4};
const Rect viewport{5, 6, 7, 8};
- const Rect scissor{9, 10, 11, 12};
+ const Rect sourceClip{9, 10, 11, 12};
+ const Rect destinationClip{13, 14, 15, 16};
const bool needsFiltering = true;
- mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+ mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+ needsFiltering);
EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
EXPECT_EQ(frame, mOutput->getState().frame);
EXPECT_EQ(viewport, mOutput->getState().viewport);
- EXPECT_EQ(scissor, mOutput->getState().scissor);
+ EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
+ EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
}
@@ -500,11 +494,18 @@
EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
}
-TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
- StrictMock<mock::Layer> layer;
- LayerFECompositionState layerFEState;
+TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+ NonInjectedLayer layer;
+ sp<LayerFE> layerFE(layer.layerFE);
- EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
+ // If the layer has no composition state, it does not belong to any output.
+ EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+}
+
+TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+ NonInjectedLayer layer;
+ sp<LayerFE> layerFE(layer.layerFE);
const uint32_t layerStack1 = 123u;
const uint32_t layerStack2 = 456u;
@@ -512,57 +513,51 @@
// If the output accepts layerStack1 and internal-only layers....
mOutput->setLayerStackFilter(layerStack1, true);
- // A null layer pointer does not belong to the output
- EXPECT_FALSE(mOutput->belongsInOutput(nullptr));
-
// A layer with no layerStack does not belong to it, internal-only or not.
- layerFEState.layerStackId = std::nullopt;
- layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = std::nullopt;
+ layer.layerFEState.internalOnly = false;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = std::nullopt;
- layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = std::nullopt;
+ layer.layerFEState.internalOnly = true;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
// Any layer with layerStack1 belongs to it, internal-only or not.
- layerFEState.layerStackId = layerStack1;
- layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack1;
+ layer.layerFEState.internalOnly = false;
+ EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack1;
- layerFEState.internalOnly = true;
- EXPECT_TRUE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack1;
+ layer.layerFEState.internalOnly = true;
+ EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack2;
- layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack2;
+ layer.layerFEState.internalOnly = true;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack2;
- layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack2;
+ layer.layerFEState.internalOnly = false;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
// If the output accepts layerStack1 but not internal-only layers...
mOutput->setLayerStackFilter(layerStack1, false);
- // A null layer pointer does not belong to the output
- EXPECT_FALSE(mOutput->belongsInOutput(nullptr));
-
// Only non-internal layers with layerStack1 belong to it.
- layerFEState.layerStackId = layerStack1;
- layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack1;
+ layer.layerFEState.internalOnly = false;
+ EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack1;
- layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack1;
+ layer.layerFEState.internalOnly = true;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack2;
- layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack2;
+ layer.layerFEState.internalOnly = true;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
- layerFEState.layerStackId = layerStack2;
- layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(&layer));
+ layer.layerFEState.layerStackId = layerStack2;
+ layer.layerFEState.internalOnly = false;
+ EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
}
/*
@@ -570,29 +565,27 @@
*/
TEST_F(OutputTest, getOutputLayerForLayerWorks) {
- mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
- mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ NonInjectedLayer layer3;
- mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer1));
- mOutput->injectOutputLayerForTest(nullptr);
- mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer2));
-
- StrictMock<mock::Layer> layer;
- StrictMock<mock::Layer> otherLayer;
+ injectOutputLayer(layer1);
+ injectNullOutputLayer();
+ injectOutputLayer(layer2);
// If the input layer matches the first OutputLayer, it will be returned.
- EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
- EXPECT_EQ(outputLayer1, mOutput->getOutputLayerForLayer(&layer));
+ EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+ EXPECT_EQ(layer1.outputLayer, mOutput->getOutputLayerForLayer(layer1.layerFE));
// If the input layer matches the second OutputLayer, it will be returned.
- EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
- EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
- EXPECT_EQ(outputLayer2, mOutput->getOutputLayerForLayer(&layer));
+ EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+ EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+ EXPECT_EQ(layer2.outputLayer, mOutput->getOutputLayerForLayer(layer2.layerFE));
// If the input layer does not match an output layer, null will be returned.
- EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
- EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
- EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(&layer));
+ EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+ EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+ EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(layer3.layerFE));
}
/*
@@ -624,7 +617,7 @@
* Output::updateLayerStateFromFE()
*/
-using OutputUpdateLayerStateFromFETest = OutputLatchFEStateTest;
+using OutputUpdateLayerStateFromFETest = OutputTest;
TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
CompositionRefreshArgs refreshArgs;
@@ -632,18 +625,18 @@
mOutput->updateLayerStateFromFE(refreshArgs);
}
-TEST_F(OutputUpdateLayerStateFromFETest, latchesContentStateForAllContainedLayers) {
- EXPECT_CALL(mLayer1FE,
- latchCompositionState(Ref(mLayer1FEState), LayerFE::StateSubset::Content));
- EXPECT_CALL(mLayer2FE,
- latchCompositionState(Ref(mLayer2FEState), LayerFE::StateSubset::Content));
- EXPECT_CALL(mLayer3FE,
- latchCompositionState(Ref(mLayer3FEState), LayerFE::StateSubset::Content));
+TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
- // Note: Must be performed after any expectations on these mocks
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+ EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+ EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
CompositionRefreshArgs refreshArgs;
refreshArgs.updatingGeometryThisFrame = false;
@@ -651,21 +644,18 @@
mOutput->updateLayerStateFromFE(refreshArgs);
}
-TEST_F(OutputUpdateLayerStateFromFETest, latchesGeometryAndContentStateForAllContainedLayers) {
- EXPECT_CALL(mLayer1FE,
- latchCompositionState(Ref(mLayer1FEState),
- LayerFE::StateSubset::GeometryAndContent));
- EXPECT_CALL(mLayer2FE,
- latchCompositionState(Ref(mLayer2FEState),
- LayerFE::StateSubset::GeometryAndContent));
- EXPECT_CALL(mLayer3FE,
- latchCompositionState(Ref(mLayer3FEState),
- LayerFE::StateSubset::GeometryAndContent));
+TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
- // Note: Must be performed after any expectations on these mocks
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
CompositionRefreshArgs refreshArgs;
refreshArgs.updatingGeometryThisFrame = true;
@@ -677,7 +667,7 @@
* Output::updateAndWriteCompositionState()
*/
-using OutputUpdateAndWriteCompositionStateTest = OutputLatchFEStateTest;
+using OutputUpdateAndWriteCompositionStateTest = OutputTest;
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) {
mOutput->editState().isEnabled = true;
@@ -687,27 +677,35 @@
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
mOutput->editState().isEnabled = false;
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
CompositionRefreshArgs args;
mOutput->updateAndWriteCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
- EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, false));
- EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, false));
- EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false));
- EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
mOutput->editState().isEnabled = true;
@@ -718,16 +716,20 @@
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
- EXPECT_CALL(*mOutputLayer1, updateCompositionState(true, false));
- EXPECT_CALL(*mOutputLayer1, writeStateToHWC(true));
- EXPECT_CALL(*mOutputLayer2, updateCompositionState(true, false));
- EXPECT_CALL(*mOutputLayer2, writeStateToHWC(true));
- EXPECT_CALL(*mOutputLayer3, updateCompositionState(true, false));
- EXPECT_CALL(*mOutputLayer3, writeStateToHWC(true));
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
mOutput->editState().isEnabled = true;
@@ -738,16 +740,20 @@
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
- EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true));
- EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true));
- EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, true));
- EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
mOutput->editState().isEnabled = true;
@@ -969,7 +975,7 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD2(ensureOutputLayerIfVisible,
- void(std::shared_ptr<compositionengine::Layer>,
+ void(sp<compositionengine::LayerFE>&,
compositionengine::Output::CoverageState&));
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(finalizePendingOutputLayers, void());
@@ -982,8 +988,8 @@
}
StrictMock<mock::OutputLayer> outputLayer;
- std::shared_ptr<StrictMock<mock::Layer>> layer{new StrictMock<mock::Layer>()};
impl::OutputLayerCompositionState outputLayerState;
+ sp<StrictMock<mock::LayerFE>> layerFE{new StrictMock<mock::LayerFE>()};
};
OutputCollectVisibleLayersTest() {
@@ -995,9 +1001,9 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
.WillRepeatedly(Return(&mLayer3.outputLayer));
- mRefreshArgs.layers.push_back(mLayer1.layer);
- mRefreshArgs.layers.push_back(mLayer2.layer);
- mRefreshArgs.layers.push_back(mLayer3.layer);
+ mRefreshArgs.layers.push_back(mLayer1.layerFE);
+ mRefreshArgs.layers.push_back(mLayer2.layerFE);
+ mRefreshArgs.layers.push_back(mLayer3.layerFE);
}
StrictMock<OutputPartialMock> mOutput;
@@ -1024,9 +1030,9 @@
InSequence seq;
// Layer coverage is evaluated from front to back!
- EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layer), Ref(mCoverageState)));
- EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layer), Ref(mCoverageState)));
- EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layer), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layerFE), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layerFE), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layerFE), Ref(mCoverageState)));
EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
EXPECT_CALL(mOutput, finalizePendingOutputLayers());
@@ -1047,43 +1053,39 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
+ MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(
- std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ MOCK_METHOD2(ensureOutputLayer,
+ compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
};
OutputEnsureOutputLayerIfVisibleTest() {
- EXPECT_CALL(*mLayer, getLayerFE()).WillRepeatedly(Return(mLayerFE));
- EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
- EXPECT_CALL(*mLayer, editFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
-
- EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+ .WillRepeatedly(Return(true));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&mOutputLayer));
-
- EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
- EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
- EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(*mLayer.get()));
+ .WillRepeatedly(Return(&mLayer.outputLayer));
mOutput.mState.bounds = Rect(0, 0, 200, 300);
mOutput.mState.viewport = Rect(0, 0, 200, 300);
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
- mLayerFEState.isVisible = true;
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mLayerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+ mLayer.layerFEState.isVisible = true;
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
- mOutputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
- mOutputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+ mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+ mLayer.outputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
- mGeomSnapshots.insert(mLayerFE);
+ mGeomSnapshots.insert(mLayer.layerFE);
+ }
+
+ void ensureOutputLayerIfVisible() {
+ sp<LayerFE> layerFE(mLayer.layerFE);
+ mOutput.ensureOutputLayerIfVisible(layerFE, mCoverageState);
}
static const Region kEmptyRegion;
@@ -1096,11 +1098,7 @@
LayerFESet mGeomSnapshots;
Output::CoverageState mCoverageState{mGeomSnapshots};
- std::shared_ptr<mock::Layer> mLayer{new StrictMock<mock::Layer>()};
- sp<StrictMock<mock::LayerFE>> mLayerFE{new StrictMock<mock::LayerFE>()};
- LayerFECompositionState mLayerFEState;
- mock::OutputLayer mOutputLayer;
- impl::OutputLayerCompositionState mOutputLayerState;
+ NonInjectedLayer mLayer;
};
const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
@@ -1113,275 +1111,282 @@
const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
Region(Rect(0, 0, 200, 100));
-TEST_F(OutputEnsureOutputLayerIfVisibleTest, doesNothingIfNoLayerFE) {
- EXPECT_CALL(*mLayer, getLayerFE).WillOnce(Return(sp<LayerFE>()));
-
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
-}
-
TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
- EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
- EXPECT_CALL(*mLayerFE.get(),
- latchCompositionState(Ref(mLayerFEState),
- compositionengine::LayerFE::StateSubset::BasicGeometry));
+ EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer.layerFE,
+ prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
mGeomSnapshots.clear();
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
- EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+ EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasNoCompositionState) {
+ EXPECT_CALL(*mLayer.layerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+ ensureOutputLayerIfVisible();
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
- mLayerFEState.isVisible = false;
+ mLayer.layerFEState.isVisible = false;
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
- mLayerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+ mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
mOutput.mState.bounds = Rect(0, 0, 0, 0);
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = false;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
RegionEq(kRightHalfBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = false;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
RegionEq(kRightHalfBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = false;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = false;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = false;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = false;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
- mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
- mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
- mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
- mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
- mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
- mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
mOutput.mState.viewport = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
mOutput.mState.viewport = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+ RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
@@ -1390,16 +1395,16 @@
arbitraryTransform.set(1, 1, -1, 1);
arbitraryTransform.set(0, 100);
- mLayerFEState.isOpaque = true;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
- mLayerFEState.geomLayerTransform = arbitraryTransform;
+ mLayer.layerFEState.isOpaque = true;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayer.layerFEState.geomLayerTransform = arbitraryTransform;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
const Region kRegion = Region(Rect(0, 0, 300, 300));
const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
@@ -1408,25 +1413,25 @@
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kRegion));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kRegion));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
- mLayerFEState.isOpaque = false;
- mLayerFEState.contentDirty = true;
- mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200));
@@ -1439,28 +1444,29 @@
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
RegionEq(kExpectedLayerVisibleNonTransparentRegion));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
+ RegionEq(kExpectedLayerVisibleRegion));
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
ui::Transform translate;
translate.set(50, 50);
- mLayerFEState.geomLayerTransform = translate;
- mLayerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.geomLayerTransform = translate;
+ mLayer.layerFEState.shadowRadius = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// half of the layer including the casting shadow is covered and opaque
mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260));
mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260));
@@ -1478,53 +1484,53 @@
EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
- EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
RegionEq(kExpectedLayerVisibleNonTransparentRegion));
- EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
- EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion,
+ EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
- EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
ui::Transform translate;
translate.set(50, 50);
- mLayerFEState.geomLayerTransform = translate;
- mLayerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.geomLayerTransform = translate;
+ mLayer.layerFEState.shadowRadius = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260));
mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260));
- EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
- .WillOnce(Return(&mOutputLayer));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260));
const Region kExpectedLayerShadowRegion =
Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
- EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
- EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
ui::Transform translate;
translate.set(50, 50);
- mLayerFEState.geomLayerTransform = translate;
- mLayerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.geomLayerTransform = translate;
+ mLayer.layerFEState.shadowRadius = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer and its shadows are covered by an opaque region
mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260));
mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260));
- mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+ ensureOutputLayerIfVisible();
}
/*
@@ -1580,13 +1586,11 @@
struct Layer {
Layer() {
- EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
- EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::Layer> mLayer;
StrictMock<mock::LayerFE> mLayerFE;
LayerFECompositionState mLayerFEState;
};
@@ -2778,7 +2782,8 @@
mOutput.mState.frame = kDefaultOutputFrame;
mOutput.mState.viewport = kDefaultOutputViewport;
- mOutput.mState.scissor = kDefaultOutputScissor;
+ mOutput.mState.sourceClip = kDefaultOutputSourceClip;
+ mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
mOutput.mState.orientation = kDefaultOutputOrientation;
mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -2822,7 +2827,8 @@
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputScissor;
+ static const Rect kDefaultOutputSourceClip;
+ static const Rect kDefaultOutputDestinationClip;
static const mat4 kDefaultColorTransformMat;
static const Region kDebugRegion;
@@ -2842,7 +2848,8 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
@@ -3085,9 +3092,10 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3096,9 +3104,10 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3107,8 +3116,8 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
.execute()
@@ -3119,8 +3128,8 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
.execute()
@@ -3132,9 +3141,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(true)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3142,12 +3152,12 @@
struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
struct Layer {
Layer() {
- EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
- EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::Layer> mLayer;
+ StrictMock<mock::LayerFE> mLayerFE;
LayerFECompositionState mLayerFEState;
};
@@ -3314,13 +3324,11 @@
Layer() {
EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
- EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
- EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
}
StrictMock<mock::OutputLayer> mOutputLayer;
- StrictMock<mock::Layer> mLayer;
StrictMock<mock::LayerFE> mLayerFE;
LayerFECompositionState mLayerFEState;
impl::OutputLayerCompositionState mOutputLayerState;
@@ -3345,7 +3353,8 @@
GenerateClientCompositionRequestsTest_ThreeLayers() {
mOutput.mState.frame = kDisplayFrame;
mOutput.mState.viewport = kDisplayViewport;
- mOutput.mState.scissor = kDisplayScissor;
+ mOutput.mState.sourceClip = kDisplaySourceClip;
+ mOutput.mState.destinationClip = kDisplayDestinationClip;
mOutput.mState.transform = ui::Transform{kDisplayOrientation};
mOutput.mState.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
@@ -3376,14 +3385,17 @@
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
- static const Rect kDisplayScissor;
+ static const Rect kDisplaySourceClip;
+ static const Rect kDisplayDestinationClip;
std::array<Layer, 3> mLayers;
};
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
+ 203);
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -3760,19 +3772,23 @@
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
// Layer requesting blur, or below, should request client composition.
- EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true));
- EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true));
- EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
- EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false));
- EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
- mLayer2FEState.backgroundBlurRadius = 10;
+ layer2.layerFEState.backgroundBlurRadius = 10;
- injectLayer(std::move(mOutputLayer1));
- injectLayer(std::move(mOutputLayer2));
- injectLayer(std::move(mOutputLayer3));
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
mOutput->editState().isEnabled = true;
@@ -3789,13 +3805,15 @@
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
+ const Rect kPortraitSourceClip(0, 0, 1000, 2000);
+ const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
const uint32_t kPortraitOrientation = TR_ROT_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
mOutput.mState.frame = kPortraitFrame;
mOutput.mState.viewport = kPortraitViewport;
- mOutput.mState.scissor = kPortraitScissor;
+ mOutput.mState.sourceClip = kPortraitSourceClip;
+ mOutput.mState.destinationClip = kPortraitDestinationClip;
mOutput.mState.transform = ui::Transform{kPortraitOrientation};
mOutput.mState.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4ae6dad..6ff39b4 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -146,12 +146,12 @@
return mCompositionDisplay->getState().dataspace;
}
-void DisplayDevice::setLayerStack(uint32_t stack) {
+void DisplayDevice::setLayerStack(ui::LayerStack stack) {
mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
}
-void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
- mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
+void DisplayDevice::setDisplaySize(int width, int height) {
+ mCompositionDisplay->setBounds(ui::Size(width, height));
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
@@ -222,10 +222,13 @@
const bool needsFiltering =
(!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
- Rect scissor = globalTransform.transform(viewport);
- if (scissor.isEmpty()) {
- scissor = displayBounds;
+ Rect sourceClip = globalTransform.transform(viewport);
+ if (sourceClip.isEmpty()) {
+ sourceClip = displayBounds;
}
+ // For normal display use we always set the source and destination clip
+ // rectangles to the same values.
+ const Rect& destinationClip = sourceClip;
uint32_t transformOrientation;
@@ -236,8 +239,8 @@
transformOrientation = ui::Transform::toRotationFlags(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
- frame, viewport, scissor, needsFiltering);
+ getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
+ sourceClip, destinationClip, needsFiltering);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -286,7 +289,7 @@
return mCompositionDisplay->getState().needsFiltering;
}
-uint32_t DisplayDevice::getLayerStack() const {
+ui::LayerStack DisplayDevice::getLayerStack() const {
return mCompositionDisplay->getState().layerStackId;
}
@@ -302,8 +305,8 @@
return mCompositionDisplay->getState().frame;
}
-const Rect& DisplayDevice::getScissor() const {
- return mCompositionDisplay->getState().scissor;
+const Rect& DisplayDevice::getSourceClip() const {
+ return mCompositionDisplay->getState().sourceClip;
}
bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ff48ecd..f45feae 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -16,8 +16,6 @@
#pragma once
-#include <stdlib.h>
-
#include <memory>
#include <optional>
#include <string>
@@ -30,7 +28,7 @@
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayState.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
@@ -80,12 +78,12 @@
// secure surfaces.
bool isSecure() const;
- int getWidth() const;
- int getHeight() const;
+ int getWidth() const;
+ int getHeight() const;
+ ui::Size getSize() const { return {getWidth(), getHeight()}; }
- void setLayerStack(uint32_t stack);
- void setDisplaySize(const int newWidth, const int newHeight);
-
+ void setLayerStack(ui::LayerStack);
+ void setDisplaySize(int width, int height);
void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
@@ -96,9 +94,9 @@
const ui::Transform& getTransform() const;
const Rect& getViewport() const;
const Rect& getFrame() const;
- const Rect& getScissor() const;
+ const Rect& getSourceClip() const;
bool needsFiltering() const;
- uint32_t getLayerStack() const;
+ ui::LayerStack getLayerStack() const;
const std::optional<DisplayId>& getId() const;
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -185,7 +183,7 @@
int32_t sequenceId = sNextSequenceId++;
std::optional<DisplayId> displayId;
sp<IGraphicBufferProducer> surface;
- uint32_t layerStack = NO_LAYER_STACK;
+ ui::LayerStack layerStack = ui::NO_LAYER_STACK;
Rect viewport;
Rect frame;
ui::Rotation orientation = ui::ROTATION_0;
@@ -271,7 +269,7 @@
Rect getSourceCrop() const override {
// use the projected display viewport by default.
if (mSourceCrop.isEmpty()) {
- return mDisplay->getScissor();
+ return mDisplay->getSourceClip();
}
// Recompute the device transformation for the source crop.
@@ -280,14 +278,14 @@
ui::Transform translateLogical;
ui::Transform scale;
const Rect& viewport = mDisplay->getViewport();
- const Rect& scissor = mDisplay->getScissor();
+ const Rect& sourceClip = mDisplay->getSourceClip();
const Rect& frame = mDisplay->getFrame();
const auto flags = ui::Transform::toRotationFlags(mDisplay->getPhysicalOrientation());
rotation.set(flags, getWidth(), getHeight());
translateLogical.set(-viewport.left, -viewport.top);
- translatePhysical.set(scissor.left, scissor.top);
+ translatePhysical.set(sourceClip.left, sourceClip.top);
scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
frame.getHeight() / float(viewport.getHeight()));
const ui::Transform finalTransform =
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 153cfe7..f8d45c0 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -109,7 +109,6 @@
android::Hwc2::Display display, int64_t timestamp,
android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
if (mVsyncSwitchingSupported) {
- // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
mCallback->onVsyncReceived(mSequenceId, display, timestamp,
std::make_optional(vsyncPeriodNanos));
} else {
@@ -579,12 +578,13 @@
sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
- auto displayFences = mDisplayData.at(displayId).releaseFences;
- if (displayFences.count(layer) == 0) {
+ const auto& displayFences = mDisplayData.at(displayId).releaseFences;
+ auto fence = displayFences.find(layer);
+ if (fence == displayFences.end()) {
ALOGV("getLayerReleaseFence: Release fence not found");
return Fence::NO_FENCE;
}
- return displayFences[layer];
+ return fence->second;
}
status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6f8df95..6ff23c5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -28,7 +28,6 @@
#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -81,7 +80,6 @@
mName(args.name),
mClientRef(args.client),
mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
-
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -101,6 +99,7 @@
mCurrentState.active.w = UINT32_MAX;
mCurrentState.active.h = UINT32_MAX;
mCurrentState.active.transform.set(0, 0);
+ mCurrentState.frameNumber = 0;
mCurrentState.transform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
@@ -116,7 +115,6 @@
mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
- mCurrentState.frameRate = 0.f;
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -415,7 +413,7 @@
win.bottom -= roundedCornersCrop.top;
}
-void Layer::latchBasicGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+void Layer::prepareBasicGeometryCompositionState() {
const auto& drawingState{getDrawingState()};
const uint32_t layerStack = getLayerStack();
const auto alpha = static_cast<float>(getAlpha());
@@ -428,31 +426,28 @@
: Hwc2::IComposerClient::BlendMode::COVERAGE;
}
- // TODO(b/121291683): Instead of filling in a passed-in compositionState
- // structure, switch to Layer owning the structure and have
- // CompositionEngine be able to get a reference to it.
-
- compositionState.layerStackId =
+ auto* compositionState = editCompositionState();
+ compositionState->layerStackId =
(layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
- compositionState.internalOnly = getPrimaryDisplayOnly();
- compositionState.isVisible = isVisible();
- compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
- compositionState.shadowRadius = mEffectiveShadowRadius;
+ compositionState->internalOnly = getPrimaryDisplayOnly();
+ compositionState->isVisible = isVisible();
+ compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+ compositionState->shadowRadius = mEffectiveShadowRadius;
- compositionState.contentDirty = contentDirty;
+ compositionState->contentDirty = contentDirty;
contentDirty = false;
- compositionState.geomLayerBounds = mBounds;
- compositionState.geomLayerTransform = getTransform();
- compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
- compositionState.transparentRegionHint = getActiveTransparentRegion(drawingState);
+ compositionState->geomLayerBounds = mBounds;
+ compositionState->geomLayerTransform = getTransform();
+ compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
+ compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
- compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
- compositionState.alpha = alpha;
- compositionState.backgroundBlurRadius = drawingState.backgroundBlurRadius;
+ compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+ compositionState->alpha = alpha;
+ compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
}
-void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+void Layer::prepareGeometryCompositionState() {
const auto& drawingState{getDrawingState()};
int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
@@ -468,45 +463,48 @@
}
}
- compositionState.geomBufferSize = getBufferSize(drawingState);
- compositionState.geomContentCrop = getBufferCrop();
- compositionState.geomCrop = getCrop(drawingState);
- compositionState.geomBufferTransform = getBufferTransform();
- compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
- compositionState.geomUsesSourceCrop = usesSourceCrop();
- compositionState.isSecure = isSecure();
+ auto* compositionState = editCompositionState();
- compositionState.type = type;
- compositionState.appId = appId;
+ compositionState->geomBufferSize = getBufferSize(drawingState);
+ compositionState->geomContentCrop = getBufferCrop();
+ compositionState->geomCrop = getCrop(drawingState);
+ compositionState->geomBufferTransform = getBufferTransform();
+ compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+ compositionState->geomUsesSourceCrop = usesSourceCrop();
+ compositionState->isSecure = isSecure();
+
+ compositionState->type = type;
+ compositionState->appId = appId;
}
-void Layer::latchPerFrameState(compositionengine::LayerFECompositionState& compositionState) const {
+void Layer::preparePerFrameCompositionState() {
const auto& drawingState{getDrawingState()};
- compositionState.forceClientComposition = false;
+ auto* compositionState = editCompositionState();
- compositionState.isColorspaceAgnostic = isColorSpaceAgnostic();
- compositionState.dataspace = getDataSpace();
- compositionState.colorTransform = getColorTransform();
- compositionState.colorTransformIsIdentity = !hasColorTransform();
- compositionState.surfaceDamage = surfaceDamageRegion;
- compositionState.hasProtectedContent = isProtected();
+ compositionState->forceClientComposition = false;
+
+ compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
+ compositionState->dataspace = getDataSpace();
+ compositionState->colorTransform = getColorTransform();
+ compositionState->colorTransformIsIdentity = !hasColorTransform();
+ compositionState->surfaceDamage = surfaceDamageRegion;
+ compositionState->hasProtectedContent = isProtected();
const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
const bool drawsShadows = mEffectiveShadowRadius != 0.f;
- compositionState.isOpaque =
+ compositionState->isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
// Force client composition for special cases known only to the front-end.
if (isHdrY410() || usesRoundedCorners || drawsShadows) {
- compositionState.forceClientComposition = true;
+ compositionState->forceClientComposition = true;
}
}
-void Layer::latchCursorCompositionState(
- compositionengine::LayerFECompositionState& compositionState) const {
- // This gives us only the "orientation" component of the transform
+void Layer::prepareCursorCompositionState() {
const State& drawingState{getDrawingState()};
+ auto* compositionState = editCompositionState();
// Apply the layer's transform, followed by the display's global transform
// Here we're guaranteed that the layer's transform preserves rects
@@ -515,30 +513,50 @@
Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
Rect frame(getTransform().transform(bounds));
- compositionState.cursorFrame = frame;
+ compositionState->cursorFrame = frame;
+}
+
+sp<compositionengine::LayerFE> Layer::asLayerFE() const {
+ return const_cast<compositionengine::LayerFE*>(
+ static_cast<const compositionengine::LayerFE*>(this));
+}
+
+sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+ return nullptr;
+}
+
+compositionengine::LayerFECompositionState* Layer::editCompositionState() {
+ return nullptr;
+}
+
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+ return nullptr;
}
bool Layer::onPreComposition(nsecs_t) {
return false;
}
-void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
- compositionengine::LayerFE::StateSubset subset) const {
+void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
using StateSubset = compositionengine::LayerFE::StateSubset;
switch (subset) {
case StateSubset::BasicGeometry:
- latchBasicGeometry(compositionState);
+ prepareBasicGeometryCompositionState();
break;
case StateSubset::GeometryAndContent:
- latchBasicGeometry(compositionState);
- latchGeometry(compositionState);
- latchPerFrameState(compositionState);
+ prepareBasicGeometryCompositionState();
+ prepareGeometryCompositionState();
+ preparePerFrameCompositionState();
break;
case StateSubset::Content:
- latchPerFrameState(compositionState);
+ preparePerFrameCompositionState();
+ break;
+
+ case StateSubset::Cursor:
+ prepareCursorCompositionState();
break;
}
}
@@ -553,7 +571,7 @@
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
- if (!getCompositionLayer()) {
+ if (!getCompositionState()) {
return {};
}
@@ -1244,7 +1262,10 @@
return true;
}
-bool Layer::setFrameRate(float frameRate) {
+bool Layer::setFrameRate(FrameRate frameRate) {
+ if (!mFlinger->useFrameRateApi) {
+ return false;
+ }
if (mCurrentState.frameRate == frameRate) {
return false;
}
@@ -1256,11 +1277,8 @@
return true;
}
-std::optional<float> Layer::getFrameRate() const {
- const auto frameRate = getDrawingState().frameRate;
- if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) return frameRate;
-
- return {};
+Layer::FrameRate Layer::getFrameRate() const {
+ return getDrawingState().frameRate;
}
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1421,7 +1439,7 @@
StringAppendF(&result, " %s\n", name.c_str());
const State& layerState(getDrawingState());
- const auto& compositionState = outputLayer->getState();
+ const auto& outputLayerState = outputLayer->getState();
if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
StringAppendF(&result, " rel %6d | ", layerState.z);
@@ -1430,13 +1448,10 @@
}
StringAppendF(&result, " %10d | ", mWindowType);
StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
- StringAppendF(&result, "%10s | ",
- toString(getCompositionLayer() ? compositionState.bufferTransform
- : static_cast<Hwc2::Transform>(0))
- .c_str());
- const Rect& frame = compositionState.displayFrame;
+ StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
+ const Rect& frame = outputLayerState.displayFrame;
StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
- const FloatRect& crop = compositionState.sourceCrop;
+ const FloatRect& crop = outputLayerState.sourceCrop;
StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
crop.bottom);
@@ -1803,6 +1818,15 @@
}
}
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+ visitor(this);
+ const LayerVector& children =
+ state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+ for (const sp<Layer>& child : children) {
+ child->traverse(state, visitor);
+ }
+}
+
LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree) {
LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
@@ -2204,17 +2228,13 @@
return mDrawingState.inputInfo.token != nullptr;
}
-std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const {
- return nullptr;
-}
-
bool Layer::canReceiveInput() const {
return !isHiddenByPolicy();
}
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const sp<const DisplayDevice>& display) const {
- return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionLayer().get());
+ return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
}
Region Layer::debugGetVisibleRegionOnDefaultDisplay() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c75a570..c110462 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -66,7 +66,6 @@
class LayerDebugInfo;
namespace compositionengine {
-class Layer;
class OutputLayer;
struct LayerFECompositionState;
}
@@ -94,7 +93,7 @@
uint32_t textureName;
};
-class Layer : public compositionengine::LayerFE {
+class Layer : public virtual RefBase, compositionengine::LayerFE {
static std::atomic<int32_t> sSequence;
static constexpr int32_t PRIORITY_UNSET = -1;
@@ -136,6 +135,34 @@
float radius = 0.0f;
};
+ // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+ // the layer.
+ enum class FrameRateCompatibility {
+ Default, // Layer didn't specify any specific handling strategy
+
+ ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+ // content properly. Any other value will result in a pull down.
+
+ NoVote, // Layer doesn't have any requirements for the refresh rate and
+ // should not be considered when the display refresh rate is determined.
+ };
+
+ // Encapsulates the frame rate and compatibility of the layer. This information will be used
+ // when the display refresh rate is determined.
+ struct FrameRate {
+ float rate;
+ FrameRateCompatibility type;
+
+ FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
+ FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+
+ bool operator==(const FrameRate& other) const {
+ return rate == other.rate && type == other.type;
+ }
+
+ bool operator!=(const FrameRate& other) const { return !(*this == other); }
+ };
+
struct State {
Geometry active_legacy;
Geometry requested_legacy;
@@ -189,6 +216,7 @@
ui::Dataspace dataspace;
// The fields below this point are only used by BufferStateLayer
+ uint64_t frameNumber;
Geometry active;
uint32_t transform;
@@ -226,7 +254,7 @@
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
- float frameRate;
+ FrameRate frameRate;
};
explicit Layer(const LayerCreationArgs& args);
@@ -328,8 +356,8 @@
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
virtual bool setCrop(const Rect& /*crop*/) { return false; };
virtual bool setFrame(const Rect& /*frame*/) { return false; };
- virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/,
- nsecs_t /*desiredPresentTime*/,
+ virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
+ nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
const client_cache_t& /*clientCacheId*/) {
return false;
};
@@ -344,6 +372,10 @@
return false;
};
virtual void forceSendCallbacks() {}
+ virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
+ nsecs_t /*requestedPresentTime*/) {
+ return false;
+ }
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
bool setShadowRadius(float shadowRadius);
@@ -361,7 +393,8 @@
// visually.
bool isLegacyDataSpace() const;
- virtual std::shared_ptr<compositionengine::Layer> getCompositionLayer() const;
+ virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
+ virtual compositionengine::LayerFECompositionState* editCompositionState();
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
@@ -506,6 +539,7 @@
virtual void updateCloneBufferInfo(){};
protected:
+ sp<compositionengine::LayerFE> asLayerFE() const;
sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
bool isClone() { return mClonedFrom != nullptr; }
bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
@@ -523,10 +557,9 @@
/*
* compositionengine::LayerFE overrides
*/
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(nsecs_t) override;
- void latchCompositionState(compositionengine::LayerFECompositionState&,
- compositionengine::LayerFE::StateSubset subset) const override;
- void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
+ void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
std::optional<LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
std::optional<LayerSettings> prepareShadowClientComposition(
@@ -536,9 +569,10 @@
const char* getDebugName() const override;
protected:
- void latchBasicGeometry(compositionengine::LayerFECompositionState& outState) const;
- void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
- virtual void latchPerFrameState(compositionengine::LayerFECompositionState& outState) const;
+ void prepareBasicGeometryCompositionState();
+ void prepareGeometryCompositionState();
+ virtual void preparePerFrameCompositionState();
+ void prepareCursorCompositionState();
public:
virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
@@ -566,6 +600,8 @@
// If a buffer was replaced this frame, release the former buffer
virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
+ virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ const CompositorTiming& /*compositorTiming*/) {}
/*
* doTransaction - process the transaction. This is a good place to figure
* out which attributes of the surface have changed.
@@ -674,10 +710,19 @@
// corner definition and converting it into current layer's coordinates.
// As of now, only 1 corner radius per display list is supported. Subsequent ones will be
// ignored.
- RoundedCornerState getRoundedCornerState() const;
+ virtual RoundedCornerState getRoundedCornerState() const;
renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ /**
+ * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+ * which will not emit children who have relativeZOrder to another layer, this method
+ * just directly emits all children. It also emits them in no particular order.
+ * So this method is not suitable for graphical operations, as it doesn't represent
+ * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+ * book-keeping.
+ */
+ void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -744,9 +789,8 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- constexpr static auto FRAME_RATE_NO_VOTE = -1.0f;
- virtual bool setFrameRate(float frameRate);
- virtual std::optional<float> getFrameRate() const;
+ virtual bool setFrameRate(FrameRate frameRate);
+ virtual FrameRate getFrameRate() const;
protected:
// constant
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 7c959b9..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -86,6 +86,14 @@
layer->traverseInReverseZOrder(stateSet, visitor);
}
}
+
+void LayerVector::traverse(const Visitor& visitor) const {
+ for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+ const auto& layer = (*this)[i];
+ layer->traverse(mStateSet, visitor);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
using Visitor = std::function<void(Layer*)>;
void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+ void traverse(const Visitor& visitor) const;
private:
const StateSet mStateSet;
};
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d3d9d3a..682679c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -155,7 +155,7 @@
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setFrameRate(Layer::FRAME_RATE_NO_VOTE);
+ mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote));
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -208,7 +208,7 @@
const int32_t buttom = top + display->getHeight() / 32;
auto buffer = mBufferCache[refreshRate.fps];
- mLayer->setBuffer(buffer, 0, 0, {});
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
mLayer->setFrame(Rect(left, top, right, buttom));
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index ca41608..809a0e5 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -542,7 +542,8 @@
resetLocked();
}
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
+ bool* periodFlushed) {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index c6aadbb..2d9afc9 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -49,7 +49,8 @@
virtual void reset() = 0;
virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
virtual void beginResync() = 0;
- virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
+ virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) = 0;
virtual void endResync() = 0;
virtual void setPeriod(nsecs_t period) = 0;
virtual nsecs_t getPeriod() = 0;
@@ -125,7 +126,8 @@
// down the DispSync model, and false otherwise.
// periodFlushed will be set to true if mPendingPeriod is flushed to
// mIntendedPeriod, and false otherwise.
- bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
+ bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) override;
void endResync() override;
// The setPeriod method sets the vsync event model's period to a specific
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b976523..b313777 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRate().has_value()) {
+ if (layer.getFrameRate().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -66,7 +66,6 @@
ATRACE_INT(tag.c_str(), fps);
ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
}
-
} // namespace
LayerHistory::LayerHistory()
@@ -102,23 +101,6 @@
partitionLayers(now);
- // Find the maximum refresh rate among recently active layers.
- for (const auto& [activeLayer, info] : activeLayers()) {
- const bool recent = info->isRecentlyActive(now);
-
- if (recent || CC_UNLIKELY(mTraceEnabled)) {
- const float refreshRate = info->getRefreshRate(now);
- if (recent && refreshRate > 0.0f) {
- if (const auto layer = activeLayer.promote(); layer) {
- const int32_t priority = layer->getFrameRateSelectionPriority();
- // TODO(b/142507166): This is where the scoring algorithm should live.
- // Layers should be organized by priority
- ALOGD("Layer has priority: %d", priority);
- }
- }
- }
- }
-
LayerHistory::Summary summary;
for (const auto& [weakLayer, info] : activeLayers()) {
const bool recent = info->isRecentlyActive(now);
@@ -126,18 +108,27 @@
// Only use the layer if the reference still exists.
if (layer || CC_UNLIKELY(mTraceEnabled)) {
// Check if frame rate was set on layer.
- auto frameRate = layer->getFrameRate();
- if (frameRate.has_value() && frameRate.value() > 0.f) {
- summary.push_back(
- {layer->getName(), LayerVoteType::Explicit, *frameRate, /* weight */ 1.0f});
+ const auto frameRate = layer->getFrameRate();
+ if (frameRate.rate > 0.f) {
+ const auto voteType = [&]() {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return LayerVoteType::ExplicitDefault;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return LayerVoteType::ExplicitExactOrMultiple;
+ case Layer::FrameRateCompatibility::NoVote:
+ return LayerVoteType::NoVote;
+ }
+ }();
+ summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
} else if (recent) {
- frameRate = info->getRefreshRate(now);
- summary.push_back({layer->getName(), LayerVoteType::Heuristic, *frameRate,
+ summary.push_back({layer->getName(), LayerVoteType::Heuristic,
+ info->getRefreshRate(now),
/* weight */ 1.0f});
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weakLayer, round<int>(*frameRate));
+ trace(weakLayer, round<int>(frameRate.rate));
}
}
}
@@ -187,6 +178,5 @@
mActiveLayersEnd = 0;
}
-
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index a6d2c74..6ef6ce4 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
- if (layer.getFrameRate().has_value()) {
+ if (layer.getFrameRate().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -63,19 +63,22 @@
const auto& name = layer->getName();
const auto noVoteTag = "LFPS NoVote " + name;
const auto heuristicVoteTag = "LFPS Heuristic " + name;
- const auto explicitVoteTag = "LFPS Explicit " + name;
+ const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
+ const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
const auto minVoteTag = "LFPS Min " + name;
const auto maxVoteTag = "LFPS Max " + name;
ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
- ATRACE_INT(explicitVoteTag.c_str(), type == LayerHistory::LayerVoteType::Explicit ? fps : 0);
+ ATRACE_INT(explicitDefaultVoteTag.c_str(),
+ type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
+ ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
+ type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
}
-
} // namespace
LayerHistoryV2::LayerHistoryV2()
@@ -120,6 +123,10 @@
continue;
}
+ // TODO(b/144307188): This needs to be plugged into layer summary as
+ // an additional parameter.
+ ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
+
const bool recent = info->isRecentlyActive(now);
if (recent) {
const auto [type, refreshRate] = info->getRefreshRate(now);
@@ -160,12 +167,18 @@
i++;
// Set layer vote if set
const auto frameRate = layer->getFrameRate();
- if (frameRate.has_value()) {
- if (*frameRate == Layer::FRAME_RATE_NO_VOTE) {
- info->setLayerVote(LayerVoteType::NoVote, 0.f);
- } else {
- info->setLayerVote(LayerVoteType::Explicit, *frameRate);
+ const auto voteType = [&]() {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return LayerVoteType::ExplicitDefault;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return LayerVoteType::ExplicitExactOrMultiple;
+ case Layer::FrameRateCompatibility::NoVote:
+ return LayerVoteType::NoVote;
}
+ }();
+ if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+ info->setLayerVote(voteType, frameRate.rate);
} else {
info->resetLayerVote();
}
@@ -202,5 +215,4 @@
mActiveLayersEnd = 0;
}
-
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index d94d758..345b8f9 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -54,8 +54,15 @@
return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
}
+bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
+ return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+ mFrameTimeValidSince.time_since_epoch())
+ .count();
+}
+
bool LayerInfoV2::isFrequent(nsecs_t now) const {
- // Assume layer is infrequent if too few present times have been recorded.
+ // If we know nothing about this layer we consider it as frequent as it might be the start
+ // of an animation.
if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
return true;
}
@@ -63,12 +70,24 @@
// Layer is frequent if the earliest value in the window of most recent present times is
// within threshold.
const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
+ if (!isFrameTimeValid(*it)) {
+ return true;
+ }
+
const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
return it->queueTime >= threshold;
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
// The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ if (mFrameTimes.size() < 2) {
+ return false;
+ }
+
+ if (!isFrameTimeValid(mFrameTimes.front())) {
+ return false;
+ }
+
if (mFrameTimes.size() < HISTORY_SIZE &&
mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
return false;
@@ -100,8 +119,7 @@
static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
// Now once we calculated the refresh rate we need to make sure that all the frames we captured
- // are evenly distrubuted and we don't calculate the average across some burst of frames.
-
+ // are evenly distributed and we don't calculate the average across some burst of frames.
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
const nsecs_t presentTimeDeltas =
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 564f05e..90f6310 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -82,12 +82,25 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- void clearHistory() { mFrameTimes.clear(); }
+ void clearHistory() {
+ // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+ // We are not deleting the old frame to keep track of whether we should treat the first
+ // buffer as Max as we don't know anything about this layer or Min as this layer is
+ // posting infrequent updates.
+ mFrameTimeValidSince = std::chrono::steady_clock::now();
+ }
private:
+ // Used to store the layer timestamps
+ struct FrameTimeData {
+ nsecs_t presetTime; // desiredPresentTime, if provided
+ nsecs_t queueTime; // buffer queue time
+ };
+
bool isFrequent(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible();
+ bool isFrameTimeValid(const FrameTimeData&) const;
// Used for sanitizing the heuristic data
const nsecs_t mHighRefreshRatePeriod;
@@ -103,12 +116,9 @@
float fps;
} mLayerVote;
- // Used to store the layer timestamps
- struct FrameTimeData {
- nsecs_t presetTime; // desiredPresentTime, if provided
- nsecs_t queueTime; // buffer queue time
- };
std::deque<FrameTimeData> mFrameTimes;
+ std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+ std::chrono::steady_clock::now();
static constexpr size_t HISTORY_SIZE = 90;
static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c187049..8202515 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -37,7 +37,8 @@
int explicitContentFramerate = 0;
for (const auto& layer : layers) {
const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
- if (layer.vote == LayerVoteType::Explicit) {
+ if (layer.vote == LayerVoteType::ExplicitDefault ||
+ layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
if (desiredRefreshRateRound > explicitContentFramerate) {
explicitContentFramerate = desiredRefreshRateRound;
}
@@ -94,7 +95,8 @@
int noVoteLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
- int explicitVoteLayers = 0;
+ int explicitDefaultVoteLayers = 0;
+ int explicitExactOrMultipleVoteLayers = 0;
for (const auto& layer : layers) {
if (layer.vote == LayerVoteType::NoVote)
noVoteLayers++;
@@ -102,8 +104,10 @@
minVoteLayers++;
else if (layer.vote == LayerVoteType::Max)
maxVoteLayers++;
- else if (layer.vote == LayerVoteType::Explicit)
- explicitVoteLayers++;
+ else if (layer.vote == LayerVoteType::ExplicitDefault)
+ explicitDefaultVoteLayers++;
+ else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+ explicitExactOrMultipleVoteLayers++;
}
// Only if all layers want Min we should return Min
@@ -112,7 +116,7 @@
}
// If we have some Max layers and no Explicit we should return Max
- if (maxVoteLayers > 0 && explicitVoteLayers == 0) {
+ if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
return *mAvailableRefreshRates.back();
}
@@ -125,14 +129,28 @@
}
for (const auto& layer : layers) {
+ ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
layer.vote == LayerVoteType::Max) {
continue;
}
- // If we have Explicit layers, ignore the Huristic ones
- if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
- continue;
+ // Adjust the weight in case we have explicit layers. The priority is:
+ // - ExplicitExactOrMultiple
+ // - ExplicitDefault
+ // - Heuristic
+ auto weight = layer.weight;
+ if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
+ if (layer.vote == LayerVoteType::Heuristic) {
+ weight /= 2.f;
+ }
+ }
+
+ if (explicitExactOrMultipleVoteLayers > 0) {
+ if (layer.vote == LayerVoteType::Heuristic ||
+ layer.vote == LayerVoteType::ExplicitDefault) {
+ weight /= 2.f;
+ }
}
for (auto& [refreshRate, overallScore] : scores) {
@@ -148,28 +166,30 @@
}
float layerScore;
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
if (displayFramesRem == 0) {
// Layer desired refresh rate matches the display rate.
- layerScore = layer.weight * 1.0f;
+ layerScore = weight * 1.0f;
} else if (displayFramesQuot == 0) {
// Layer desired refresh rate is higher the display rate.
- layerScore = layer.weight * layerPeriod / displayPeriod;
+ layerScore = weight *
+ (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
} else {
// Layer desired refresh rate is lower the display rate. Check how well it fits the
// cadence
auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
int iter = 2;
- static constexpr size_t MAX_ITERATOR = 10; // Stop calculating when score < 0.1
- while (diff > MARGIN && iter < MAX_ITERATOR) {
+ while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
diff = diff - (displayPeriod - diff);
iter++;
}
- layerScore = layer.weight * 1.0f / iter;
+ layerScore = weight * (1.0f / iter);
}
- ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
- layer.weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+ ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
+ 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
overallScore += layerScore;
}
}
@@ -196,20 +216,12 @@
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
- if (!mRefreshRateSwitching) {
- return *mCurrentRefreshRate;
- } else {
- return *mAvailableRefreshRates.front();
- }
+ return *mAvailableRefreshRates.front();
}
const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
- if (!mRefreshRateSwitching) {
- return *mCurrentRefreshRate;
- } else {
return *mAvailableRefreshRates.back();
- }
}
const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
@@ -217,23 +229,28 @@
return *mCurrentRefreshRate;
}
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
+ mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
+ return *mCurrentRefreshRate;
+ }
+ return mRefreshRates.at(mDefaultConfig);
+}
+
void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
std::lock_guard lock(mLock);
mCurrentRefreshRate = &mRefreshRates.at(configId);
}
-RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
- const std::vector<InputConfig>& configs,
- HwcConfigIndexType currentHwcConfig)
- : mRefreshRateSwitching(refreshRateSwitching) {
+RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig) {
init(configs, currentHwcConfig);
}
RefreshRateConfigs::RefreshRateConfigs(
- bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId)
- : mRefreshRateSwitching(refreshRateSwitching) {
+ HwcConfigIndexType currentConfigId) {
std::vector<InputConfig> inputConfigs;
for (size_t configId = 0; configId < configs.size(); ++configId) {
auto configGroup = HwcConfigGroupType(configs[configId]->getConfigGroup());
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 80d42cc..e5bb557 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -94,16 +94,16 @@
// Returns true if config is allowed by the current policy.
bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
- // Returns true if this device is doing refresh rate switching. This won't change at runtime.
- bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
-
// Describes the different options the layer voted for refresh rate
enum class LayerVoteType {
- NoVote, // Doesn't care about the refresh rate
- Min, // Minimal refresh rate available
- Max, // Maximal refresh rate available
- Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
- Explicit, // Specific refresh rate that was provided by the app
+ NoVote, // Doesn't care about the refresh rate
+ Min, // Minimal refresh rate available
+ Max, // Maximal refresh rate available
+ Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
+ ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+ // compatibility
+ ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
+ // ExactOrMultiple compatibility
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -149,6 +149,10 @@
// Returns the current refresh rate
const 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;
+
// Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
// runtime.
const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
@@ -164,10 +168,9 @@
nsecs_t vsyncPeriod = 0;
};
- RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ RefreshRateConfigs(const std::vector<InputConfig>& configs,
HwcConfigIndexType currentHwcConfig);
- RefreshRateConfigs(bool refreshRateSwitching,
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
private:
@@ -205,8 +208,6 @@
const RefreshRate* mMinSupportedRefreshRate;
const RefreshRate* mMaxSupportedRefreshRate;
- const bool mRefreshRateSwitching;
-
mutable std::mutex mLock;
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7de35af..331c8a8 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -64,7 +64,7 @@
std::unique_ptr<DispSync> createDispSync() {
// TODO (140302863) remove this and use the vsync_reactor system.
- if (property_get_bool("debug.sf.vsync_reactor", false)) {
+ if (property_get_bool("debug.sf.vsync_reactor", true)) {
// TODO (144707443) tune Predictor tunables.
static constexpr int default_rate = 60;
static constexpr auto initial_period =
@@ -108,12 +108,10 @@
mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
- if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
@@ -340,13 +338,15 @@
}
}
-void Scheduler::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) {
bool needsHwVsync = false;
*periodFlushed = false;
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
+ needsHwVsync =
+ mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
}
}
@@ -408,7 +408,8 @@
"SurfaceView - "
"com.google.android.youtube/"
"com.google.android.apps.youtube.app.WatchWhileActivity#0") {
- layer->setFrameRate(vote);
+ layer->setFrameRate(
+ Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
}
}
}
@@ -436,7 +437,7 @@
mFeatures.contentDetection =
!summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
- newConfigId = calculateRefreshRateType();
+ newConfigId = calculateRefreshRateConfigIndexType();
if (mFeatures.configId == newConfigId) {
return;
}
@@ -529,10 +530,6 @@
using base::StringAppendF;
const char* const states[] = {"off", "on"};
- const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
- StringAppendF(&result, "+ Refresh rate switching: %s\n", states[supported]);
- StringAppendF(&result, "+ Content detection: %s\n", states[mLayerHistory != nullptr]);
-
StringAppendF(&result, "+ Idle timer: %s\n",
mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
StringAppendF(&result, "+ Touch timer: %s\n\n",
@@ -549,7 +546,7 @@
return;
}
*currentState = newState;
- newConfigId = calculateRefreshRateType();
+ newConfigId = calculateRefreshRateConfigIndexType();
if (mFeatures.configId == newConfigId) {
return;
}
@@ -563,8 +560,10 @@
}
bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
+ // Traverse all the layers to see if any of them requested frame rate.
for (const auto& layer : mFeatures.contentRequirements) {
- if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::Explicit) {
+ if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault ||
+ layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple) {
return true;
}
}
@@ -572,36 +571,44 @@
return false;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateType() {
- if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
- return mRefreshRateConfigs.getCurrentRefreshRate().configId;
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
+ // This block of the code checks whether any layers used the SetFrameRate API. If they have,
+ // their request should be honored depending on other active layers.
+ if (layerHistoryHasClientSpecifiedFrameRate()) {
+ if (!mUseContentDetectionV2) {
+ return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
+ .configId;
+ } else {
+ return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+ .configId;
+ }
}
- // If the layer history doesn't have the frame rate specified, use the old path. NOTE:
- // if we remove the kernel idle timer, and use our internal idle timer, this code will have to
- // be refactored.
- if (!layerHistoryHasClientSpecifiedFrameRate()) {
- // If Display Power is not in normal operation we want to be in performance mode.
- // When coming back to normal mode, a grace period is given with DisplayPowerTimer
- if (!mFeatures.isDisplayPowerStateNormal ||
- mFeatures.displayPowerTimer == TimerState::Reset) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
- }
+ // If the layer history doesn't have the frame rate specified, check for other features and
+ // honor them. NOTE: If we remove the kernel idle timer, and use our internal idle timer, this
+ // code will have to be refactored. If Display Power is not in normal operation we want to be in
+ // performance mode. When coming back to normal mode, a grace period is given with
+ // DisplayPowerTimer.
+ if (mDisplayPowerTimer &&
+ (!mFeatures.isDisplayPowerStateNormal ||
+ mFeatures.displayPowerTimer == TimerState::Reset)) {
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+ }
- // As long as touch is active we want to be in performance mode
- if (mFeatures.touch == TouchState::Active) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
- }
+ // As long as touch is active we want to be in performance mode.
+ if (mTouchTimer && mFeatures.touch == TouchState::Active) {
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+ }
- // If timer has expired as it means there is no new content on the screen
- if (mFeatures.idleTimer == TimerState::Expired) {
- return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
- }
+ // If timer has expired as it means there is no new content on the screen.
+ if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
+ return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
if (!mUseContentDetectionV2) {
- // If content detection is off we choose performance as we don't know the content fps
+ // If content detection is off we choose performance as we don't know the content fps.
if (mFeatures.contentDetection == ContentDetectionState::Off) {
+ // NOTE: V1 always calls this, but this is not a default behavior for V2.
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
@@ -615,12 +622,16 @@
.configId;
}
- // There are no signals for refresh rate, just leave it as is
- return mRefreshRateConfigs.getCurrentRefreshRate().configId;
+ // There are no signals for refresh rate, just leave it as is.
+ return mRefreshRateConfigs.getCurrentRefreshRateByPolicy().configId;
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ // Make sure that the default config ID is first updated, before returned.
+ if (mFeatures.configId.has_value()) {
+ mFeatures.configId = calculateRefreshRateConfigIndexType();
+ }
return mFeatures.configId;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 01062f8..82b78e3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -109,7 +109,8 @@
// Passes a vsync sample to DispSync. periodFlushed will be true if
// DispSync detected that the vsync period changed, and false otherwise.
- void addResyncSample(nsecs_t timestamp, bool* periodFlushed);
+ void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed);
void addPresentFence(const std::shared_ptr<FenceTime>&);
void setIgnorePresentFences(bool ignore);
nsecs_t getDispSyncExpectedPresentTime();
@@ -177,7 +178,10 @@
void setVsyncPeriod(nsecs_t period);
- HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ // This function checks whether individual features that are affecting the refresh rate
+ // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+ // for the suggested refresh rate.
+ HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock);
bool layerHistoryHasClientSpecifiedFrameRate() REQUIRES(mFeatureStateLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ac32633..b467f24 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -38,7 +38,7 @@
VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
- : mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
+ : mTraceOn(property_get_bool("debug.sf.vsp_trace", true)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
@@ -71,12 +71,12 @@
return std::get<0>(mRateMap.find(mIdealPeriod)->second);
}
-void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
if (!validate(timestamp)) {
ALOGV("timestamp was too far off the last known timestamp");
- return;
+ return false;
}
if (timestamps.size() != kHistorySize) {
@@ -89,7 +89,7 @@
if (timestamps.size() < kMinimumSamplesForPrediction) {
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
- return;
+ return true;
}
// This is a 'simple linear regression' calculation of Y over X, with Y being the
@@ -143,7 +143,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {mIdealPeriod, 0};
- return;
+ return false;
}
nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
@@ -156,6 +156,7 @@
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
+ return true;
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index e366555..532fe9e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,7 +38,7 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- void addVsyncTimestamp(nsecs_t timestamp) final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
nsecs_t currentPeriod() const final;
void resetModel() final;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 53fa212..da73e4e 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -130,10 +130,11 @@
}
std::lock_guard<std::mutex> lk(mMutex);
- if (mIgnorePresentFences) {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
return true;
}
+ bool timestampAccepted = true;
for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
auto const time = (*it)->getCachedSignalTime();
if (time == Fence::SIGNAL_TIME_PENDING) {
@@ -141,7 +142,8 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
it = mUnfiredFences.erase(it);
}
}
@@ -152,7 +154,13 @@
}
mUnfiredFences.push_back(fence);
} else {
- mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ }
+
+ if (!timestampAccepted) {
+ mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
+ mPeriodConfirmationInProgress = true;
}
return mMoreSamplesNeeded;
@@ -160,8 +168,17 @@
void VSyncReactor::setIgnorePresentFences(bool ignoration) {
std::lock_guard<std::mutex> lk(mMutex);
- mIgnorePresentFences = ignoration;
- if (mIgnorePresentFences == true) {
+ mExternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+ mInternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
mUnfiredFences.clear();
}
}
@@ -177,14 +194,18 @@
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+ mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
}
void VSyncReactor::endPeriodTransition() {
- mPeriodTransitioningTo.reset();
- mLastHwVsync.reset();
+ setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
+ mPeriodTransitioningTo.reset();
+ mPeriodConfirmationInProgress = false;
+ mLastHwVsync.reset();
}
void VSyncReactor::setPeriod(nsecs_t period) {
@@ -208,27 +229,42 @@
void VSyncReactor::endResync() {}
-bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
- if (!mLastHwVsync || !mPeriodTransitioningTo) {
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
+ if (!mPeriodConfirmationInProgress) {
return false;
}
+
+ if (!mLastHwVsync && !HwcVsyncPeriod) {
+ return false;
+ }
+
+ auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+ static constexpr int allowancePercent = 10;
+ static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+ auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
+ if (HwcVsyncPeriod) {
+ return std::abs(*HwcVsyncPeriod - period) < allowance;
+ }
+
auto const distance = vsync_timestamp - *mLastHwVsync;
- return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+ return std::abs(distance - period) < allowance;
}
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) {
assert(periodFlushed);
std::lock_guard<std::mutex> lk(mMutex);
- if (periodChangeDetected(timestamp)) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(*mPeriodTransitioningTo);
+ if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+ if (mPeriodTransitioningTo) {
+ mTracker->setPeriod(*mPeriodTransitioningTo);
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(*mPeriodTransitioningTo);
+ }
+ *periodFlushed = true;
}
-
endPeriodTransition();
- *periodFlushed = true;
- } else if (mPeriodTransitioningTo) {
+ } else if (mPeriodConfirmationInProgress) {
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f318dcb..aa8a38d 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,7 +49,8 @@
// TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
void beginResync() final;
- bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) final;
+ bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) final;
void endResync() final;
status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
@@ -61,9 +62,12 @@
void reset() final;
private:
+ void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+ void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
- bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+ bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
+ REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncTracker> const mTracker;
@@ -71,10 +75,12 @@
size_t const mPendingLimit;
std::mutex mMutex;
- bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+ bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+ bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+ bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2b27884..a25b8a9 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -33,8 +33,10 @@
* to the model.
*
* \param [in] timestamp The timestamp when the vsync signal was.
+ * \return True if the timestamp was consistent with the internal model,
+ * False otherwise
*/
- virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+ virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
/*
* Access the next anticipated vsync time such that the anticipated time >= timePoint.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 62d47e1..f81179a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -45,6 +45,7 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -61,8 +62,10 @@
#include <renderengine/RenderEngine.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
#include <ui/UiConfig.h>
@@ -115,6 +118,7 @@
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
+#include <android/configuration.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
@@ -182,6 +186,19 @@
bool mLocked;
};
+// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV / 160.f;
+
+float getDensityFromProperty(const char* property, bool required) {
+ char value[PROPERTY_VALUE_MAX];
+ const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f;
+ if (!density && required) {
+ ALOGE("%s must be defined as a build property", property);
+ return FALLBACK_DENSITY;
+ }
+ return density / 160.f;
+}
+
// Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
bool validateCompositionDataspace(Dataspace dataspace) {
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
@@ -211,6 +228,7 @@
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
+bool SurfaceFlinger::useFrameRateApi;
std::string getHwcServiceName() {
char value[PROPERTY_VALUE_MAX] = {};
@@ -249,7 +267,8 @@
mFrameTracer(std::make_unique<FrameTracer>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
- mPendingSyncInputWindows(false) {}
+ mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
+ mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
@@ -369,6 +388,8 @@
// for production purposes later on.
setenv("TREBLE_TESTING_OVERRIDE", "true", true);
}
+
+ useFrameRateApi = use_frame_rate_api(true);
}
void SurfaceFlinger::onFirstRef()
@@ -538,12 +559,6 @@
readPersistentProperties();
mBootStage = BootStage::FINISHED;
- if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
- // set the refresh rate according to the policy
- const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
- changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
- }
-
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
@@ -734,8 +749,56 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) {
+ if (!displayToken || !state) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ state->layerStack = display->getLayerStack();
+ state->orientation = display->getOrientation();
+
+ const Rect viewport = display->getViewport();
+ state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) {
+ if (!displayToken || !info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ if (display->isVirtual()) {
+ return INVALID_OPERATION;
+ }
+
+ if (mEmulatedDisplayDensity) {
+ info->density = mEmulatedDisplayDensity;
+ } else {
+ info->density = display->isPrimary() ? mInternalDisplayDensity : FALLBACK_DENSITY;
+ }
+
+ info->secure = display->isSecure();
+
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) {
+ Vector<DisplayConfig>* configs) {
if (!displayToken || !configs) {
return BAD_VALUE;
}
@@ -747,78 +810,42 @@
return NAME_NOT_FOUND;
}
- // TODO: Not sure if display density should handled by SF any longer
- class Density {
- static float getDensityFromProperty(char const* propName) {
- char property[PROPERTY_VALUE_MAX];
- float density = 0.0f;
- if (property_get(propName, property, nullptr) > 0) {
- density = strtof(property, nullptr);
- }
- return density;
- }
- public:
- static float getEmuDensity() {
- return getDensityFromProperty("qemu.sf.lcd_density"); }
- static float getBuildDensity() {
- return getDensityFromProperty("ro.sf.lcd_density"); }
- };
+ const bool isInternal = (displayId == getInternalDisplayIdLocked());
configs->clear();
for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
- DisplayInfo info = DisplayInfo();
+ DisplayConfig config;
- float xdpi = hwConfig->getDpiX();
- float ydpi = hwConfig->getDpiY();
+ auto width = hwConfig->getWidth();
+ auto height = hwConfig->getHeight();
- info.w = hwConfig->getWidth();
- info.h = hwConfig->getHeight();
- // Default display viewport to display width and height
- info.viewportW = info.w;
- info.viewportH = info.h;
+ auto xDpi = hwConfig->getDpiX();
+ auto yDpi = hwConfig->getDpiY();
- if (displayId == getInternalDisplayIdLocked()) {
- // The density of the device is provided by a build property
- float density = Density::getBuildDensity() / 160.0f;
- if (density == 0) {
- // the build doesn't provide a density -- this is wrong!
- // use xdpi instead
- ALOGE("ro.sf.lcd_density must be defined as a build property");
- density = xdpi / 160.0f;
- }
- if (Density::getEmuDensity()) {
- // if "qemu.sf.lcd_density" is specified, it overrides everything
- xdpi = ydpi = density = Density::getEmuDensity();
- density /= 160.0f;
- }
- info.density = density;
-
- const auto display = getDefaultDisplayDeviceLocked();
- info.orientation = display->getOrientation();
-
- // This is for screenrecord
- const Rect viewport = display->getViewport();
- if (viewport.isValid()) {
- info.viewportW = uint32_t(viewport.getWidth());
- info.viewportH = uint32_t(viewport.getHeight());
- }
- info.layerStack = display->getLayerStack();
- } else {
- // TODO: where should this value come from?
- static const int TV_DENSITY = 213;
- info.density = TV_DENSITY / 160.0f;
-
- const auto display = getDisplayDeviceLocked(displayToken);
- info.layerStack = display->getLayerStack();
+ if (isInternal &&
+ (internalDisplayOrientation == ui::ROTATION_90 ||
+ internalDisplayOrientation == ui::ROTATION_270)) {
+ std::swap(width, height);
+ std::swap(xDpi, yDpi);
}
- info.xdpi = xdpi;
- info.ydpi = ydpi;
- info.fps = 1e9 / hwConfig->getVsyncPeriod();
+ config.resolution = ui::Size(width, height);
- const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
- info.appVsyncOffset = offset.late.app;
+ if (mEmulatedDisplayDensity) {
+ config.xDpi = mEmulatedDisplayDensity;
+ config.yDpi = mEmulatedDisplayDensity;
+ } else {
+ config.xDpi = xDpi;
+ config.yDpi = yDpi;
+ }
+
+ const nsecs_t period = hwConfig->getVsyncPeriod();
+ config.refreshRate = 1e9f / period;
+
+ const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
+ config.appVsyncOffset = offsets.late.app;
+ config.sfVsyncOffset = offsets.late.sf;
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -832,18 +859,9 @@
//
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
- info.presentationDeadline = hwConfig->getVsyncPeriod() - offset.late.sf + 1000000;
+ config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
- // All non-virtual displays are currently considered secure.
- info.secure = true;
-
- if (displayId == getInternalDisplayIdLocked() &&
- (internalDisplayOrientation == ui::ROTATION_90 ||
- internalDisplayOrientation == ui::ROTATION_270)) {
- std::swap(info.w, info.h);
- }
-
- configs->push_back(info);
+ configs->push_back(config);
}
return NO_ERROR;
@@ -1484,11 +1502,9 @@
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
int64_t timestamp,
- std::optional<hwc2_vsync_period_t> /*vsyncPeriod*/) {
+ std::optional<hwc2_vsync_period_t> vsyncPeriod) {
ATRACE_NAME("SF onVsync");
- // TODO(b/140201379): use vsyncPeriod in the new DispSync
-
Mutex::Autolock lock(mStateLock);
// Ignore any vsyncs from a previous hardware composer.
if (sequenceId != getBE().mComposerSequenceId) {
@@ -1505,7 +1521,7 @@
}
bool periodFlushed = false;
- mScheduler->addResyncSample(timestamp, &periodFlushed);
+ mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
mVSyncModulator->onRefreshRateChangeCompleted();
}
@@ -1857,13 +1873,13 @@
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
- auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) refreshArgs.layers.push_back(compositionLayer);
+ if (auto layerFE = layer->getCompositionEngineLayerFE())
+ refreshArgs.layers.push_back(layerFE);
});
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (sp<Layer> layer : mLayersWithQueuedFrames) {
- auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+ if (auto layerFE = layer->getCompositionEngineLayerFE())
+ refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
@@ -2016,12 +2032,10 @@
ATRACE_CALL();
ALOGV("postComposition");
- // Release any buffers which were replaced this frame
nsecs_t dequeueReadyTime = systemTime();
for (auto& layer : mLayersWithQueuedFrames) {
layer->releasePendingBuffer(dequeueReadyTime);
}
-
// |mStateLock| not needed as we are on the main thread
const auto displayDevice = getDefaultDisplayDeviceLocked();
@@ -2059,7 +2073,7 @@
compositorTiming = getBE().mCompositorTiming;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
presentFenceTime, compositorTiming);
if (frameLatched) {
@@ -2496,7 +2510,7 @@
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Notify all layers of available frames
- mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) {
+ mCurrentState.traverse([expectedPresentTime](Layer* layer) {
layer->notifyAvailableFrames(expectedPresentTime);
});
@@ -2506,7 +2520,7 @@
*/
if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2553,7 +2567,7 @@
sp<const DisplayDevice> hintDisplay;
uint32_t currentlayerStack = 0;
bool first = true;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
// NOTE: we rely on the fact that layers are sorted by
// layerStack first (so we don't have to traverse the list
// of displays for every layer).
@@ -2699,8 +2713,7 @@
auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
- getHwComposer().getConfigs(
+ std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
primaryDisplayId),
currentConfig);
mRefreshRateStats =
@@ -2781,7 +2794,7 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->commitChildList();
// If the layer can be reached when traversing mDrawingState, then the layer is no
@@ -2792,7 +2805,7 @@
});
commitOffscreenLayers();
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
}
void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
@@ -2817,7 +2830,7 @@
void SurfaceFlinger::commitOffscreenLayers() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2858,7 +2871,7 @@
// 3.) Layer 1 is latched.
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
@@ -2876,7 +2889,7 @@
// be shown on screen. Therefore, we need to latch and release buffers of offscreen
// layers to ensure dequeueBuffer doesn't block indefinitely.
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing,
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* l) { l->latchAndReleaseBuffer(); });
}
@@ -2911,7 +2924,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
// Only continue with the refresh if there is actually new work to do
return !mLayersWithQueuedFrames.empty() && newDataLatched;
@@ -3328,6 +3341,11 @@
layer->pushPendingState();
}
+ // Only set by BLAST adapter layers
+ if (what & layer_state_t::eProducerDisconnect) {
+ layer->onDisconnect();
+ }
+
if (what & layer_state_t::ePositionChanged) {
if (layer->setPosition(s.x, s.y)) {
flags |= eTraversalNeeded;
@@ -3535,7 +3553,9 @@
}
}
if (what & layer_state_t::eFrameRateChanged) {
- if (layer->setFrameRate(s.frameRate)) flags |= eTraversalNeeded;
+ if (layer->setFrameRate(
+ Layer::FrameRate(s.frameRate, Layer::FrameRateCompatibility::Default)))
+ flags |= eTraversalNeeded;
}
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
@@ -3577,7 +3597,8 @@
buffer = s.buffer;
}
if (buffer) {
- if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
+ if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
+ s.cachedBuffer)) {
flags |= eTraversalNeeded;
}
}
@@ -3733,7 +3754,7 @@
bool matchFound = true;
while (matchFound) {
matchFound = false;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (layer->getName() == uniqueName) {
matchFound = true;
uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
@@ -4104,7 +4125,7 @@
const bool clearAll = args.size() < 2;
const auto name = clearAll ? String8() : String8(args[1]);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (clearAll || layer->getName() == name.string()) {
layer->clearFrameStats();
}
@@ -4120,7 +4141,7 @@
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->logFrameStats();
});
@@ -4355,7 +4376,7 @@
result.append("Offscreen Layers:\n");
postMessageSync(new LambdaMessage([&]() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
layer->dumpCallingUidPid(result);
});
}
@@ -4422,8 +4443,13 @@
{
StringAppendF(&result, "Composition layers\n");
mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) compositionLayer->dump(result);
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState) return;
+
+ android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
+ layer->getDebugName() ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(result);
});
}
@@ -4614,7 +4640,9 @@
case GET_PHYSICAL_DISPLAY_TOKEN:
case GET_DISPLAY_COLOR_MODES:
case GET_DISPLAY_NATIVE_PRIMARIES:
+ case GET_DISPLAY_INFO:
case GET_DISPLAY_CONFIGS:
+ case GET_DISPLAY_STATE:
case GET_DISPLAY_STATS:
case GET_SUPPORTED_FRAME_TIMESTAMPS:
// Calling setTransactionState is safe, because you need to have been
@@ -4644,12 +4672,6 @@
}
return OK;
}
- // The following codes are deprecated and should never be allowed to access SF.
- case CONNECT_DISPLAY_UNUSED:
- case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: {
- ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
- return PERMISSION_DENIED;
- }
case CAPTURE_SCREEN_BY_ID: {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
@@ -5588,6 +5610,10 @@
// ---------------------------------------------------------------------------
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+ layersSortedByZ.traverse(visitor);
+}
+
void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
layersSortedByZ.traverseInZOrder(stateSet, visitor);
}
@@ -5677,26 +5703,19 @@
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
display->getActiveConfig(), vsyncPeriod);
- if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
- auto configId = mScheduler->getPreferredConfigId();
- auto preferredRefreshRate = configId
- ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
- : mRefreshRateConfigs->getMinRefreshRateByPolicy();
- ALOGV("trying to switch to Scheduler preferred config %d (%s)",
- preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
- if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
- ALOGV("switching to Scheduler preferred config %d",
- preferredRefreshRate.configId.value());
- setDesiredActiveConfig(
- {preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
- } else {
- // Set the highest allowed config
- setDesiredActiveConfig({mRefreshRateConfigs->getMaxRefreshRateByPolicy().configId,
- Scheduler::ConfigEvent::Changed});
- }
+ auto configId = mScheduler->getPreferredConfigId();
+ auto preferredRefreshRate = configId
+ ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+ // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
+ : mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
+ ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+ preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
+
+ if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
+ ALOGV("switching to Scheduler preferred config %d", preferredRefreshRate.configId.value());
+ setDesiredActiveConfig({preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
} else {
- ALOGV("switching to config %d", defaultConfig.value());
- setDesiredActiveConfig({defaultConfig, Scheduler::ConfigEvent::Changed});
+ LOG_ALWAYS_FATAL("Desired config not allowed: %d", preferredRefreshRate.configId.value());
}
return NO_ERROR;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f8980a5..8cabcf0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -248,6 +248,11 @@
static ui::Dataspace wideColorGamutCompositionDataspace;
static ui::PixelFormat wideColorGamutCompositionPixelFormat;
+ // Whether to use frame rate API when deciding about the refresh rate of the display. This
+ // variable is caches in SF, so that we can check it with each layer creation, and a void the
+ // overhead that is caused by reading from sysprop.
+ static bool useFrameRateApi;
+
static char const* getServiceName() ANDROID_API {
return "SurfaceFlinger";
}
@@ -384,6 +389,7 @@
renderengine::ShadowSettings globalShadowSettings;
+ void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
@@ -433,13 +439,13 @@
float frameScale, bool childrenOnly) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
- status_t getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) override;
+ status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
+ status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
+ status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
int getActiveConfig(const sp<IBinder>& displayToken) override;
- status_t getDisplayColorModes(const sp<IBinder>& displayToken,
- Vector<ui::ColorMode>* configs) override;
+ status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
- ui::DisplayPrimaries &primaries);
+ ui::DisplayPrimaries&) override;
ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
@@ -1164,6 +1170,9 @@
sp<RegionSamplingThread> mRegionSamplingThread;
ui::DisplayPrimaries mInternalDisplayPrimaries;
+ const float mInternalDisplayDensity;
+ const float mEmulatedDisplayDensity;
+
sp<IInputFlinger> mInputFlinger;
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
// Should only be accessed by the main thread.
@@ -1180,7 +1189,7 @@
const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
- bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+ bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
// This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 45889a5..f9658a7 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -77,7 +77,7 @@
ISchedulerCallback& schedulerCallback) {
return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
property_get_bool("debug.sf.use_content_detection_v2",
- false));
+ true));
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b4716eb..1a611f5 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -18,7 +18,9 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
#include <configstore/Utils.h>
+#include <utils/Log.h>
+#include <log/log.h>
#include <cstdlib>
#include <tuple>
@@ -227,8 +229,12 @@
}
bool refresh_rate_switching(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+#pragma clang diagnostic pop
if (temp.has_value()) {
+ ALOGW("Using deprecated refresh_rate_switching sysprop. Value: %d", *temp);
return *temp;
}
return defaultValue;
@@ -258,8 +264,17 @@
return defaultValue;
}
-bool use_smart_90_for_video(bool defaultValue) {
- auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+bool use_content_detection_for_refresh_rate(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ auto smart_90_deprecated = SurfaceFlingerProperties::use_smart_90_for_video();
+#pragma clang diagnostic pop
+ if (smart_90_deprecated.has_value()) {
+ ALOGW("Using deprecated use_smart_90_for_video sysprop. Value: %d", *smart_90_deprecated);
+ return *smart_90_deprecated;
+ }
+
+ auto temp = SurfaceFlingerProperties::use_content_detection_for_refresh_rate();
if (temp.has_value()) {
return *temp;
}
@@ -282,6 +297,14 @@
return defaultValue;
}
+bool use_frame_rate_api(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_frame_rate_api();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
#define DISPLAY_PRIMARY_SIZE 3
constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index e394cca..4c6e191 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -81,12 +81,14 @@
int32_t set_display_power_timer_ms(int32_t defaultValue);
-bool use_smart_90_for_video(bool defaultValue);
+bool use_content_detection_for_refresh_rate(bool defaultValue);
bool enable_protected_contents(bool defaultValue);
bool support_kernel_idle_timer(bool defaultValue);
+bool use_frame_rate_api(bool defaultValue);
+
android::ui::DisplayPrimaries getDisplayNativePrimaries();
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 12c98da..b2cce7c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -37,27 +37,33 @@
namespace impl {
-status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag,
- pulled_stats_event_list* data, void* cookie) {
+AStatsManager_PullAtomCallbackReturn TimeStats::pullAtomCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+ AStatsManager_PullAtomCallbackReturn result = AStatsManager_PULL_SKIP;
if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
- return timeStats->populateGlobalAtom(data);
+ result = timeStats->populateGlobalAtom(data);
} else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
- return timeStats->populateLayerAtom(data);
+ result = timeStats->populateLayerAtom(data);
}
- return STATS_PULL_SKIP;
+ // Enable timestats now. The first full pull for a given build is expected to
+ // have empty or very little stats, as stats are first enabled after the
+ // first pull is completed for either the global or layer stats.
+ timeStats->enable();
+ return result;
}
-status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStart == 0) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
flushPowerTimeLocked();
- struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data);
+ AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
@@ -67,7 +73,7 @@
mStatsDelegate->statsEventBuild(event);
clearGlobalLocked();
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
namespace {
@@ -105,7 +111,7 @@
}
} // namespace
-status_pull_atom_return_t TimeStats::populateLayerAtom(pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
@@ -124,7 +130,7 @@
}
for (const auto& layer : dumpStats) {
- struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data);
+ AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
@@ -142,11 +148,14 @@
}
}
+ mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
+ mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+
mStatsDelegate->statsEventBuild(event);
}
clearLayersLocked();
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
@@ -167,13 +176,19 @@
}
}
+TimeStats::~TimeStats() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->unregisterStatsPullAtomCallback(
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
void TimeStats::onBootFinished() {
- // Temporarily enable TimeStats by default. Telemetry is disabled while
- // we move onto statsd, so TimeStats is currently not exercised at all
- // during testing without enabling by default.
- // TODO: remove this, as we should only be paying this overhead on devices
- // where statsd exists.
- enable();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -352,7 +367,12 @@
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+ timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
+ timeStatsLayer.badDesiredPresentFrames += layerRecord.badDesiredPresentFrames;
+
layerRecord.droppedFrames = 0;
+ layerRecord.lateAcquireFrames = 0;
+ layerRecord.badDesiredPresentFrames = 0;
const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
timeRecords[0].frameTime.acquireTime);
@@ -466,6 +486,36 @@
}
}
+void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId,
+ static_cast<std::underlying_type<LatchSkipReason>::type>(reason));
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+
+ switch (reason) {
+ case LatchSkipReason::LateAcquire:
+ layerRecord.lateAcquireFrames++;
+ break;
+ }
+}
+
+void TimeStats::incrementBadDesiredPresent(int32_t layerId) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%d]-BadDesiredPresent", layerId);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+ layerRecord.badDesiredPresentFrames++;
+}
+
void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
if (!mEnabled.load()) return;
@@ -735,10 +785,6 @@
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -751,9 +797,6 @@
flushPowerTimeLocked();
mEnabled.store(false);
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
ALOGD("Disabled");
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71f06af..a428ef4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -70,6 +70,18 @@
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+ // Reasons why latching a particular buffer may be skipped
+ enum class LatchSkipReason {
+ // If the acquire fence did not fire on some devices we skip latching
+ // the buffer until the fence fires.
+ LateAcquire,
+ };
+ // Increments the counter of skipped latch buffers.
+ virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
+ // Increments the counter of bad desired present times for this layer.
+ // Bad desired present times are "implausible" and cause SurfaceFlinger to
+ // latch a buffer immediately to avoid stalling.
+ virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
@@ -116,6 +128,8 @@
// fences to signal, but rather waiting to receive those fences/timestamps.
int32_t waitData = -1;
uint32_t droppedFrames = 0;
+ uint32_t lateAcquireFrames = 0;
+ uint32_t badDesiredPresentFrames = 0;
TimeRecord prevTimeRecord;
std::deque<TimeRecord> timeRecords;
};
@@ -145,43 +159,46 @@
class StatsEventDelegate {
public:
virtual ~StatsEventDelegate() = default;
- virtual struct stats_event* addStatsEventToPullData(pulled_stats_event_list* data) {
- return add_stats_event_to_pull_data(data);
+ virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
+ return AStatsEventList_addStatsEvent(data);
}
virtual void registerStatsPullAtomCallback(int32_t atom_tag,
- stats_pull_atom_callback_t callback,
- pull_atom_metadata* metadata, void* cookie) {
- return register_stats_pull_atom_callback(atom_tag, callback, metadata, cookie);
+ AStatsManager_PullAtomCallback callback,
+ AStatsManager_PullAtomMetadata* metadata,
+ void* cookie) {
+ return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
}
virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
- return unregister_stats_pull_atom_callback(atom_tag);
+ return AStatsManager_unregisterPullAtomCallback(atom_tag);
}
- virtual void statsEventSetAtomId(struct stats_event* event, uint32_t atom_id) {
- return stats_event_set_atom_id(event, atom_id);
+ virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
+ return AStatsEvent_setAtomId(event, atom_id);
}
- virtual void statsEventWriteInt64(struct stats_event* event, int64_t field) {
- return stats_event_write_int64(event, field);
+ virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
+ return AStatsEvent_writeInt64(event, field);
}
- virtual void statsEventWriteString8(struct stats_event* event, const char* field) {
- return stats_event_write_string8(event, field);
+ virtual void statsEventWriteString8(AStatsEvent* event, const char* field) {
+ return AStatsEvent_writeString(event, field);
}
- virtual void statsEventWriteByteArray(struct stats_event* event, const uint8_t* buf,
+ virtual void statsEventWriteByteArray(AStatsEvent* event, const uint8_t* buf,
size_t numBytes) {
- return stats_event_write_byte_array(event, buf, numBytes);
+ return AStatsEvent_writeByteArray(event, buf, numBytes);
}
- virtual void statsEventBuild(struct stats_event* event) { return stats_event_build(event); }
+ virtual void statsEventBuild(AStatsEvent* event) { return AStatsEvent_build(event); }
};
// For testing only for injecting custom dependencies.
TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
+ ~TimeStats() override;
+
void onBootFinished() override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
@@ -200,6 +217,8 @@
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+ void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
+ void incrementBadDesiredPresent(int32_t layerId) override;
void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
@@ -220,10 +239,11 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- static status_pull_atom_return_t pullAtomCallback(int32_t atom_tag,
- pulled_stats_event_list* data, void* cookie);
- status_pull_atom_return_t populateGlobalAtom(pulled_stats_event_list* data);
- status_pull_atom_return_t populateLayerAtom(pulled_stats_event_list* data);
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie);
+ AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
+ AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 0ba90e2..e2f85cc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -83,6 +83,8 @@
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+ StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
+ StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
@@ -112,8 +114,14 @@
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
result.append(presentToPresent.toString());
+ const float averageFrameDuration = frameDuration.averageTime();
+ StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
+ std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
StringAppendF(&result, "frameDuration histogram is as below:\n");
result.append(frameDuration.toString());
+ const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+ StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
+ std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
result.append(renderEngineTiming.toString());
const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 702c50e..e374b73 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -46,6 +46,8 @@
std::string packageName;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
+ int32_t lateAcquireFrames = 0;
+ int32_t badDesiredPresentFrames = 0;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index daa67ae..0cdff8f 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -237,9 +237,13 @@
// destroyed the client side is dead and there won't be anyone to send the callback to.
sp<IBinder> surfaceControl = handle->surfaceControl.promote();
if (surfaceControl) {
+ FrameEventHistoryStats eventStats(handle->frameNumber,
+ handle->gpuCompositionDoneFence->getSnapshot().fence,
+ handle->compositorTiming, handle->refreshStartTime,
+ handle->dequeueReadyTime);
transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
handle->previousReleaseFence,
- handle->transformHint);
+ handle->transformHint, eventStats);
}
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 12ea8fe..f50147a 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -45,6 +45,11 @@
nsecs_t acquireTime = -1;
nsecs_t latchTime = -1;
uint32_t transformHint = 0;
+ std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+ CompositorTiming compositorTiming;
+ nsecs_t refreshStartTime = 0;
+ nsecs_t dequeueReadyTime = 0;
+ uint64_t frameNumber = 0;
};
class TransactionCompletedThread {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index ed2b220..b19eae6 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -311,6 +311,7 @@
scope: System
access: Readonly
prop_name: "ro.surface_flinger.refresh_rate_switching"
+ deprecated: true
}
prop {
@@ -344,14 +345,26 @@
prop_name: "ro.surface_flinger.set_display_power_timer_ms"
}
+# useContentDetectionForRefreshRate indicates whether Scheduler should detect content FPS, and try
+# to adjust the screen refresh rate based on that.
+prop {
+ api_name: "use_content_detection_for_refresh_rate"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+}
+
# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
# screen refresh rate based on that.
+# Replaced by useContentDetectionForRefreshRate
prop {
api_name: "use_smart_90_for_video"
type: Boolean
scope: Public
access: Readonly
prop_name: "ro.surface_flinger.use_smart_90_for_video"
+ deprecated: true
}
prop {
@@ -380,3 +393,13 @@
access: Readonly
prop_name: "ro.surface_flinger.supports_background_blur"
}
+
+# Indicates whether Scheduler should use frame rate API when adjusting the
+# display refresh rate.
+prop {
+ api_name: "use_frame_rate_api"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_frame_rate_api"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index d24ad18..c66523a 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -75,6 +75,7 @@
prop {
api_name: "refresh_rate_switching"
prop_name: "ro.surface_flinger.refresh_rate_switching"
+ deprecated: true
}
prop {
api_name: "running_without_sync_framework"
@@ -112,12 +113,21 @@
prop_name: "ro.surface_flinger.use_color_management"
}
prop {
+ api_name: "use_content_detection_for_refresh_rate"
+ prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+ }
+ prop {
api_name: "use_context_priority"
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
+ api_name: "use_frame_rate_api"
+ prop_name: "ro.surface_flinger.use_frame_rate_api"
+ }
+ prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
+ deprecated: true
}
prop {
api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index f339ab0..507d28b 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,20 +1,15 @@
-#include <algorithm>
-#include <functional>
-#include <limits>
-#include <ostream>
-
#include <gtest/gtest.h>
-
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <utils/String8.h>
+#include <functional>
+
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
@@ -67,14 +62,13 @@
mDisplay = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
- const ssize_t displayWidth = info.w;
- const ssize_t displayHeight = info.h;
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
// Background surface
mBGSurfaceControl =
- mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+ mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
+ config.resolution.getHeight(),
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
@@ -188,10 +182,10 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_TRUE(display != nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- Vector<DisplayInfo> configs;
+ Vector<DisplayConfig> configs;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
@@ -255,17 +249,6 @@
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
-TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
- setupVirtualDisplay();
-
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
- SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
- // This test currently fails. TODO(b/112002626): Find a way to properly create
- // a display in the test environment, so that destroy display can remove it.
- ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
-}
-
TEST_F(CredentialsTest, CaptureTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 3aa4474..0ed2ffb 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -46,24 +46,25 @@
&initialMin, &initialMax);
ASSERT_EQ(res, NO_ERROR);
- Vector<DisplayInfo> configs;
+ Vector<DisplayConfig> configs;
res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
ASSERT_EQ(res, NO_ERROR);
for (size_t i = 0; i < configs.size(); i++) {
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i, configs[i].fps,
- configs[i].fps);
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+ configs[i].refreshRate,
+ configs[i].refreshRate);
ASSERT_EQ(res, NO_ERROR);
int defaultConfig;
- float minFps;
- float maxFps;
+ float minRefreshRate;
+ float maxRefreshRate;
res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
- &minFps, &maxFps);
+ &minRefreshRate, &maxRefreshRate);
ASSERT_EQ(res, NO_ERROR);
ASSERT_EQ(defaultConfig, i);
- ASSERT_EQ(minFps, configs[i].fps);
- ASSERT_EQ(maxFps, configs[i].fps);
+ ASSERT_EQ(minRefreshRate, configs[i].refreshRate);
+ ASSERT_EQ(maxRefreshRate, configs[i].refreshRate);
}
res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 8a756a6..4023c66 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -14,24 +14,20 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-
+#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <utils/String8.h>
#include <limits>
-#include <ui/DisplayInfo.h>
-
-#include <utils/String8.h>
-
#include "BufferGenerator.h"
#include "utils/CallbackUtils.h"
#include "utils/ColorUtils.h"
@@ -231,10 +227,10 @@
ASSERT_EQ(NO_ERROR, mClient->initCheck());
mPrimaryDisplay = mClient->getInternalDisplayToken();
- DisplayInfo info;
- mClient->getDisplayInfo(mPrimaryDisplay, &info);
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
+ DisplayConfig config;
+ mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
+ mDisplayWidth = config.resolution.getWidth();
+ mDisplayHeight = config.resolution.getHeight();
Transaction setupTransaction;
setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index f7a6d96..5eb1739 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -13,17 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef ANDROID_LAYER_TRANSACTION_TEST_H
-#define ANDROID_LAYER_TRANSACTION_TEST_H
+
+#pragma once
#include <gtest/gtest.h>
-
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <hardware/hwcomposer_defs.h>
#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include "BufferGenerator.h"
#include "utils/ScreenshotUtils.h"
@@ -255,18 +253,16 @@
mDisplay = mClient->getInternalDisplayToken();
ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
- // get display width/height
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
- mDisplayRect =
- Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+ mDisplayRect = Rect(config.resolution);
+ mDisplayWidth = mDisplayRect.getWidth();
+ mDisplayHeight = mDisplayRect.getHeight();
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
mDisplayLayerStack = 0;
@@ -295,6 +291,5 @@
friend class LayerRenderPathTestHarness;
};
-} // namespace android
-#endif
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 3bbd12a..dbace11 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -199,10 +199,17 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setCrop_legacy(layer, Rect(0, 0, size, size))
- .apply();
+ if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+ Transaction()
+ .setCornerRadius(layer, cornerRadius)
+ .setCrop_legacy(layer, Rect(0, 0, size, size))
+ .apply();
+ } else {
+ Transaction()
+ .setCornerRadius(layer, cornerRadius)
+ .setFrame(layer, Rect(0, 0, size, size))
+ .apply();
+ }
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -226,12 +233,21 @@
ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
- .setPosition(child, 0, size / 2)
- .apply();
+ if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .reparent(child, parent->getHandle())
+ .setPosition(child, 0, size / 2)
+ .apply();
+ } else {
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setFrame(parent, Rect(0, 0, size, size))
+ .reparent(child, parent->getHandle())
+ .setFrame(child, Rect(0, size / 2, size, size))
+ .apply();
+ }
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -279,6 +295,48 @@
50 /* tolerance */);
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ if (!atoi(value)) {
+ // This device doesn't support blurs, no-op.
+ return;
+ }
+
+ auto size = 256;
+ auto center = size / 2;
+ auto blurRadius = 50;
+
+ sp<SurfaceControl> backgroundLayer;
+ ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+ sp<SurfaceControl> leftLayer;
+ ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+ sp<SurfaceControl> blurLayer1;
+ auto centralSquareSize = size / 2;
+ ASSERT_NO_FATAL_FAILURE(blurLayer1 =
+ createLayer("blur1", centralSquareSize, centralSquareSize));
+ ASSERT_NO_FATAL_FAILURE(
+ fillLayerColor(blurLayer1, Color::BLUE, centralSquareSize, centralSquareSize));
+
+ sp<SurfaceControl> blurLayer2;
+ ASSERT_NO_FATAL_FAILURE(blurLayer2 = createLayer("blur2", size, size));
+ ASSERT_NO_FATAL_FAILURE(
+ fillLayerColor(blurLayer2, Color::TRANSPARENT, centralSquareSize, centralSquareSize));
+
+ Transaction()
+ .setBackgroundBlurRadius(blurLayer1, blurRadius)
+ .setBackgroundBlurRadius(blurLayer2, blurRadius)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(center - 5, center - 5, center, center), Color{100, 100, 100, 255},
+ 40 /* tolerance */);
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 0459386..a1c4128 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -36,14 +36,13 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ const ui::Size& resolution = config.resolution;
// Background surface
- mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, displayHeight, 0);
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+ resolution.getHeight(), 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
@@ -73,7 +72,8 @@
.show(mFGSurfaceControl);
t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
- .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
+ .setPosition(mSyncSurfaceControl, resolution.getWidth() - 2,
+ resolution.getHeight() - 2)
.show(mSyncSurfaceControl);
});
}
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index e525e2a..c9fdc3b 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -18,6 +18,8 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <ui/DisplayState.h>
+
#include "LayerTransactionTest.h"
namespace android {
@@ -34,12 +36,14 @@
ASSERT_EQ(NO_ERROR, mClient->initCheck());
mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
- SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+ SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
+ SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
consumer->setConsumerName(String8("Virtual disp consumer"));
- consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+ consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
+ mMainDisplayConfig.resolution.getHeight());
}
virtual void TearDown() {
@@ -48,14 +52,14 @@
mColorLayer = 0;
}
- void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+ void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
mVirtualDisplay =
SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
asTransaction([&](Transaction& t) {
t.setDisplaySurface(mVirtualDisplay, mProducer);
t.setDisplayLayerStack(mVirtualDisplay, layerStack);
- t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
- Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+ t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
+ Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
});
}
@@ -76,7 +80,8 @@
});
}
- DisplayInfo mMainDisplayInfo;
+ ui::DisplayState mMainDisplayState;
+ DisplayConfig mMainDisplayConfig;
sp<IBinder> mMainDisplay;
sp<IBinder> mVirtualDisplay;
sp<IGraphicBufferProducer> mProducer;
@@ -85,7 +90,7 @@
};
TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+ createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
createColorLayer(1 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -108,7 +113,7 @@
// Assumption here is that the new mirrored display has the same viewport as the
// primary display that it is mirroring.
- createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+ createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
createColorLayer(0 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 4a2ab7c..0e7eba8 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -20,18 +20,13 @@
#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
-
#include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-
#include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <fstream>
#include <random>
@@ -271,21 +266,21 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ const ui::Size& resolution = config.resolution;
// Background surface
- mBGSurfaceControl = mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), displayWidth,
- displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ mBGSurfaceControl =
+ mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
+ resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
// Foreground surface
- mFGSurfaceControl = mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), displayWidth,
- displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ mFGSurfaceControl =
+ mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
+ resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 5612bb2..040852f 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -16,41 +16,10 @@
#ifndef ANDROID_TRANSACTION_TEST_HARNESSES
#define ANDROID_TRANSACTION_TEST_HARNESSES
-/*#include <algorithm>
-#include <chrono>
-#include <cinttypes>
-#include <functional>
-#include <limits>
-#include <ostream>
+#include <ui/DisplayState.h>
-#include <android/native_window.h>
-
-#include <binder/ProcessState.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <hardware/hwcomposer_defs.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <math.h>
-#include <math/vec3.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "BufferGenerator.h"
-*/
#include "LayerTransactionTest.h"
-/*#include "utils/CallbackUtils.h"
-#include "utils/ColorUtils.h"
-#include "utils/ScreenshotUtils.h"
-#include "utils/TransactionUtils.h"
-*/
+
namespace android {
using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -66,9 +35,14 @@
return mDelegate->screenshot();
case RenderPath::VIRTUAL_DISPLAY:
- const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
- DisplayInfo mainDisplayInfo;
- SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+ const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+ ui::DisplayState displayState;
+ SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+
+ DisplayConfig displayConfig;
+ SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+ const ui::Size& resolution = displayConfig.resolution;
sp<IBinder> vDisplay;
sp<IGraphicBufferProducer> producer;
@@ -77,7 +51,7 @@
BufferQueue::createBufferQueue(&producer, &consumer);
consumer->setConsumerName(String8("Virtual disp consumer"));
- consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+ consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
itemConsumer = new BufferItemConsumer(consumer,
// Sample usage bits from screenrecord
@@ -90,9 +64,8 @@
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(vDisplay, producer);
t.setDisplayLayerStack(vDisplay, 0);
- t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
- Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
- Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+ t.setDisplayProjection(vDisplay, displayState.orientation,
+ Rect(displayState.viewport), Rect(resolution));
t.apply();
SurfaceComposerClient::Transaction().apply(true);
BufferItem item;
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 6874f6f..e751496 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -41,7 +41,7 @@
#include <hwbinder/ProcessState.h>
#include <log/log.h>
#include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
#include <utils/Looper.h>
#include <gmock/gmock.h>
@@ -338,15 +338,16 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
EXPECT_FALSE(display == nullptr);
- DisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(200u, info.w);
- EXPECT_EQ(400u, info.h);
- EXPECT_EQ(1e9f / 16'666'666, info.fps);
+ DisplayConfig config;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ const ui::Size& resolution = config.resolution;
+ EXPECT_EQ(ui::Size(200, 400), resolution);
+ EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -369,8 +370,8 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
EXPECT_TRUE(display == nullptr);
- DisplayInfo info;
- EXPECT_NE(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ DisplayConfig config;
+ EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
}
}
@@ -398,17 +399,18 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
EXPECT_FALSE(display == nullptr);
- DisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(200u, info.w);
- EXPECT_EQ(400u, info.h);
- EXPECT_EQ(1e9f / 16'666'666, info.fps);
+ DisplayConfig config;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(200, 400), config.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -421,7 +423,7 @@
}
}
- Vector<DisplayInfo> configs;
+ Vector<DisplayConfig> configs;
EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
EXPECT_EQ(configs.size(), 2);
@@ -436,27 +438,29 @@
}
for (int i = 0; i < configs.size(); i++) {
- if (configs[i].w == 800u) {
+ const auto& config = configs[i];
+ if (config.resolution.getWidth() == 800) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- configs[i].fps,
- configs[i].fps));
+ config.refreshRate,
+ config.refreshRate));
waitForDisplayTransaction();
EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(800u, info.w);
- EXPECT_EQ(1600u, info.h);
- EXPECT_EQ(1e9f / 11'111'111, info.fps);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -500,17 +504,18 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
EXPECT_FALSE(display == nullptr);
- DisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(800u, info.w);
- EXPECT_EQ(1600u, info.h);
- EXPECT_EQ(1e9f / 16'666'666, info.fps);
+ DisplayConfig config;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -523,7 +528,7 @@
}
}
- Vector<DisplayInfo> configs;
+ Vector<DisplayConfig> configs;
EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
EXPECT_EQ(configs.size(), 2);
@@ -537,27 +542,29 @@
}
for (int i = 0; i < configs.size(); i++) {
- if (configs[i].fps == 1e9f / 11'111'111) {
+ const auto& config = configs[i];
+ if (config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- configs[i].fps,
- configs[i].fps));
+ config.refreshRate,
+ config.refreshRate));
waitForDisplayTransaction();
EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(800u, info.w);
- EXPECT_EQ(1600u, info.h);
- EXPECT_EQ(1e9f / 11'111'111, info.fps);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -611,17 +618,18 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
EXPECT_FALSE(display == nullptr);
- DisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(800u, info.w);
- EXPECT_EQ(1600u, info.h);
- EXPECT_EQ(1e9f / 16'666'666, info.fps);
+ DisplayConfig config;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -634,7 +642,7 @@
}
}
- Vector<DisplayInfo> configs;
+ Vector<DisplayConfig> configs;
EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
EXPECT_EQ(configs.size(), 4);
@@ -648,27 +656,29 @@
}
for (int i = 0; i < configs.size(); i++) {
- if (configs[i].w == 800u && configs[i].fps == 1e9f / 11'111'111) {
+ const auto& config = configs[i];
+ if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- configs[i].fps,
- configs[i].fps));
+ configs[i].refreshRate,
+ configs[i].refreshRate));
waitForDisplayTransaction();
EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(800u, info.w);
- EXPECT_EQ(1600u, info.h);
- EXPECT_EQ(1e9f / 11'111'111, info.fps);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -691,27 +701,29 @@
}
for (int i = 0; i < configs.size(); i++) {
- if (configs[i].fps == 1e9f / 8'333'333) {
+ const auto& config = configs[i];
+ if (config.refreshRate == 1e9f / 8'333'333) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- configs[i].fps,
- configs[i].fps));
+ config.refreshRate,
+ config.refreshRate));
waitForDisplayTransaction();
EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(1600u, info.w);
- EXPECT_EQ(3200u, info.h);
- EXPECT_EQ(1e9f / 8'333'333, info.fps);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+ EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -734,27 +746,29 @@
}
for (int i = 0; i < configs.size(); i++) {
- if (configs[i].w == 1600 && configs[i].fps == 1e9f / 11'111'111) {
+ const auto& config = configs[i];
+ if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
- configs[i].fps,
- configs[i].fps));
+ config.refreshRate,
+ config.refreshRate));
waitForDisplayTransaction();
EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
break;
}
}
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- EXPECT_EQ(1600u, info.w);
- EXPECT_EQ(3200u, info.h);
- EXPECT_EQ(1e9f / 11'111'111, info.fps);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+ EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
mFakeComposerClient->clearFrames();
{
+ const ui::Size& resolution = config.resolution;
auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
- info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ mComposerClient->createSurface(String8("Display Test Surface Foo"),
+ resolution.getWidth(), resolution.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 0);
EXPECT_TRUE(surfaceControl != nullptr);
EXPECT_TRUE(surfaceControl->isValid());
fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -787,8 +801,8 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
EXPECT_TRUE(display == nullptr);
- DisplayInfo info;
- auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ DisplayConfig config;
+ auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
EXPECT_NE(NO_ERROR, result);
}
@@ -813,12 +827,11 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
EXPECT_FALSE(display == nullptr);
- DisplayInfo info;
- auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ DisplayConfig config;
+ auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
EXPECT_EQ(NO_ERROR, result);
- ASSERT_EQ(400u, info.w);
- ASSERT_EQ(200u, info.h);
- EXPECT_EQ(1e9f / 16'666'666, info.fps);
+ ASSERT_EQ(ui::Size(400, 200), config.resolution);
+ EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
}
}
@@ -968,11 +981,12 @@
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
ASSERT_FALSE(display == nullptr);
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
+ const ui::Size& resolution = config.resolution;
+ mDisplayWidth = resolution.getWidth();
+ mDisplayHeight = resolution.getHeight();
// Background surface
mBGSurfaceControl =
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 98cc023..888e009 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -713,7 +713,9 @@
struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
-struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {
+ static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+};
struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
using Base = BaseLayerProperties<SidebandLayerProperties>;
@@ -840,8 +842,8 @@
EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
.WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
- auto outputLayer = test->mDisplay->getCompositionDisplay()
- ->injectOutputLayerForTest(layer->getCompositionLayer(), layer);
+ auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest(
+ layer->getCompositionEngineLayerFE());
outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9680a17..dddad92 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1357,7 +1357,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1369,7 +1370,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
// For 90, the frame and viewport have the hardware display size width and height swapped
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
@@ -1382,7 +1384,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1394,7 +1397,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
// For 270, the frame and viewport have the hardware display size width and height swapped
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 9ca1b70..18e9941 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -63,8 +63,7 @@
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
- RefreshRateConfigs mConfigs{true,
- {
+ RefreshRateConfigs mConfigs{{
RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
HwcConfigGroupType(0),
LO_FPS_PERIOD},
@@ -85,7 +84,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -114,7 +113,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -138,15 +137,15 @@
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = mTime;
EXPECT_EQ(3, layerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 11ace05..959c256 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -71,8 +71,7 @@
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
- RefreshRateConfigs mConfigs{true,
- {
+ RefreshRateConfigs mConfigs{{
RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
HwcConfigGroupType(0),
LO_FPS_PERIOD},
@@ -84,7 +83,6 @@
TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
TestableSurfaceFlinger mFlinger;
- const nsecs_t mTime = systemTime();
};
namespace {
@@ -92,28 +90,30 @@
TEST_F(LayerHistoryTestV2, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
+ const nsecs_t time = systemTime();
+
// No layers returned if no layers are active.
- EXPECT_TRUE(history().summarize(mTime).empty());
+ EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
// Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, mTime);
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ history().record(layer.get(), 0, time);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
// Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, mTime);
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ history().record(layer.get(), 0, time);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
}
@@ -121,33 +121,36 @@
TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, mTime);
- auto summary = history().summarize(mTime);
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ nsecs_t time = systemTime();
+
+ history().record(layer.get(), 0, time);
+ auto summary = history().summarize(time);
+ ASSERT_EQ(1, history().summarize(time).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- summary = history().summarize(mTime);
- EXPECT_TRUE(history().summarize(mTime).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
}
TEST_F(LayerHistoryTestV2, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time);
time += LO_FPS_PERIOD;
@@ -163,14 +166,14 @@
TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time);
time += HI_FPS_PERIOD;
@@ -190,14 +193,14 @@
TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time);
time += HI_FPS_PERIOD;
@@ -218,14 +221,14 @@
TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time);
time += LO_FPS_PERIOD;
@@ -246,19 +249,53 @@
TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(73.4f));
+ EXPECT_CALL(*layer, getFrameRate())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time);
time += HI_FPS_PERIOD;
}
ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Explicit, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -278,15 +315,15 @@
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
EXPECT_EQ(3, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -314,7 +351,7 @@
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 78009b8..841c624 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -74,26 +74,14 @@
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
-}
-
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingNotSupported) {
- std::vector<RefreshRateConfigs::InputConfig> configs{
- {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
}
TEST_F(RefreshRateConfigsTest, invalidPolicy) {
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
}
@@ -103,10 +91,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
const auto minRate = refreshRateConfigs->getMinRefreshRate();
const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
@@ -128,10 +113,8 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -145,7 +128,6 @@
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -161,9 +143,8 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -174,7 +155,6 @@
ASSERT_EQ(expectedPerformanceConfig, performanceRate);
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -187,8 +167,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
{
auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
@@ -212,10 +191,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -276,10 +252,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -387,10 +360,7 @@
{HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
@@ -430,10 +400,7 @@
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
{HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -465,15 +432,83 @@
EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
}
+TEST_F(RefreshRateConfigsTest,
+ twoDeviceConfigs_getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+ {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+ RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60) {
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
@@ -495,7 +530,7 @@
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr.desiredRefreshRate = 45.0f;
- EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
@@ -511,10 +546,7 @@
{HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -553,10 +585,7 @@
{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -577,7 +606,7 @@
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr1.vote = LayerVoteType::Min;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
@@ -587,7 +616,7 @@
EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr1.vote = LayerVoteType::Max;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
@@ -599,7 +628,7 @@
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30.0f;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
}
@@ -609,10 +638,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -621,7 +647,7 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& lr = layers[0];
- lr.vote = LayerVoteType::Explicit;
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
@@ -635,10 +661,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -650,13 +673,13 @@
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 60.0f;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90.0f;
EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90.0f;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
}
@@ -666,10 +689,7 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -681,13 +701,13 @@
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 60.0f;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90.0f;
EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90.0f;
- lr2.vote = LayerVoteType::Explicit;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
}
@@ -702,6 +722,29 @@
ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0f, 59.998f));
}
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_75HzContent) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = fps;
+ const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+ printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+ EXPECT_EQ(expected90Config, refreshRate);
+ }
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 8e07c79..18d6bd2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -47,8 +47,8 @@
~RefreshRateStatsTest();
void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
- mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
- /*refreshRateSwitching=*/true, configs, /*currentConfig=*/CONFIG_ID_0);
+ mRefreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
mRefreshRateStats =
std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
/*currentConfigId=*/CONFIG_ID_0,
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 82a00ee..89002a8 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -73,8 +73,7 @@
std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
{{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
- /*currentConfig=*/HwcConfigIndexType(0));
+ scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2491533..798ba76 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -17,7 +17,6 @@
#pragma once
#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
@@ -203,8 +202,7 @@
std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
{{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
mFlinger->mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
- /*currentConfig=*/HwcConfigIndexType(0));
+ scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mFlinger->mRefreshRateStats = std::make_unique<
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
@@ -252,7 +250,7 @@
void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
layer->mDrawingState.sidebandStream = sidebandStream;
layer->mSidebandStream = sidebandStream;
- layer->getCompositionLayer()->editFEState().sidebandStream = sidebandStream;
+ layer->editCompositionState()->sidebandStream = sidebandStream;
}
void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) {
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 68e6697..fb2e3bd 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -37,6 +37,7 @@
using namespace android::surfaceflinger;
using namespace google::protobuf;
+using namespace std::chrono_literals;
namespace android {
namespace {
@@ -148,30 +149,31 @@
FakeStatsEventDelegate() = default;
~FakeStatsEventDelegate() override = default;
- struct stats_event* addStatsEventToPullData(pulled_stats_event_list*) override {
+ struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
return mEvent;
}
- void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
- pull_atom_metadata*, void* cookie) override {
+ void registerStatsPullAtomCallback(int32_t atom_tag,
+ AStatsManager_PullAtomCallback callback,
+ AStatsManager_PullAtomMetadata*, void* cookie) override {
mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
}
- status_pull_atom_return_t makePullAtomCallback(int32_t atom_tag, void* cookie) {
+ AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atom_tag, void* cookie) {
return (*mCallback)(atom_tag, nullptr, cookie);
}
MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
- MOCK_METHOD2(statsEventSetAtomId, void(struct stats_event*, uint32_t));
- MOCK_METHOD2(statsEventWriteInt64, void(struct stats_event*, int64_t));
- MOCK_METHOD2(statsEventWriteString8, void(struct stats_event*, const char*));
- MOCK_METHOD3(statsEventWriteByteArray, void(struct stats_event*, const uint8_t*, size_t));
- MOCK_METHOD1(statsEventBuild, void(struct stats_event*));
+ MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
+ MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
+ MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
+ MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
+ MOCK_METHOD1(statsEventBuild, void(AStatsEvent*));
- struct stats_event* mEvent = stats_event_obtain();
+ AStatsEvent* mEvent = AStatsEvent_obtain();
std::vector<int32_t> mAtomTags;
- stats_pull_atom_callback_t mCallback = nullptr;
+ AStatsManager_PullAtomCallback mCallback = nullptr;
void* mCookie = nullptr;
};
FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
@@ -258,22 +260,25 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, enabledAfterBoot) {
+TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
mTimeStats->onBootFinished();
- ASSERT_TRUE(mTimeStats->isEnabled());
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ mTimeStats.reset();
}
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
@@ -306,6 +311,41 @@
EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
}
+TEST_F(TimeStatsTest, canIncreaseLateAcquireFrames) {
+ // this stat is not in the proto so verify by checking the string dump
+ constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+ mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+ }
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ const std::string expectedResult = "lateAcquireFrames = " + std::to_string(LATE_ACQUIRE_FRAMES);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) {
+ // this stat is not in the proto so verify by checking the string dump
+ constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 2;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+ mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+ }
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ const std::string expectedResult =
+ "badDesiredPresentFrames = " + std::to_string(BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -321,6 +361,45 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canAverageFrameDuration) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(16ms)
+ .count());
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms"));
+}
+
+TEST_F(TimeStatsTest, canAverageRenderEngineTimings) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+ .count(),
+ std::make_shared<FenceTime>(
+ std::chrono::duration_cast<
+ std::chrono::nanoseconds>(3ms)
+ .count()));
+
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+ .count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
+ .count());
+
+ // Push a dummy present fence to trigger flushing the RenderEngine timings.
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms"));
+}
+
TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -353,8 +432,6 @@
TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- using namespace std::chrono_literals;
-
mTimeStats->setPowerMode(HWC_POWER_MODE_OFF);
mTimeStats
->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
@@ -378,8 +455,6 @@
TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- using namespace std::chrono_literals;
-
mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
.count(),
std::make_shared<FenceTime>(
@@ -635,7 +710,6 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
- using namespace std::chrono_literals;
mTimeStats
->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
@@ -665,14 +739,27 @@
EXPECT_EQ(0, globalProto.stats_size());
}
-TEST_F(TimeStatsTest, canClearClientCompositionSkippedFrames) {
- // this stat is not in the proto so verify by checking the string dump
+TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) {
+ // These stats are not in the proto so verify by checking the string dump.
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+ .count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+ .count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
EXPECT_THAT(result, HasSubstr("clientCompositionReusedFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
+ EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -749,7 +836,7 @@
EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
mDelegate->mCookie));
@@ -805,12 +892,20 @@
return expected == actual;
}
-TEST_F(TimeStatsTest, layerStatsCallback_pullsAllHistogramsAndClears) {
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
+ constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+ constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
mTimeStats->onBootFinished();
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+ mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+ }
+ for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+ mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+ }
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
EXPECT_THAT(mDelegate->mAtomTags,
@@ -868,9 +963,12 @@
BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
expectedPostToAcquire.size()),
expectedPostToAcquire.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
mDelegate->mCookie));
@@ -904,7 +1002,7 @@
statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
EXPECT_CALL(*mDelegate,
statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
mDelegate->mCookie));
}
@@ -945,7 +1043,7 @@
EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
.Times(AnyNumber());
}
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
mDelegate->mCookie));
}
@@ -985,7 +1083,7 @@
EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
.Times(AnyNumber());
}
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
mDelegate->mCookie));
}
@@ -1017,7 +1115,7 @@
.Times(1);
EXPECT_CALL(*mDelegate,
statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
- EXPECT_EQ(STATS_PULL_SUCCESS,
+ EXPECT_EQ(AStatsManager_PULL_SUCCESS,
mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
mDelegate->mCookie));
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index acf852d..caac61d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -37,7 +37,7 @@
public:
FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
auto const floor = timePoint % mPeriod;
@@ -60,7 +60,7 @@
public:
VRRStubTracker(nsecs_t period) : mPeriod{period} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
std::lock_guard<decltype(mMutex)> lk(mMutex);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 70c9225..3ab38e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,9 +39,10 @@
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+ ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
}
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ce1fafe..2f36bb2 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -35,7 +35,8 @@
class MockVSyncTracker : public VSyncTracker {
public:
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
@@ -46,7 +47,9 @@
public:
VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
- void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+ bool addVsyncTimestamp(nsecs_t timestamp) final {
+ return mTracker->addVsyncTimestamp(timestamp);
+ }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
@@ -239,6 +242,22 @@
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+ mReactor.setIgnorePresentFences(true);
+
+ nsecs_t const newPeriod = 5000;
+ mReactor.setPeriod(newPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
nsecs_t const fakeTimestamp = 4839;
EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
@@ -283,16 +302,16 @@
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(10000, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(20000, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(25000, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -301,14 +320,14 @@
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
mReactor.setPeriod(period);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -319,17 +338,46 @@
mReactor.setPeriod(secondPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
mReactor.setPeriod(thirdPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+ nsecs_t skewyPeriod = period >> 1;
+ bool periodFlushed = false;
+ nsecs_t sampleTime = 0;
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .Times(2)
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ auto fence = generatePendingFence();
+ EXPECT_FALSE(mReactor.addPresentFence(fence));
+ signalFenceWithTime(fence, period >> 1);
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
@@ -342,12 +390,12 @@
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(5000, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(10000, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -356,7 +404,7 @@
bool periodFlushed = false;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
- EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -370,17 +418,17 @@
auto constexpr numTimestampSubmissions = 10;
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += period;
- EXPECT_TRUE(mReactor.addResyncSample(time, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
}
@@ -392,11 +440,11 @@
mReactor.setPeriod(newPeriod);
time += period;
- mReactor.addResyncSample(time, &periodFlushed);
+ mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
time += newPeriod;
- mReactor.addResyncSample(time, &periodFlushed);
+ mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
@@ -520,8 +568,8 @@
bool periodFlushed = false;
mReactor.setPeriod(anotherPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
}
@@ -566,6 +614,24 @@
mReactor.beginResync();
}
+TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+ mReactor.setIgnorePresentFences(true);
+
+ nsecs_t const newPeriod = 5000;
+ mReactor.setPeriod(newPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 9ca116d..a2ae6c9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(reset, void());
MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
MOCK_METHOD0(beginResync, void());
- MOCK_METHOD2(addResyncSample, bool(nsecs_t, bool*));
+ MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD0(endResync, void());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(getPeriod, nsecs_t());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 494e73d..e2f6abd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
- MOCK_CONST_METHOD0(getFrameRate, std::optional<float>());
+ MOCK_CONST_METHOD0(getFrameRate, FrameRate());
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index d1df08c..2720537 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -40,6 +40,8 @@
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
+ MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
index 4e2b7c3..1318deb 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -121,8 +121,10 @@
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
- const auto& [surfaceControl, acquireTime, previousReleaseFence, transformHint] =
- surfaceControlStats;
+ const auto&
+ [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
+ transformHint,
+ frameEvents] = surfaceControlStats;
ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
<< "bad acquire time";
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 90a73e2..a7ec4ae 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -164,6 +164,11 @@
"ro.board.platform",
}};
+// LoadDriver returns:
+// * 0 when succeed, or
+// * -ENOENT when fail to open binary libraries, or
+// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
+// HWVULKAN_HARDWARE_MODULE_ID in the library.
int LoadDriver(android_namespace_t* library_namespace,
const hwvulkan_module_t** module) {
ATRACE_CALL();
@@ -221,7 +226,13 @@
return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN_UPDATED);
- return LoadDriver(ns, module);
+ int result = LoadDriver(ns, module);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL(
+ "couldn't find an updated Vulkan implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
+ }
+ return result;
}
bool Hal::Open() {