Merge "Export libui_headers as vndk header only library" into oc-mr1-dev
am: 0d70e447b5
Change-Id: I288dd3619888ab63b17d7a0c13d6342983cf4952
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 9cdc9e9..a48dab9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -481,8 +481,8 @@
newClock = "global";
}
- size_t begin = clockStr.find("[") + 1;
- size_t end = clockStr.find("]");
+ size_t begin = clockStr.find('[') + 1;
+ size_t end = clockStr.find(']');
if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) {
return true;
}
@@ -543,7 +543,7 @@
auto listRet = sm->list([&](const auto &interfaces) {
for (size_t i = 0; i < interfaces.size(); i++) {
string fqInstanceName = interfaces[i];
- string::size_type n = fqInstanceName.find("/");
+ string::size_type n = fqInstanceName.find('/');
if (n == std::string::npos || interfaces[i].size() == n+1)
continue;
hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5f93e99..b3d628c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -697,7 +697,7 @@
std::string valid_name = entry_name;
// Rename extension if necessary.
- size_t idx = entry_name.rfind(".");
+ size_t idx = entry_name.rfind('.');
if (idx != std::string::npos) {
std::string extension = entry_name.substr(idx);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
@@ -1438,7 +1438,7 @@
return true;
}
-static std::string SHA256_file_hash(std::string filepath) {
+static std::string SHA256_file_hash(const std::string& filepath) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
| O_CLOEXEC | O_NOFOLLOW)));
if (fd == -1) {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index fa6f6df..3227749 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -177,7 +177,7 @@
}
for (size_t i = 0; i < N; i++) {
- String16 service_name = std::move(services[i]);
+ const String16& service_name = std::move(services[i]);
if (IsSkipped(skippedServices, service_name)) continue;
sp<IBinder> service = sm_->checkService(service_name);
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 66beb6d..5ca2b57 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -95,7 +95,7 @@
}
int i = 0;
std::ostringstream actual_stream, expected_stream;
- for (String16 actual : arg) {
+ for (const String16& actual : arg) {
std::string actual_str = String8(actual).c_str();
std::string expected_str = expected[i];
actual_stream << "'" << actual_str << "' ";
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 3eb39b9..ea0cd9e 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -60,7 +60,7 @@
ATRACE_BEGIN("loadStats tree");
cacheUsed = 0;
- for (auto path : mDataPaths) {
+ for (const auto& path : mDataPaths) {
auto cachePath = read_path_inode(path, "cache", kXattrInodeCache);
auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
calculate_tree_size(cachePath, &cacheUsed);
@@ -170,7 +170,7 @@
items.clear();
ATRACE_BEGIN("loadItems");
- for (auto path : mDataPaths) {
+ for (const auto& path : mDataPaths) {
loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache));
loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache));
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8a79ee1..af7455a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1349,7 +1349,7 @@
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- for (auto packageName : packageNames) {
+ for (const auto& packageName : packageNames) {
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
}
// NOTE: Locking is relaxed on this method, since it's limited to
@@ -1388,7 +1388,7 @@
}
ATRACE_BEGIN("obb");
- for (auto packageName : packageNames) {
+ for (const auto& packageName : packageNames) {
auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
calculate_tree_size(obbCodePath, &extStats.codeSize);
}
@@ -1396,7 +1396,7 @@
if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
ATRACE_BEGIN("code");
- for (auto codePath : codePaths) {
+ for (const auto& codePath : codePaths) {
calculate_tree_size(codePath, &stats.codeSize, -1,
multiuser_get_shared_gid(0, appId));
}
@@ -1407,7 +1407,7 @@
ATRACE_END();
} else {
ATRACE_BEGIN("code");
- for (auto codePath : codePaths) {
+ for (const auto& codePath : codePaths) {
calculate_tree_size(codePath, &stats.codeSize);
}
ATRACE_END();
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 9ab56dd..8c75ee5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -67,6 +68,10 @@
return unique_fd(-1);
}
+static bool is_debug_runtime() {
+ return android::base::GetProperty("persist.sys.dalvik.vm.lib.2", "") == "libartd.so";
+}
+
static bool clear_profile(const std::string& profile) {
unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (ufd.get() < 0) {
@@ -267,7 +272,8 @@
dex2oat_large_app_threshold);
}
- static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+ // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
+ const char* dex2oat_bin = is_debug_runtime() ? "/system/bin/dex2oatd" : "/system/bin/dex2oat";
static const char* RUNTIME_ARG = "--runtime-arg";
@@ -376,7 +382,7 @@
}
- ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);
+ ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
const char* argv[9 // program name, mandatory arguments and the final NULL
+ (have_dex2oat_isa_variant ? 1 : 0)
@@ -397,7 +403,7 @@
+ (has_base_dir ? 1 : 0)
+ (have_dex2oat_large_app_threshold ? 1 : 0)];
int i = 0;
- argv[i++] = DEX2OAT_BIN;
+ argv[i++] = dex2oat_bin;
argv[i++] = zip_fd_arg;
argv[i++] = zip_location_arg;
argv[i++] = input_vdex_fd_arg;
@@ -463,8 +469,8 @@
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
- execv(DEX2OAT_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+ execv(dex2oat_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", dex2oat_bin, strerror(errno));
}
/*
@@ -643,7 +649,7 @@
static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
const unique_fd& reference_profile_fd) {
static const size_t MAX_INT_LEN = 32;
- static const char* PROFMAN_BIN = "/system/bin/profman";
+ const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
std::vector<std::string> profile_args(profiles_fd.size());
char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
@@ -657,7 +663,7 @@
// program name, reference profile fd, the final NULL and the profile fds
const char* argv[3 + profiles_fd.size()];
int i = 0;
- argv[i++] = PROFMAN_BIN;
+ argv[i++] = profman_bin;
argv[i++] = reference_profile_arg;
for (size_t k = 0; k < profile_args.size(); k++) {
argv[i++] = profile_args[k].c_str();
@@ -665,8 +671,8 @@
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
- execv(PROFMAN_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+ execv(profman_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", profman_bin, strerror(errno));
exit(68); /* only get here on exec failure */
}
@@ -1371,7 +1377,10 @@
// the profile has changed.
static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set,
const std::string& compiler_filter, bool profile_was_updated, bool downgrade) {
- static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+ const char* dexoptanalyzer_bin =
+ is_debug_runtime()
+ ? "/system/bin/dexoptanalyzerd"
+ : "/system/bin/dexoptanalyzer";
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) {
@@ -1392,7 +1401,7 @@
(downgrade ? 1 : 0);
const char* argv[argc];
int i = 0;
- argv[i++] = DEXOPTANALYZER_BIN;
+ argv[i++] = dexoptanalyzer_bin;
argv[i++] = dex_file_arg.c_str();
argv[i++] = isa_arg.c_str();
argv[i++] = compiler_filter_arg.c_str();
@@ -1404,8 +1413,8 @@
}
argv[i] = NULL;
- execv(DEXOPTANALYZER_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+ execv(dexoptanalyzer_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno));
}
// Prepares the oat dir for the secondary dex files.
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 67b5b46..2eed1ce 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -28,6 +28,7 @@
"Lshal.cpp",
"ListCommand.cpp",
"PipeRelay.cpp",
+ "TextTable.cpp",
"utils.cpp",
],
}
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 7c6cfd9..31c42e7 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -35,6 +35,7 @@
#include "Lshal.h"
#include "PipeRelay.h"
+#include "TextTable.h"
#include "Timeout.h"
#include "utils.h"
@@ -206,42 +207,46 @@
}
}
-void ListCommand::printLine(
- const std::string &interfaceName,
- const std::string &transport,
- const std::string &arch,
- const std::string &threadUsage,
- const std::string &server,
- const std::string &serverCmdline,
- const std::string &address,
- const std::string &clients,
- const std::string &clientCmdlines) const {
- if (mSelectedColumns & ENABLE_INTERFACE_NAME)
- mOut << std::setw(80) << interfaceName << "\t";
- if (mSelectedColumns & ENABLE_TRANSPORT)
- mOut << std::setw(10) << transport << "\t";
- if (mSelectedColumns & ENABLE_ARCH)
- mOut << std::setw(5) << arch << "\t";
- if (mSelectedColumns & ENABLE_THREADS) {
- mOut << std::setw(8) << threadUsage << "\t";
- }
- if (mSelectedColumns & ENABLE_SERVER_PID) {
- if (mEnableCmdlines) {
- mOut << std::setw(15) << serverCmdline << "\t";
- } else {
- mOut << std::setw(5) << server << "\t";
+void ListCommand::addLine(TextTable *textTable, const std::string &interfaceName,
+ const std::string &transport, const std::string &arch,
+ const std::string &threadUsage, const std::string &server,
+ const std::string &serverCmdline, const std::string &address,
+ const std::string &clients, const std::string &clientCmdlines) const {
+ std::vector<std::string> columns;
+ for (TableColumnType type : mSelectedColumns) {
+ switch (type) {
+ case TableColumnType::INTERFACE_NAME: {
+ columns.push_back(interfaceName);
+ } break;
+ case TableColumnType::TRANSPORT: {
+ columns.push_back(transport);
+ } break;
+ case TableColumnType::ARCH: {
+ columns.push_back(arch);
+ } break;
+ case TableColumnType::THREADS: {
+ columns.push_back(threadUsage);
+ } break;
+ case TableColumnType::SERVER_ADDR: {
+ columns.push_back(address);
+ } break;
+ case TableColumnType::SERVER_PID: {
+ if (mEnableCmdlines) {
+ columns.push_back(serverCmdline);
+ } else {
+ columns.push_back(server);
+ }
+ } break;
+ case TableColumnType::CLIENT_PIDS: {
+ if (mEnableCmdlines) {
+ columns.push_back(clientCmdlines);
+ } else {
+ columns.push_back(clients);
+ }
+ } break;
}
}
- if (mSelectedColumns & ENABLE_SERVER_ADDR)
- mOut << std::setw(16) << address << "\t";
- if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
- if (mEnableCmdlines) {
- mOut << std::setw(0) << clientCmdlines;
- } else {
- mOut << std::setw(0) << clients;
- }
- }
- mOut << std::endl;
+ textTable->add(std::move(columns));
}
static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
@@ -397,7 +402,25 @@
}
}
+void ListCommand::addLine(TextTable *table, const TableEntry &entry) {
+ addLine(table, entry.interfaceName, entry.transport, getArchString(entry.arch),
+ entry.getThreadUsage(),
+ entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
+ entry.serverCmdline,
+ entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
+ join(entry.clientPids, " "), join(entry.clientCmdlines, ";"));
+}
+
void ListCommand::dumpTable() {
+ if (mNeat) {
+ TextTable textTable;
+ forEachTable([this, &textTable](const Table &table) {
+ for (const auto &entry : table) addLine(&textTable, entry);
+ });
+ textTable.dump(mOut.buf());
+ return;
+ }
+
mServicesTable.description =
"All binderized services (registered services through hwservicemanager)";
mPassthroughRefTable.description =
@@ -409,42 +432,34 @@
"the library and successfully fetched the passthrough implementation.";
mImplementationsTable.description =
"All available passthrough implementations (all -impl.so files)";
- forEachTable([this] (const Table &table) {
- if (!mNeat) {
- mOut << table.description << std::endl;
- }
- mOut << std::left;
- if (!mNeat) {
- printLine("Interface", "Transport", "Arch", "Thread Use", "Server",
- "Server CMD", "PTR", "Clients", "Clients CMD");
- }
+
+ forEachTable([this](const Table &table) {
+ TextTable textTable;
+
+ textTable.add(table.description);
+ addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD",
+ "PTR", "Clients", "Clients CMD");
for (const auto &entry : table) {
- printLine(entry.interfaceName,
- entry.transport,
- getArchString(entry.arch),
- entry.getThreadUsage(),
- entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
- entry.serverCmdline,
- entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
- join(entry.clientPids, " "),
- join(entry.clientCmdlines, ";"));
-
+ addLine(&textTable, entry);
// We're only interested in dumping debug info for already
// instantiated services. There's little value in dumping the
// debug info for a service we create on the fly, so we only operate
// on the "mServicesTable".
if (mEmitDebugInfo && &table == &mServicesTable) {
+ std::stringstream out;
auto pair = splitFirst(entry.interfaceName, '/');
- mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
- NullableOStream<std::ostream>(nullptr));
+ mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
+ NullableOStream<std::ostream>(nullptr));
+ textTable.add(out.str());
}
}
- if (!mNeat) {
- mOut << std::endl;
- }
- });
+ // Add empty line after each table
+ textTable.add();
+
+ textTable.dump(mOut.buf());
+ });
}
void ListCommand::dump() {
@@ -713,31 +728,31 @@
mVintf = true;
}
case 'i': {
- mSelectedColumns |= ENABLE_INTERFACE_NAME;
+ mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
break;
}
case 't': {
- mSelectedColumns |= ENABLE_TRANSPORT;
+ mSelectedColumns.push_back(TableColumnType::TRANSPORT);
break;
}
case 'r': {
- mSelectedColumns |= ENABLE_ARCH;
+ mSelectedColumns.push_back(TableColumnType::ARCH);
break;
}
case 'p': {
- mSelectedColumns |= ENABLE_SERVER_PID;
+ mSelectedColumns.push_back(TableColumnType::SERVER_PID);
break;
}
case 'a': {
- mSelectedColumns |= ENABLE_SERVER_ADDR;
+ mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
break;
}
case 'c': {
- mSelectedColumns |= ENABLE_CLIENT_PIDS;
+ mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
break;
}
case 'e': {
- mSelectedColumns |= ENABLE_THREADS;
+ mSelectedColumns.push_back(TableColumnType::THREADS);
break;
}
case 'm': {
@@ -771,10 +786,19 @@
if (optind < arg.argc) {
// see non option
mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+ mLshal.usage(command);
+ return USAGE;
}
- if (mSelectedColumns == 0) {
- mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS;
+ if (mNeat && mEmitDebugInfo) {
+ mErr << "Error: --neat should not be used with --debug." << std::endl;
+ mLshal.usage(command);
+ return USAGE;
+ }
+
+ if (mSelectedColumns.empty()) {
+ mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
+ TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
}
return OK;
}
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index a75db04..176d5b9 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -28,6 +28,7 @@
#include "NullableOStream.h"
#include "TableEntry.h"
+#include "TextTable.h"
#include "utils.h"
namespace android {
@@ -58,16 +59,11 @@
void dumpTable();
void dumpVintf() const;
- void printLine(
- const std::string &interfaceName,
- const std::string &transport,
- const std::string &arch,
- const std::string &threadUsage,
- const std::string &server,
- const std::string &serverCmdline,
- const std::string &address,
- const std::string &clients,
- const std::string &clientCmdlines) const;
+ void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport,
+ const std::string &arch, const std::string &threadUsage, const std::string &server,
+ const std::string &serverCmdline, const std::string &address,
+ const std::string &clients, const std::string &clientCmdlines) const;
+ void addLine(TextTable *table, const TableEntry &entry);
// Return /proc/{pid}/cmdline if it exists, else empty string.
const std::string &getCmdline(pid_t pid);
// Call getCmdline on all pid in pids. If it returns empty string, the process might
@@ -86,7 +82,7 @@
NullableOStream<std::ostream> mOut;
NullableOStream<std::ofstream> mFileOutput = nullptr;
TableEntryCompare mSortColumn = nullptr;
- TableEntrySelect mSelectedColumns = 0;
+ std::vector<TableColumnType> mSelectedColumns;
// If true, cmdlines will be printed instead of pid.
bool mEnableCmdlines = false;
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index e2d5f6d..ac74775 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -63,13 +63,13 @@
"list:\n"
" lshal\n"
" lshal list\n"
- " List all hals with default ordering and columns (`lshal list -ipc`)\n"
+ " List all hals with default ordering and columns (`lshal list -iepc`)\n"
" lshal list [-h|--help]\n"
" -h, --help: Print help message for list (`lshal help list`)\n"
" lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n"
" [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
" [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
- " [--debug|-d[=<output file>]]\n"
+ " [--debug|-d[=<output file>]] [--neat]\n"
" -i, --interface: print the interface name column\n"
" -n, --instance: print the instance name column\n"
" -t, --transport: print the transport mode column\n"
@@ -81,10 +81,12 @@
" -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
" -m, --cmdline: print cmdline instead of PIDs\n"
" -d[=<output file>], --debug[=<output file>]: emit debug info from \n"
- " IBase::debug with empty options\n"
+ " IBase::debug with empty options. Cannot be used with --neat.\n"
" --sort=i, --sort=interface: sort by interface name\n"
" --sort=p, --sort=pid: sort by server pid\n"
- " --init-vintf=<output file>: form a skeleton HAL manifest to specified\n"
+ " --neat: output is machine parsable (no explanatory text)\n"
+ " Cannot be used with --debug.\n"
+ " --init-vintf[=<output file>]: form a skeleton HAL manifest to specified\n"
" file, or stdout if no file specified.\n";
static const std::string debug =
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index e04c3ca..f18f38a 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -84,18 +84,16 @@
using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
-enum : unsigned int {
- ENABLE_INTERFACE_NAME = 1 << 0,
- ENABLE_TRANSPORT = 1 << 1,
- ENABLE_SERVER_PID = 1 << 2,
- ENABLE_SERVER_ADDR = 1 << 3,
- ENABLE_CLIENT_PIDS = 1 << 4,
- ENABLE_ARCH = 1 << 5,
- ENABLE_THREADS = 1 << 6,
+enum class TableColumnType : unsigned int {
+ INTERFACE_NAME,
+ TRANSPORT,
+ SERVER_PID,
+ SERVER_ADDR,
+ CLIENT_PIDS,
+ ARCH,
+ THREADS,
};
-using TableEntrySelect = unsigned int;
-
enum {
NO_PID = -1,
NO_PTR = 0
diff --git a/cmds/lshal/TextTable.cpp b/cmds/lshal/TextTable.cpp
new file mode 100644
index 0000000..a35917c
--- /dev/null
+++ b/cmds/lshal/TextTable.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <iomanip>
+
+#include "TextTable.h"
+
+namespace android {
+namespace lshal {
+
+void TextTable::computeWidth(const std::vector<std::string>& v) {
+ if (mWidths.size() < v.size()) {
+ mWidths.resize(v.size());
+ }
+ for (size_t i = 0; i < v.size(); ++i) {
+ mWidths[i] = std::max(mWidths[i], v[i].length());
+ }
+}
+
+void TextTable::dump(std::ostream& out) const {
+ out << std::left;
+ for (const auto& row : mTable) {
+ if (!row.isRow()) {
+ out << row.line() << std::endl;
+ continue;
+ }
+
+ for (size_t i = 0; i < row.fields().size(); ++i) {
+ if (i != 0) {
+ out << " ";
+ }
+ // last column does not std::setw to avoid printing unnecessary spaces.
+ if (i < row.fields().size() - 1) {
+ out << std::setw(mWidths[i]);
+ }
+ out << row.fields()[i];
+ }
+ out << std::endl;
+ }
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h
new file mode 100644
index 0000000..4636f15
--- /dev/null
+++ b/cmds/lshal/TextTable.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "TableEntry.h"
+
+namespace android {
+namespace lshal {
+
+// An element in TextTable. This is either an actual row (an array of cells
+// in this row), or a string of explanatory text.
+// To see if this is an actual row, test fields().empty().
+class TextTableRow {
+public:
+ // An empty line.
+ TextTableRow() {}
+
+ // A row of cells.
+ TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {}
+
+ // A single comment string.
+ TextTableRow(std::string&& s) : mLine(std::move(s)) {}
+ TextTableRow(const std::string& s) : mLine(s) {}
+
+ // Whether this row is an actual row of cells.
+ bool isRow() const { return !fields().empty(); }
+
+ // Get all cells.
+ const std::vector<std::string>& fields() const { return mFields; }
+
+ // Get the single comment string.
+ const std::string& line() const { return mLine; }
+
+private:
+ std::vector<std::string> mFields;
+ std::string mLine;
+};
+
+// A TextTable is a 2D array of strings.
+class TextTable {
+public:
+
+ // Add a TextTableRow.
+ void add() { mTable.emplace_back(); }
+ void add(std::vector<std::string>&& v) {
+ computeWidth(v);
+ mTable.emplace_back(std::move(v));
+ }
+ void add(const std::string& s) { mTable.emplace_back(s); }
+ void add(std::string&& s) { mTable.emplace_back(std::move(s)); }
+
+ // Prints the table to out, with column widths adjusted appropriately according
+ // to the content.
+ void dump(std::ostream& out) const;
+
+private:
+ void computeWidth(const std::vector<std::string>& v);
+ std::vector<size_t> mWidths;
+ std::vector<TextTableRow> mTable;
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 93a18fc..fade8cf 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -514,7 +514,7 @@
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->type = BINDER_TYPE_BINDER;
+ obj->hdr.type = BINDER_TYPE_BINDER;
obj->binder = (uintptr_t)ptr;
obj->cookie = 0;
}
@@ -532,7 +532,7 @@
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->type = BINDER_TYPE_HANDLE;
+ obj->hdr.type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
@@ -649,7 +649,7 @@
if (!obj)
return 0;
- if (obj->type == BINDER_TYPE_HANDLE)
+ if (obj->hdr.type == BINDER_TYPE_HANDLE)
return obj->handle;
return 0;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 35b63ec..2b5389b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -505,7 +505,7 @@
ALOGV("Setting Transparent Region Hint");
Region re = Region();
- for (auto r : trhc.region()) {
+ for (const auto& r : trhc.region()) {
Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
re.merge(rect);
}
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 7f46087..a88733c 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -388,13 +388,13 @@
#endif
#if __ANDROID_API__ >= __ANDROID_API_O__
-/*
+/**
* Get a reference to the sensor manager. ASensorManager is a singleton
* per package as different packages may have access to different sensors.
*
* Example:
*
- * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
+ * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
*
*/
ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName);
@@ -503,14 +503,12 @@
* {@link ASensor_isDirectChannelTypeSupported}, respectively.
*
* Example:
- * \code{.cpp}
- * ASensorManager *manager = ...;
- * ASensor *sensor = ...;
- * int channelId = ...;
*
- * ASensorManager_configureDirectReport(
- * manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
- * \endcode
+ * ASensorManager *manager = ...;
+ * ASensor *sensor = ...;
+ * int channelId = ...;
+ *
+ * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
*
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
@@ -530,50 +528,86 @@
/*****************************************************************************/
/**
- * Enable the selected sensor with a specified sampling period and max batch report latency.
- * Returns a negative error code on failure.
- * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
+ * Enable the selected sensor with sampling and report parameters
+ *
+ * Enable the selected sensor at a specified sampling period and max batch report latency.
+ * To disable sensor, use {@link ASensorEventQueue_disableSensor}.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ * \param samplingPeriodUs sampling period of sensor in microseconds.
+ * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are
+ * delievered in microseconds. For sensor streaming, set to 0.
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
/**
- * Enable the selected sensor. Returns a negative error code on failure.
+ * Enable the selected sensor at default sampling rate.
+ *
+ * Start event reports of a sensor to specified sensor event queue at a default rate.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ *
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/**
- * Disable the selected sensor. Returns a negative error code on failure.
+ * Disable the selected sensor.
+ *
+ * Stop event reports from the sensor to specified sensor event queue.
+ *
+ * \param queue {@link ASensorEventQueue} to be changed
+ * \param sensor {@link ASensor} to be disabled
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/**
* Sets the delivery rate of events in microseconds for the given sensor.
+ *
+ * This function has to be called after {@link ASensorEventQueue_enableSensor}.
* Note that this is a hint only, generally event will arrive at a higher
* rate. It is an error to set a rate inferior to the value returned by
* ASensor_getMinDelay().
- * Returns a negative error code on failure.
+ *
+ * \param queue {@link ASensorEventQueue} to which sensor event is delivered.
+ * \param sensor {@link ASensor} of which sampling rate to be updated.
+ * \param usec sensor sampling period (1/sampling rate) in microseconds
+ * \return 0 on sucess or a negative error code on failure.
*/
int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);
/**
- * Returns true if there are one or more events available in the
- * sensor queue. Returns 1 if the queue has events; 0 if
- * it does not have events; and a negative value if there is an error.
+ * Determine if a sensor event queue has pending event to be processed.
+ *
+ * \param queue {@link ASensorEventQueue} to be queried
+ * \return 1 if the queue has events; 0 if it does not have events;
+ * or a negative value if there is an error.
*/
int ASensorEventQueue_hasEvents(ASensorEventQueue* queue);
/**
- * Returns the next available events from the queue. Returns a negative
- * value if no events are available or an error has occurred, otherwise
- * the number of events returned.
+ * Retrieve pending events in sensor event queue
+ *
+ * Retrieve next available events from the queue to a specified event array.
+ *
+ * \param queue {@link ASensorEventQueue} to get events from
+ * \param events pointer to an array of {@link ASensorEvents}.
+ * \param count max number of event that can be filled into array event.
+ * \return number of events returned on success; negative error code when
+ * no events are pending or an error has occurred.
*
* Examples:
- * ASensorEvent event;
- * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
*
- * ASensorEvent eventBuffer[8];
- * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
+ * ASensorEvent event;
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
+ *
+ * ASensorEvent eventBuffer[8];
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
*
*/
ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index e832961..0b390c5 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -470,22 +470,33 @@
void IPCThreadState::processPendingDerefs()
{
if (mIn.dataPosition() >= mIn.dataSize()) {
- size_t numPending = mPendingWeakDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- RefBase::weakref_type* refs = mPendingWeakDerefs[i];
+ /*
+ * The decWeak()/decStrong() calls may cause a destructor to run,
+ * which in turn could have initiated an outgoing transaction,
+ * which in turn could cause us to add to the pending refs
+ * vectors; so instead of simply iterating, loop until they're empty.
+ *
+ * We do this in an outer loop, because calling decStrong()
+ * may result in something being added to mPendingWeakDerefs,
+ * which could be delayed until the next incoming command
+ * from the driver if we don't process it now.
+ */
+ while (mPendingWeakDerefs.size() > 0 || mPendingStrongDerefs.size() > 0) {
+ while (mPendingWeakDerefs.size() > 0) {
+ RefBase::weakref_type* refs = mPendingWeakDerefs[0];
+ mPendingWeakDerefs.removeAt(0);
refs->decWeak(mProcess.get());
}
- mPendingWeakDerefs.clear();
- }
- numPending = mPendingStrongDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- BBinder* obj = mPendingStrongDerefs[i];
+ if (mPendingStrongDerefs.size() > 0) {
+ // We don't use while() here because we don't want to re-order
+ // strong and weak decs at all; if this decStrong() causes both a
+ // decWeak() and a decStrong() to be queued, we want to process
+ // the decWeak() first.
+ BBinder* obj = mPendingStrongDerefs[0];
+ mPendingStrongDerefs.removeAt(0);
obj->decStrong(mProcess.get());
}
- mPendingStrongDerefs.clear();
}
}
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e22179b..84ca3c0 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -104,7 +104,7 @@
void acquire_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
- switch (obj.type) {
+ switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
@@ -140,7 +140,7 @@
}
}
- ALOGD("Invalid object type 0x%08x", obj.type);
+ ALOGD("Invalid object type 0x%08x", obj.hdr.type);
}
void acquire_object(const sp<ProcessState>& proc,
@@ -152,7 +152,7 @@
static void release_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
- switch (obj.type) {
+ switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
@@ -191,7 +191,7 @@
}
}
- ALOGE("Invalid object type 0x%08x", obj.type);
+ ALOGE("Invalid object type 0x%08x", obj.hdr.type);
}
void release_object(const sp<ProcessState>& proc,
@@ -227,17 +227,17 @@
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_HANDLE;
+ obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
@@ -261,12 +261,12 @@
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_WEAK_HANDLE;
+ obj.hdr.type = BINDER_TYPE_WEAK_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
- obj.type = BINDER_TYPE_WEAK_BINDER;
+ obj.hdr.type = BINDER_TYPE_WEAK_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get());
}
@@ -281,13 +281,13 @@
// but we can't do that with the different reference counting
// implementation we are using.
ALOGE("Unable to unflatten Binder weak reference!");
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
return finish_flatten_binder(NULL, obj, out);
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
return finish_flatten_binder(NULL, obj, out);
@@ -307,7 +307,7 @@
const flat_binder_object* flat = in.readObject(false);
if (flat) {
- switch (flat->type) {
+ switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
@@ -326,7 +326,7 @@
const flat_binder_object* flat = in.readObject(false);
if (flat) {
- switch (flat->type) {
+ switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
@@ -543,7 +543,7 @@
= reinterpret_cast<flat_binder_object*>(mData + off);
acquire_object(proc, *flat, this, &mOpenAshmemSize);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
// If this is a file descriptor, we need to dup it so the
// new Parcel now owns its own fd, and can declare that we
// officially know we have fds.
@@ -1152,7 +1152,7 @@
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
{
flat_binder_object obj;
- obj.type = BINDER_TYPE_FD;
+ obj.hdr.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = fd;
@@ -1310,7 +1310,7 @@
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// remember if it's a file descriptor
- if (val.type == BINDER_TYPE_FD) {
+ if (val.hdr.type == BINDER_TYPE_FD) {
if (!mAllowFds) {
// fail before modifying our object index
return FDS_NOT_ALLOWED;
@@ -2132,7 +2132,7 @@
{
const flat_binder_object* flat = readObject(true);
- if (flat && flat->type == BINDER_TYPE_FD) {
+ if (flat && flat->hdr.type == BINDER_TYPE_FD) {
return flat->handle;
}
@@ -2325,7 +2325,7 @@
i--;
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
//ALOGI("Closing fd: %ld", flat->handle);
close(flat->handle);
}
@@ -2397,7 +2397,7 @@
const flat_binder_object* flat
= reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
- << TypeCode(flat->type & 0x7f7f7f00)
+ << TypeCode(flat->hdr.type & 0x7f7f7f00)
<< " = " << flat->binder;
}
} else {
@@ -2618,7 +2618,7 @@
for (size_t i=objectsSize; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
@@ -2728,7 +2728,7 @@
for (size_t i=0; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
hasFds = true;
break;
}
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
index fd1dfd5..85cd739 100644
--- a/libs/binder/Value.cpp
+++ b/libs/binder/Value.cpp
@@ -182,10 +182,12 @@
Value& Value::operator=(const Value& rhs)
{
- delete mContent;
- mContent = rhs.mContent
- ? rhs.mContent->clone()
- : NULL;
+ if (this != &rhs) {
+ delete mContent;
+ mContent = rhs.mContent
+ ? rhs.mContent->clone()
+ : NULL;
+ }
return *this;
}
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index ff5912f..b14631d 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -139,6 +139,12 @@
ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version);
}
+TEST_F(BinderDriverInterfaceTest, OpenNoMmap) {
+ int binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ ASSERT_GE(binderFd, 0);
+ close(binderFd);
+}
+
TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT);
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index a04869a..1611e11 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -28,10 +28,19 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <sys/epoll.h>
+
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+static ::testing::AssertionResult IsPageAligned(void *buf) {
+ if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
+ return ::testing::AssertionSuccess();
+ else
+ return ::testing::AssertionFailure() << buf << " is not page aligned";
+}
+
static testing::Environment* binder_env;
static char *binderservername;
static char *binderserversuffix;
@@ -43,7 +52,10 @@
BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
BINDER_LIB_TEST_REGISTER_SERVER,
BINDER_LIB_TEST_ADD_SERVER,
+ BINDER_LIB_TEST_ADD_POLL_SERVER,
BINDER_LIB_TEST_CALL_BACK,
+ BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
+ BINDER_LIB_TEST_DELAYED_CALL_BACK,
BINDER_LIB_TEST_NOP_CALL_BACK,
BINDER_LIB_TEST_GET_SELF_TRANSACTION,
BINDER_LIB_TEST_GET_ID_TRANSACTION,
@@ -60,7 +72,7 @@
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
};
-pid_t start_server_process(int arg2)
+pid_t start_server_process(int arg2, bool usePoll = false)
{
int ret;
pid_t pid;
@@ -68,11 +80,13 @@
int pipefd[2];
char stri[16];
char strpipefd1[16];
+ char usepoll[2];
char *childargv[] = {
binderservername,
binderserverarg,
stri,
strpipefd1,
+ usepoll,
binderserversuffix,
NULL
};
@@ -83,6 +97,7 @@
snprintf(stri, sizeof(stri), "%d", arg2);
snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]);
+ snprintf(usepoll, sizeof(usepoll), "%d", usePoll ? 1 : 0);
pid = fork();
if (pid == -1)
@@ -167,14 +182,14 @@
virtual void TearDown() {
}
protected:
- sp<IBinder> addServer(int32_t *idPtr = NULL)
+ sp<IBinder> addServerEtc(int32_t *idPtr, int code)
{
int ret;
int32_t id;
Parcel data, reply;
sp<IBinder> binder;
- ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply);
+ ret = m_server->transact(code, data, &reply);
EXPECT_EQ(NO_ERROR, ret);
EXPECT_FALSE(binder != NULL);
@@ -186,6 +201,17 @@
*idPtr = id;
return binder;
}
+
+ sp<IBinder> addServer(int32_t *idPtr = NULL)
+ {
+ return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER);
+ }
+
+ sp<IBinder> addPollServer(int32_t *idPtr = NULL)
+ {
+ return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER);
+ }
+
void waitForReadData(int fd, int timeout_ms) {
int ret;
pollfd pfd = pollfd();
@@ -265,17 +291,23 @@
pthread_mutex_unlock(&m_waitMutex);
return ret;
}
+ pthread_t getTriggeringThread()
+ {
+ return m_triggeringThread;
+ }
protected:
void triggerEvent(void) {
pthread_mutex_lock(&m_waitMutex);
pthread_cond_signal(&m_waitCond);
m_eventTriggered = true;
+ m_triggeringThread = pthread_self();
pthread_mutex_unlock(&m_waitMutex);
};
private:
pthread_mutex_t m_waitMutex;
pthread_cond_t m_waitCond;
bool m_eventTriggered;
+ pthread_t m_triggeringThread;
};
class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
@@ -283,6 +315,7 @@
public:
BinderLibTestCallBack()
: m_result(NOT_ENOUGH_DATA)
+ , m_prev_end(NULL)
{
}
status_t getResult(void)
@@ -298,16 +331,43 @@
(void)reply;
(void)flags;
switch(code) {
- case BINDER_LIB_TEST_CALL_BACK:
- m_result = data.readInt32();
+ case BINDER_LIB_TEST_CALL_BACK: {
+ status_t status = data.readInt32(&m_result);
+ if (status != NO_ERROR) {
+ m_result = status;
+ }
triggerEvent();
return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: {
+ sp<IBinder> server;
+ int ret;
+ const uint8_t *buf = data.data();
+ size_t size = data.dataSize();
+ if (m_prev_end) {
+ /* 64-bit kernel needs at most 8 bytes to align buffer end */
+ EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8);
+ } else {
+ EXPECT_TRUE(IsPageAligned((void *)buf));
+ }
+
+ m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t);
+
+ if (size > 0) {
+ server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+ ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION,
+ data, reply);
+ EXPECT_EQ(NO_ERROR, ret);
+ }
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
}
}
status_t m_result;
+ const uint8_t *m_prev_end;
};
class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent
@@ -606,6 +666,65 @@
}
}
+TEST_F(BinderLibTest, DeathNotificationThread)
+{
+ status_t ret;
+ sp<BinderLibTestCallBack> callback;
+ sp<IBinder> target = addServer();
+ ASSERT_TRUE(target != NULL);
+ sp<IBinder> client = addServer();
+ ASSERT_TRUE(client != NULL);
+
+ sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
+
+ ret = target->linkToDeath(testDeathRecipient);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ {
+ Parcel data, reply;
+ ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
+ EXPECT_EQ(0, ret);
+ }
+
+ /* Make sure it's dead */
+ testDeathRecipient->waitEvent(5);
+
+ /* Now, pass the ref to another process and ask that process to
+ * call linkToDeath() on it, and wait for a response. This tests
+ * two things:
+ * 1) You still get death notifications when calling linkToDeath()
+ * on a ref that is already dead when it was passed to you.
+ * 2) That death notifications are not directly pushed to the thread
+ * registering them, but to the threadpool (proc workqueue) instead.
+ *
+ * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION
+ * is blocked on a condition variable waiting for the death notification to be
+ * called; therefore, that thread is not available for handling proc work.
+ * So, if the death notification was pushed to the thread workqueue, the callback
+ * would never be called, and the test would timeout and fail.
+ *
+ * Note that we can't do this part of the test from this thread itself, because
+ * the binder driver would only push death notifications to the thread if
+ * it is a looper thread, which this thread is not.
+ *
+ * See b/23525545 for details.
+ */
+ {
+ Parcel data, reply;
+
+ callback = new BinderLibTestCallBack();
+ data.writeStrongBinder(target);
+ data.writeStrongBinder(callback);
+ ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+ }
+
+ ret = callback->waitEvent(5);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callback->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -681,7 +800,7 @@
const flat_binder_object *fb = reply.readObject(false);
ASSERT_TRUE(fb != NULL);
- EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type);
+ EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type);
EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle));
EXPECT_EQ((binder_uintptr_t)0, fb->cookie);
EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32);
@@ -728,6 +847,61 @@
}
}
+TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
+ status_t ret;
+ Parcel data, reply;
+ sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+ for (int i = 0; i < 2; i++) {
+ BinderLibTestBundle datai;
+ datai.appendFrom(&data, 0, data.dataSize());
+
+ data.freeData();
+ data.writeInt32(1);
+ data.writeStrongBinder(callBack);
+ data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF);
+
+ datai.appendTo(&data);
+ }
+ ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, OnewayQueueing)
+{
+ status_t ret;
+ Parcel data, data2;
+
+ sp<IBinder> pollServer = addPollServer();
+
+ sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+ data.writeStrongBinder(callBack);
+ data.writeInt32(500000); // delay in us before calling back
+
+ sp<BinderLibTestCallBack> callBack2 = new BinderLibTestCallBack();
+ data2.writeStrongBinder(callBack2);
+ data2.writeInt32(0); // delay in us
+
+ ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, NULL, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ // The delay ensures that this second transaction will end up on the async_todo list
+ // (for a single-threaded server)
+ ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, NULL, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ // The server will ensure that the two transactions are handled in the expected order;
+ // If the ordering is not as expected, an error will be returned through the callbacks.
+ ret = callBack->waitEvent(2);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callBack->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+
+ ret = callBack2->waitEvent(2);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callBack2->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -735,6 +909,7 @@
: m_id(id)
, m_nextServerId(id + 1)
, m_serverStartRequested(false)
+ , m_callback(NULL)
{
pthread_mutex_init(&m_serverWaitMutex, NULL);
pthread_cond_init(&m_serverWaitCond, NULL);
@@ -743,6 +918,16 @@
{
exit(EXIT_SUCCESS);
}
+
+ void processPendingCall() {
+ if (m_callback != NULL) {
+ Parcel data;
+ data.writeInt32(NO_ERROR);
+ m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
+ m_callback = NULL;
+ }
+ }
+
virtual status_t onTransact(uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags = 0) {
@@ -774,6 +959,7 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_ADD_POLL_SERVER:
case BINDER_LIB_TEST_ADD_SERVER: {
int ret;
uint8_t buf[1] = { 0 };
@@ -788,9 +974,10 @@
} else {
serverid = m_nextServerId++;
m_serverStartRequested = true;
+ bool usePoll = code == BINDER_LIB_TEST_ADD_POLL_SERVER;
pthread_mutex_unlock(&m_serverWaitMutex);
- ret = start_server_process(serverid);
+ ret = start_server_process(serverid, usePoll);
pthread_mutex_lock(&m_serverWaitMutex);
}
if (ret > 0) {
@@ -818,6 +1005,42 @@
}
case BINDER_LIB_TEST_NOP_TRANSACTION:
return NO_ERROR;
+ case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
+ // Note: this transaction is only designed for use with a
+ // poll() server. See comments around epoll_wait().
+ if (m_callback != NULL) {
+ // A callback was already pending; this means that
+ // we received a second call while still processing
+ // the first one. Fail the test.
+ sp<IBinder> callback = data.readStrongBinder();
+ Parcel data2;
+ data2.writeInt32(UNKNOWN_ERROR);
+
+ callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, NULL, TF_ONE_WAY);
+ } else {
+ m_callback = data.readStrongBinder();
+ int32_t delayUs = data.readInt32();
+ /*
+ * It's necessary that we sleep here, so the next
+ * transaction the caller makes will be queued to
+ * the async queue.
+ */
+ usleep(delayUs);
+
+ /*
+ * Now when we return, libbinder will tell the kernel
+ * we are done with this transaction, and the kernel
+ * can move the queued transaction to either the
+ * thread todo worklist (for kernels without the fix),
+ * or the proc todo worklist. In case of the former,
+ * the next outbound call will pick up the pending
+ * transaction, which leads to undesired reentrant
+ * behavior. This is caught in the if() branch above.
+ */
+ }
+
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_NOP_CALL_BACK: {
Parcel data2, reply2;
sp<IBinder> binder;
@@ -825,7 +1048,7 @@
if (binder == NULL) {
return BAD_VALUE;
}
- reply2.writeInt32(NO_ERROR);
+ data2.writeInt32(NO_ERROR);
binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
}
@@ -967,16 +1190,26 @@
bool m_serverStartRequested;
sp<IBinder> m_serverStarted;
sp<IBinder> m_strongRef;
+ bool m_callbackPending;
+ sp<IBinder> m_callback;
};
-int run_server(int index, int readypipefd)
+int run_server(int index, int readypipefd, bool usePoll)
{
binderLibTestServiceName += String16(binderserversuffix);
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
+ BinderLibTestService* testServicePtr;
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
+ /*
+ * We need this below, but can't hold a sp<> because it prevents the
+ * node from being cleaned up automatically. It's safe in this case
+ * because of how the tests are written.
+ */
+ testServicePtr = testService.get();
+
if (index == 0) {
ret = sm->addService(binderLibTestServiceName, testService);
} else {
@@ -994,8 +1227,53 @@
if (ret)
return 1;
//printf("%s: joinThreadPool\n", __func__);
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+ if (usePoll) {
+ int fd;
+ struct epoll_event ev;
+ int epoll_fd;
+ IPCThreadState::self()->setupPolling(&fd);
+ if (fd < 0) {
+ return 1;
+ }
+ IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ return 1;
+ }
+
+ ev.events = EPOLLIN;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ return 1;
+ }
+
+ while (1) {
+ /*
+ * We simulate a single-threaded process using the binder poll
+ * interface; besides handling binder commands, it can also
+ * issue outgoing transactions, by storing a callback in
+ * m_callback and setting m_callbackPending.
+ *
+ * processPendingCall() will then issue that transaction.
+ */
+ struct epoll_event events[1];
+ int numEvents = epoll_wait(epoll_fd, events, 1, 1000);
+ if (numEvents < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return 1;
+ }
+ if (numEvents > 0) {
+ IPCThreadState::self()->handlePolledCommands();
+ IPCThreadState::self()->flushCommands(); // flush BC_FREE_BUFFER
+ testServicePtr->processPendingCall();
+ }
+ }
+ } else {
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ }
//printf("%s: joinThreadPool returned\n", __func__);
return 1; /* joinThreadPool should not return */
}
@@ -1009,9 +1287,9 @@
binderservername = argv[0];
}
- if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
- binderserversuffix = argv[4];
- return run_server(atoi(argv[2]), atoi(argv[3]));
+ if (argc == 6 && !strcmp(argv[1], binderserverarg)) {
+ binderserversuffix = argv[5];
+ return run_server(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) == 1);
}
binderserversuffix = new char[16];
snprintf(binderserversuffix, 16, "%d", getpid());
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3996305..21debc1 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -96,6 +96,7 @@
"IProducerListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
+ "LayerDebugInfo.cpp",
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 625dc5b..c5cab2d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -764,7 +764,7 @@
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
- Region surfaceDamage = input.getSurfaceDamage();
+ const Region& surfaceDamage = input.getSurfaceDamage();
if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a0d112..8e7f814 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,6 +28,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
+#include <gui/LayerDebugInfo.h>
#include <private/gui/LayerState.h>
@@ -469,6 +470,36 @@
return result;
}
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
+ {
+ if (!outLayers) {
+ return UNEXPECTED_NULL;
+ }
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ outLayers->clear();
+ return reply.readParcelableVector(outLayers);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -763,6 +794,17 @@
}
return injectVSync(when);
}
+ case GET_LAYER_DEBUG_INFO: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<LayerDebugInfo> outLayers;
+ status_t result = getLayerDebugInfo(&outLayers);
+ reply->writeInt32(result);
+ if (result == NO_ERROR)
+ {
+ result = reply->writeParcelableVector(outLayers);
+ }
+ return result;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
new file mode 100644
index 0000000..57ddde0
--- /dev/null
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/LayerDebugInfo.h>
+
+#include <ui/DebugUtils.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/String8.h>
+
+using namespace android;
+
+#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
+
+namespace android {
+
+status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const {
+ RETURN_ON_ERROR(parcel->writeCString(mName.c_str()));
+ RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str()));
+ RETURN_ON_ERROR(parcel->writeCString(mType.c_str()));
+ RETURN_ON_ERROR(parcel->write(mTransparentRegion));
+ RETURN_ON_ERROR(parcel->write(mVisibleRegion));
+ RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion));
+ RETURN_ON_ERROR(parcel->writeUint32(mLayerStack));
+ RETURN_ON_ERROR(parcel->writeFloat(mX));
+ RETURN_ON_ERROR(parcel->writeFloat(mY));
+ RETURN_ON_ERROR(parcel->writeUint32(mZ));
+ RETURN_ON_ERROR(parcel->writeInt32(mWidth));
+ RETURN_ON_ERROR(parcel->writeInt32(mHeight));
+ RETURN_ON_ERROR(parcel->write(mCrop));
+ RETURN_ON_ERROR(parcel->write(mFinalCrop));
+ RETURN_ON_ERROR(parcel->writeFloat(mAlpha));
+ RETURN_ON_ERROR(parcel->writeUint32(mFlags));
+ RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
+ RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
+ for (size_t index = 0; index < 4; index++) {
+ RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2]));
+ }
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat));
+ RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames));
+ RETURN_ON_ERROR(parcel->writeBool(mRefreshPending));
+ RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
+ RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
+ return NO_ERROR;
+}
+
+status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) {
+ mName = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mParentName = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mType = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ RETURN_ON_ERROR(parcel->read(mTransparentRegion));
+ RETURN_ON_ERROR(parcel->read(mVisibleRegion));
+ RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion));
+ RETURN_ON_ERROR(parcel->readUint32(&mLayerStack));
+ RETURN_ON_ERROR(parcel->readFloat(&mX));
+ RETURN_ON_ERROR(parcel->readFloat(&mY));
+ RETURN_ON_ERROR(parcel->readUint32(&mZ));
+ RETURN_ON_ERROR(parcel->readInt32(&mWidth));
+ RETURN_ON_ERROR(parcel->readInt32(&mHeight));
+ RETURN_ON_ERROR(parcel->read(mCrop));
+ RETURN_ON_ERROR(parcel->read(mFinalCrop));
+ RETURN_ON_ERROR(parcel->readFloat(&mAlpha));
+ RETURN_ON_ERROR(parcel->readUint32(&mFlags));
+ RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat));
+ // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways?
+ mDataSpace = static_cast<android_dataspace>(parcel->readUint32());
+ RETURN_ON_ERROR(parcel->errorCheck());
+ for (size_t index = 0; index < 4; index++) {
+ RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2]));
+ }
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat));
+ RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames));
+ RETURN_ON_ERROR(parcel->readBool(&mRefreshPending));
+ RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
+ RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
+ return NO_ERROR;
+}
+
+std::string to_string(const LayerDebugInfo& info) {
+ String8 result;
+
+ result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
+ info.mTransparentRegion.dump(result, "TransparentRegion");
+ info.mVisibleRegion.dump(result, "VisibleRegion");
+ info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
+
+ result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+ info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY),
+ info.mWidth, info.mHeight);
+
+ result.appendFormat("crop=%s, finalCrop=%s, ",
+ to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str());
+ result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
+ result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
+ result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
+ result.appendFormat("alpha=%.3f, flags=0x%08x, ",
+ static_cast<double>(info.mAlpha), info.mFlags);
+ result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]",
+ static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]),
+ static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1]));
+ result.append("\n");
+ result.appendFormat(" parent=%s\n", info.mParentName.c_str());
+ result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],",
+ info.mActiveBufferWidth, info.mActiveBufferHeight,
+ info.mActiveBufferStride,
+ decodePixelFormat(info.mActiveBufferFormat).c_str());
+ result.appendFormat(" queued-frames=%d, mRefreshPending=%d",
+ info.mNumQueuedFrames, info.mRefreshPending);
+ result.append("\n");
+ return std::string(result.c_str());
+}
+
+} // android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f80ba00..b226742 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -39,6 +39,7 @@
struct DisplayState;
struct DisplayInfo;
struct DisplayStatInfo;
+class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
class IGraphicBufferProducer;
@@ -195,6 +196,12 @@
virtual status_t enableVSyncInjections(bool enable) = 0;
virtual status_t injectVSync(nsecs_t when) = 0;
+
+ /* Gets the list of active layers in Z order for debugging purposes
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
};
// ----------------------------------------------------------------------------
@@ -229,6 +236,7 @@
SET_ACTIVE_COLOR_MODE,
ENABLE_VSYNC_INJECTIONS,
INJECT_VSYNC,
+ GET_LAYER_DEBUG_INFO,
CREATE_SCOPED_CONNECTION
};
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
new file mode 100644
index 0000000..8453e04
--- /dev/null
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Region.h>
+
+#include <string>
+
+namespace android {
+
+/* Class for transporting debug info from SurfaceFlinger to authorized
+ * recipients. The class is intended to be a data container. There are
+ * no getters or setters.
+ */
+class LayerDebugInfo : public Parcelable {
+public:
+ LayerDebugInfo() = default;
+ LayerDebugInfo(const LayerDebugInfo&) = default;
+ virtual ~LayerDebugInfo() = default;
+
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
+
+ std::string mName = std::string("NOT FILLED");
+ std::string mParentName = std::string("NOT FILLED");
+ std::string mType = std::string("NOT FILLED");
+ Region mTransparentRegion = Region::INVALID_REGION;
+ Region mVisibleRegion = Region::INVALID_REGION;
+ Region mSurfaceDamageRegion = Region::INVALID_REGION;
+ uint32_t mLayerStack = 0;
+ float mX = 0.f;
+ float mY = 0.f;
+ uint32_t mZ = 0 ;
+ int32_t mWidth = -1;
+ int32_t mHeight = -1;
+ Rect mCrop = Rect::INVALID_RECT;
+ Rect mFinalCrop = Rect::INVALID_RECT;
+ float mAlpha = 0.f;
+ uint32_t mFlags = 0;
+ PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
+ android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
+ // Row-major transform matrix (SurfaceControl::setMatrix())
+ float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}};
+ int32_t mActiveBufferWidth = -1;
+ int32_t mActiveBufferHeight = -1;
+ int32_t mActiveBufferStride = 0;
+ PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE;
+ int32_t mNumQueuedFrames = -1;
+ bool mRefreshPending = false;
+ bool mIsOpaque = false;
+ bool mContentDirty = false;
+};
+
+std::string to_string(const LayerDebugInfo& info);
+
+} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 8bb705c..c15209d 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -90,6 +90,16 @@
status_t setFlags(uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(const Region& transparent);
status_t setAlpha(float alpha=1.0f);
+
+ // Experimentarily it appears that the matrix transforms the
+ // on-screen rectangle and it's contents before the position is
+ // applied.
+ //
+ // TODO: Test with other combinations to find approximate transformation rules.
+ //
+ // For example:
+ // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives
+ // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y))
status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
status_t setCrop(const Rect& crop);
status_t setFinalCrop(const Rect& crop);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e18af17..45e95a5 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -539,6 +539,9 @@
return NO_ERROR;
}
status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d8dc957..9abd04c 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -450,7 +450,7 @@
break;
}
- case AINPUT_EVENT_TYPE_MOTION: {
+ case InputMessage::TYPE_MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
Batch& batch = mBatches.editItemAt(batchIndex);
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index b174fa8..62acea3 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -556,6 +556,46 @@
return true;
}
+/*
+ * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
+ * the default implementation
+ */
+static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) {
+ float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
+
+ for (size_t i = 0; i < count; i++) {
+ float xi = x[i];
+ float yi = y[i];
+ float xi2 = xi*xi;
+ float xi3 = xi2*xi;
+ float xi4 = xi3*xi;
+ float xi2yi = xi2*yi;
+ float xiyi = xi*yi;
+
+ sxi += xi;
+ sxi2 += xi2;
+ sxiyi += xiyi;
+ sxi2yi += xi2yi;
+ syi += yi;
+ sxi3 += xi3;
+ sxi4 += xi4;
+ }
+
+ float Sxx = sxi2 - sxi*sxi / count;
+ float Sxy = sxiyi - sxi*syi / count;
+ float Sxx2 = sxi3 - sxi*sxi2 / count;
+ float Sx2y = sxi2yi - sxi2*syi / count;
+ float Sx2x2 = sxi4 - sxi2*sxi2 / count;
+
+ float numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
+ float denominator = Sxx*Sx2x2 - Sxx2*Sxx2;
+ if (denominator == 0) {
+ ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
+ return 0;
+ }
+ return numerator/denominator;
+}
+
bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
VelocityTracker::Estimator* outEstimator) const {
outEstimator->clear();
@@ -597,6 +637,19 @@
degree = m - 1;
}
if (degree >= 1) {
+ if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit
+ outEstimator->time = newestMovement.eventTime;
+ outEstimator->degree = 2;
+ outEstimator->confidence = 1;
+ outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0
+ outEstimator->yCoeff[0] = 0;
+ outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m);
+ outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m);
+ outEstimator->xCoeff[2] = 0;
+ outEstimator->yCoeff[2] = 0;
+ return true;
+ }
+
float xdet, ydet;
uint32_t n = degree + 1;
if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index d5676cc..2d72944 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -16,10 +16,13 @@
#include <ui/DebugUtils.h>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <android-base/stringprintf.h>
#include <string>
+using android::base::StringPrintf;
+
std::string decodeStandard(android_dataspace dataspace) {
const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
switch (dataspaceSelect) {
@@ -187,7 +190,7 @@
std::string dataspaceDetails(android_dataspace dataspace) {
if (dataspace == 0) {
- return "Default (0)";
+ return "Default";
}
return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
decodeTransfer(dataspace).c_str(),
@@ -262,3 +265,7 @@
return android::base::StringPrintf("Unknown %#08x", format);
}
}
+
+std::string to_string(const android::Rect& rect) {
+ return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
+}
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 30f4a59..dad9446 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -21,9 +21,14 @@
#include <string>
+namespace android {
+class Rect;
+}
+
std::string decodeStandard(android_dataspace dataspace);
std::string decodeTransfer(android_dataspace dataspace);
std::string decodeRange(android_dataspace dataspace);
std::string dataspaceDetails(android_dataspace dataspace);
std::string decodeColorMode(android_color_mode colormode);
std::string decodePixelFormat(android::PixelFormat format);
+std::string to_string(const android::Rect& rect);
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 3418d65..f9a5dcd 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -198,7 +198,7 @@
}
void HardwareComposer::OnPostThreadResumed() {
- hidl_.reset(new Hwc2::Composer(false));
+ hidl_.reset(new Hwc2::Composer("default"));
hidl_callback_ = new ComposerCallback;
hidl_->registerCallback(hidl_callback_);
diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h
index 36acff9..25033f2 100644
--- a/opengl/include/GLES/gl.h
+++ b/opengl/include/GLES/gl.h
@@ -51,6 +51,7 @@
#ifndef GL_VERSION_ES_CM_1_0
#define GL_VERSION_ES_CM_1_0 1
typedef void GLvoid;
+typedef char GLchar;
typedef unsigned int GLenum;
#include <KHR/khrplatform.h>
typedef khronos_float_t GLfloat;
diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h
index b6fe620..1a150e3 100644
--- a/opengl/include/GLES/glext.h
+++ b/opengl/include/GLES/glext.h
@@ -104,7 +104,6 @@
#ifndef GL_OES_byte_coordinates
#define GL_OES_byte_coordinates 1
-typedef khronos_int8_t GLbyte;
#endif /* GL_OES_byte_coordinates */
#ifndef GL_OES_compressed_ETC1_RGB8_sub_texture
@@ -128,7 +127,6 @@
#ifndef GL_OES_draw_texture
#define GL_OES_draw_texture 1
-typedef short GLshort;
#define GL_TEXTURE_CROP_RECT_OES 0x8B9D
typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height);
typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height);
@@ -409,7 +407,6 @@
#ifndef GL_OES_single_precision
#define GL_OES_single_precision 1
-typedef khronos_float_t GLclampf;
typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth);
typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation);
typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 371239d..399affc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -389,7 +389,7 @@
static bool find(std::string& result,
const std::string& pattern, const char* const search, bool exact) {
if (exact) {
- std::string absolutePath = std::string(search) + "/" + pattern;
+ std::string absolutePath = std::string(search) + "/" + pattern + ".so";
if (!access(absolutePath.c_str(), R_OK)) {
result = absolutePath;
return true;
diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp
index 69e56ff..5956366 100644
--- a/opengl/tests/hwc/hwcRects.cpp
+++ b/opengl/tests/hwc/hwcRects.cpp
@@ -170,7 +170,7 @@
static EGLint width, height;
// Function prototypes
-static Rectangle parseRect(string rectStr);
+static Rectangle parseRect(const string& rectStr);
void init(void);
void printSyntax(const char *cmd);
@@ -358,7 +358,7 @@
// Parse string description of rectangle and add it to list of rectangles
// to be rendered.
-static Rectangle parseRect(string rectStr)
+static Rectangle parseRect(const string& rectStr)
{
int rv;
string str;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cc93105..4775e4e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -2,3 +2,5 @@
name: "libsurfaceflingerincludes",
export_include_dirs: ["."],
}
+
+subdirs = ["tests/fakehwc"]
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b28c9ba..248ef53 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -65,7 +65,7 @@
using namespace android::hardware::configstore::V1_0;
static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3;
+ &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3;
#if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle)
// Dummy implementation in case it is missing.
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index ac739a2..433a224 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -157,15 +157,11 @@
write64(metadata.usage);
}
-Composer::Composer(bool useVrComposer)
+Composer::Composer(const std::string& serviceName)
: mWriter(kWriterInitialSize),
- mIsUsingVrComposer(useVrComposer)
+ mIsUsingVrComposer(serviceName == std::string("vr"))
{
- if (mIsUsingVrComposer) {
- mComposer = IComposer::getService("vr");
- } else {
- mComposer = IComposer::getService(); // use default name
- }
+ mComposer = IComposer::getService(serviceName);
if (mComposer == nullptr) {
LOG_ALWAYS_FATAL("failed to get hwcomposer service");
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 533509b..31a3c1d 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -136,7 +136,7 @@
// Composer is a wrapper to IComposer, a proxy to server-side composer.
class Composer {
public:
- Composer(bool useVrComposer);
+ Composer(const std::string& serviceName);
std::vector<IComposer::Capability> getCapabilities();
std::string dumpDebugInfo();
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 1ac21c6..93c6d54 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -34,6 +34,7 @@
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -103,6 +104,7 @@
sp<Fence> acquireFence(Fence::NO_FENCE);
android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
+ mDataSpace = dataspace;
if (result != NO_ERROR) {
ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
strerror(-result), result);
@@ -249,7 +251,10 @@
#endif
void FramebufferSurface::dumpAsString(String8& result) const {
- ConsumerBase::dumpState(result);
+ Mutex::Autolock lock(mMutex);
+ result.appendFormat("FramebufferSurface: dataspace: %s(%d)\n",
+ dataspaceDetails(mDataSpace).c_str(), mDataSpace);
+ ConsumerBase::dumpLocked(result, "");
}
void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 69a72d7..a1756ca 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -83,6 +83,13 @@
// or the buffer is not associated with a slot.
int mCurrentBufferSlot;
+ // mDataSpace is the dataspace of the current composition buffer for
+ // this FramebufferSurface. It will be 0 when HWC is doing the
+ // compositing. Otherwise it will display the dataspace of the buffer
+ // use for compositing which can change as wide-color content is
+ // on/off.
+ android_dataspace mDataSpace;
+
// mCurrentBuffer is the current buffer or NULL to indicate that there is
// no current buffer.
sp<GraphicBuffer> mCurrentBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ae44ae0..78c0c85 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -98,8 +98,8 @@
// Device methods
-Device::Device(bool useVrComposer)
- : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+Device::Device(const std::string& serviceName)
+ : mComposer(std::make_unique<Hwc2::Composer>(serviceName)),
mCapabilities(),
mDisplays(),
mRegisteredCallback(false)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 949f0e3..fbe4c7e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -79,10 +79,9 @@
class Device
{
public:
- // useVrComposer is passed to the composer HAL. When true, the composer HAL
- // will use the vr composer service, otherwise it uses the real hardware
- // composer.
- Device(bool useVrComposer);
+ // Service name is expected to be 'default' or 'vr' for normal use.
+ // 'vr' will slightly modify the behavior of the mComposer.
+ Device(const std::string& serviceName);
void registerCallback(ComposerCallback* callback, int32_t sequenceId);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3f3c67b..b096a3a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -59,7 +59,7 @@
// ---------------------------------------------------------------------------
-HWComposer::HWComposer(bool useVrComposer)
+HWComposer::HWComposer(const std::string& serviceName)
: mHwcDevice(),
mDisplayData(2),
mFreeDisplaySlots(),
@@ -73,7 +73,7 @@
mVSyncCounts[i] = 0;
}
- mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+ mHwcDevice = std::make_unique<HWC2::Device>(serviceName);
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e25dee1..3640bb5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -65,10 +65,9 @@
class HWComposer
{
public:
- // useVrComposer is passed to the composer HAL. When true, the composer HAL
- // will use the vr composer service, otherwise it uses the real hardware
- // composer.
- HWComposer(bool useVrComposer);
+ // Uses the named composer service. Valid choices for normal use
+ // are 'default' and 'vr'.
+ HWComposer(const std::string& serviceName);
~HWComposer();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 038ece2..e92565f 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,6 +40,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include "clz.h"
@@ -2420,69 +2421,51 @@
// debugging
// ----------------------------------------------------------------------------
-void Layer::dump(String8& result, Colorizer& colorizer) const
-{
- const Layer::State& s(getDrawingState());
-
- colorizer.colorize(result, Colorizer::GREEN);
- result.appendFormat(
- "+ %s %p (%s)\n",
- getTypeId(), this, getName().string());
- colorizer.reset(result);
-
- s.activeTransparentRegion.dump(result, "transparentRegion");
- visibleRegion.dump(result, "visibleRegion");
- surfaceDamageRegion.dump(result, "surfaceDamageRegion");
- sp<Client> client(mClientRef.promote());
- PixelFormat pf = PIXEL_FORMAT_UNKNOWN;
- const sp<GraphicBuffer>& buffer(getActiveBuffer());
- if (buffer != NULL) {
- pf = buffer->getPixelFormat();
+LayerDebugInfo Layer::getLayerDebugInfo() const {
+ LayerDebugInfo info;
+ const Layer::State& ds = getDrawingState();
+ info.mName = getName();
+ sp<Layer> parent = getParent();
+ info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
+ info.mType = String8(getTypeId());
+ info.mTransparentRegion = ds.activeTransparentRegion;
+ info.mVisibleRegion = visibleRegion;
+ info.mSurfaceDamageRegion = surfaceDamageRegion;
+ info.mLayerStack = getLayerStack();
+ info.mX = ds.active.transform.tx();
+ info.mY = ds.active.transform.ty();
+ info.mZ = ds.z;
+ info.mWidth = ds.active.w;
+ info.mHeight = ds.active.h;
+ info.mCrop = ds.crop;
+ info.mFinalCrop = ds.finalCrop;
+ info.mAlpha = ds.alpha;
+ info.mFlags = ds.flags;
+ info.mPixelFormat = getPixelFormat();
+ info.mDataSpace = getDataSpace();
+ info.mMatrix[0][0] = ds.active.transform[0][0];
+ info.mMatrix[0][1] = ds.active.transform[0][1];
+ info.mMatrix[1][0] = ds.active.transform[1][0];
+ info.mMatrix[1][1] = ds.active.transform[1][1];
+ {
+ sp<const GraphicBuffer> activeBuffer = getActiveBuffer();
+ if (activeBuffer != 0) {
+ info.mActiveBufferWidth = activeBuffer->getWidth();
+ info.mActiveBufferHeight = activeBuffer->getHeight();
+ info.mActiveBufferStride = activeBuffer->getStride();
+ info.mActiveBufferFormat = activeBuffer->format;
+ } else {
+ info.mActiveBufferWidth = 0;
+ info.mActiveBufferHeight = 0;
+ info.mActiveBufferStride = 0;
+ info.mActiveBufferFormat = 0;
+ }
}
-
- result.appendFormat( " "
- "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
- "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
- "isOpaque=%1d, invalidate=%1d, "
- "dataspace=%s, pixelformat=%s "
-#ifdef USE_HWC2
- "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#else
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#endif
- " client=%p\n",
- getLayerStack(), s.z,
- s.active.transform.tx(), s.active.transform.ty(),
- s.active.w, s.active.h,
- s.crop.left, s.crop.top,
- s.crop.right, s.crop.bottom,
- s.finalCrop.left, s.finalCrop.top,
- s.finalCrop.right, s.finalCrop.bottom,
- isOpaque(s), contentDirty,
- dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(),
- s.alpha, s.flags,
- s.active.transform[0][0], s.active.transform[0][1],
- s.active.transform[1][0], s.active.transform[1][1],
- client.get());
-
- sp<const GraphicBuffer> buf0(mActiveBuffer);
- uint32_t w0=0, h0=0, s0=0, f0=0;
- if (buf0 != 0) {
- w0 = buf0->getWidth();
- h0 = buf0->getHeight();
- s0 = buf0->getStride();
- f0 = buf0->format;
- }
- result.appendFormat(
- " "
- "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
- " queued-frames=%d, mRefreshPending=%d\n",
- mFormat, w0, h0, s0,f0,
- mQueuedFrames, mRefreshPending);
-
- if (mSurfaceFlingerConsumer != 0) {
- mSurfaceFlingerConsumer->dumpState(result, " ");
- }
+ info.mNumQueuedFrames = getQueuedFrameCount();
+ info.mRefreshPending = isBufferLatched();
+ info.mIsOpaque = isOpaque(ds);
+ info.mContentDirty = contentDirty;
+ return info;
}
#ifdef USE_HWC2
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c34d8a0..f7b82e4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -60,6 +60,7 @@
class DisplayDevice;
class GraphicBuffer;
class SurfaceFlinger;
+class LayerDebugInfo;
// ---------------------------------------------------------------------------
@@ -447,6 +448,8 @@
bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
mSidebandStreamChanged || mAutoRefresh; }
+ int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+
#ifdef USE_HWC2
// -----------------------------------------------------------------------
@@ -479,9 +482,9 @@
inline const State& getCurrentState() const { return mCurrentState; }
inline State& getCurrentState() { return mCurrentState; }
+ LayerDebugInfo getLayerDebugInfo() const;
/* always call base class first */
- void dump(String8& result, Colorizer& colorizer) const;
#ifdef USE_HWC2
static void miniDumpHeader(String8& result);
void miniDump(String8& result, int32_t hwcId) const;
@@ -679,6 +682,9 @@
sp<IGraphicBufferProducer> getProducer() const;
const String8& getName() const;
void notifyAvailableFrames();
+
+ PixelFormat getPixelFormat() const { return mFormat; }
+
private:
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7363464..959d22f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -46,6 +46,7 @@
#include <gui/BufferQueue.h>
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <ui/GraphicBufferAllocator.h>
@@ -138,6 +139,21 @@
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
bool SurfaceFlinger::hasWideColorDisplay;
+
+std::string getHwcServiceName() {
+ char value[PROPERTY_VALUE_MAX] = {};
+ property_get("debug.sf.hwc_service_name", value, "default");
+ ALOGI("Using HWComposer service: '%s'", value);
+ return std::string(value);
+}
+
+bool useTrebleTestingOverride() {
+ char value[PROPERTY_VALUE_MAX] = {};
+ property_get("debug.sf.treble_testing_override", value, "false");
+ ALOGI("Treble testing override: '%s'", value);
+ return std::string(value) == "true";
+}
+
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(),
mTransactionFlags(0),
@@ -146,6 +162,7 @@
mLayersRemoved(false),
mLayersAdded(false),
mRepaintEverything(0),
+ mHwcServiceName(getHwcServiceName()),
mRenderEngine(nullptr),
mBootTime(systemTime()),
mBuiltinDisplays(),
@@ -247,6 +264,15 @@
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
// instead read after the boot animation
+
+ if (useTrebleTestingOverride()) {
+ // Without the override SurfaceFlinger cannot connect to HIDL
+ // services that are not listed in the manifests. Considered
+ // deriving the setting from the set service name, but it
+ // would be brittle if the name that's not 'default' is used
+ // for production purposes later on.
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ }
}
void SurfaceFlinger::onFirstRef()
@@ -599,7 +625,7 @@
LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
"Starting with vr flinger active is not currently supported.");
- mHwc.reset(new HWComposer(false));
+ mHwc.reset(new HWComposer(mHwcServiceName));
mHwc->registerCallback(this, mComposerSequenceId);
if (useVrFlinger) {
@@ -1047,6 +1073,33 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(sDump, pid, uid)) {
+ ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+
+ // Try to acquire a lock for 1s, fail gracefully
+ const status_t err = mStateLock.timedLock(s2ns(1));
+ const bool locked = (err == NO_ERROR);
+ if (!locked) {
+ ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+ return TIMED_OUT;
+ }
+
+ outLayers->clear();
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ outLayers->push_back(layer->getLayerDebugInfo());
+ });
+
+ mStateLock.unlock();
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1336,7 +1389,8 @@
resetDisplayState();
mHwc.reset(); // Delete the current instance before creating the new one
- mHwc.reset(new HWComposer(vrFlingerRequestsDisplay));
+ mHwc.reset(new HWComposer(
+ vrFlingerRequestsDisplay ? "vr" : mHwcServiceName));
mHwc->registerCallback(this, ++mComposerSequenceId);
LOG_ALWAYS_FATAL_IF(!mHwc->getComposer()->isRemote(),
@@ -2205,7 +2259,7 @@
sp<const DisplayDevice> hw(mDisplays[dpy]);
if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
if (disp == NULL) {
- disp = hw;
+ disp = std::move(hw);
} else {
disp = NULL;
break;
@@ -2977,7 +3031,10 @@
}
}
if (what & layer_state_t::eRelativeLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3702,7 +3759,7 @@
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- layer->dump(result, colorizer);
+ result.append(to_string(layer->getLayerDebugInfo()).c_str());
});
/*
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 025da0e..058f4a1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -311,6 +311,7 @@
HdrCapabilities* outCapabilities) const;
virtual status_t enableVSyncInjections(bool enable);
virtual status_t injectVSync(nsecs_t when);
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
/* ------------------------------------------------------------------------
@@ -646,6 +647,10 @@
// vr, our HWComposer instance will be recreated.
std::unique_ptr<HWComposer> mHwc;
+#ifdef USE_HWC2
+ const std::string mHwcServiceName; // "default" for real use, something else for testing.
+#endif
+
// constant members (no synchronization needed for access)
RenderEngine* mRenderEngine;
nsecs_t mBootTime;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b28fe68..a92e1f9 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -41,6 +41,7 @@
#include <gui/BufferQueue.h>
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <ui/GraphicBufferAllocator.h>
@@ -931,6 +932,34 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(sDump, pid, uid)) {
+ ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+
+ // Try to acquire a lock for 1s, fail gracefully
+ status_t err = mStateLock.timedLock(s2ns(1));
+ bool locked = (err == NO_ERROR);
+ if (!locked) {
+ ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+ return TIMED_OUT;
+ }
+
+ outLayers->clear();
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ outLayers->push_back(layer->getLayerDebugInfo());
+ });
+
+ mStateLock.unlock();
+
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -2577,6 +2606,14 @@
}
}
}
+ if (what & layer_state_t::eRelativeLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+ if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
+ flags |= eTransactionNeeded|eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eSizeChanged) {
if (layer->setSize(s.w, s.h)) {
flags |= eTraversalNeeded;
@@ -3254,7 +3291,7 @@
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- layer->dump(result, colorizer);
+ result.append(to_string(layer->getLayerDebugInfo()).c_str());
});
/*
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
new file mode 100644
index 0000000..94f3f25
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -0,0 +1,35 @@
+cc_test {
+ name: "sffakehwc_test",
+ srcs: [
+ "FakeComposerClient.cpp",
+ "FakeComposerService.cpp",
+ "FakeComposerUtils.cpp",
+ "SFFakeHwc_test.cpp"
+ ],
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbinder",
+ "libui",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.mapper@2.0",
+ "libhwbinder",
+ "libhardware",
+ "libhidlbase",
+ "libsync",
+ "libfmq",
+ "libbase",
+ "libhidltransport"
+ ],
+ static_libs: [
+ "libhwcomposer-client",
+ "libsurfaceflingerincludes",
+ "libtrace_proto",
+ "libgmock"
+ ],
+ tags: ["tests"],
+ test_suites: ["device-tests"]
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
new file mode 100644
index 0000000..60916f3
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeComposer"
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+#include <inttypes.h>
+#include <time.h>
+#include <algorithm>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <set>
+#include <thread>
+
+constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
+constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1);
+
+using namespace sftest;
+
+using android::Condition;
+using android::Mutex;
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = std::chrono::time_point<Clock>;
+
+namespace {
+
+// Internal state of a layer in the HWC API.
+class LayerImpl {
+public:
+ LayerImpl() = default;
+
+ bool mValid = true;
+ RenderState mRenderState;
+ uint32_t mZ = 0;
+};
+
+// Struct for storing per frame rectangle state. Contains the render
+// state shared to the test case. Basically a snapshot and a subset of
+// LayerImpl sufficient to re-create the pixels of a layer for the
+// frame.
+struct FrameRect {
+public:
+ FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
+ : layer(layer_), renderState(state), z(z_) {}
+
+ const Layer layer;
+ const RenderState renderState;
+ const uint32_t z;
+};
+
+// Collection of FrameRects forming one rendered frame. Could store
+// related fences and other data in the future.
+class Frame {
+public:
+ Frame() = default;
+ std::vector<std::unique_ptr<FrameRect>> rectangles;
+};
+
+class DelayedEventGenerator {
+public:
+ DelayedEventGenerator(std::function<void()> onTimerExpired)
+ : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
+
+ ~DelayedEventGenerator() {
+ ALOGI("DelayedEventGenerator exiting.");
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mRunning = false;
+ mWakeups.clear();
+ mCondition.notify_one();
+ }
+ mThread.join();
+ ALOGI("DelayedEventGenerator exited.");
+ }
+
+ void wakeAfter(std::chrono::nanoseconds waitTime) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mWakeups.insert(Clock::now() + waitTime);
+ mCondition.notify_one();
+ }
+
+private:
+ void loop() {
+ while (true) {
+ // Lock scope
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
+ if (!mRunning && mWakeups.empty()) {
+ // This thread should only exit once the destructor has been called and all
+ // wakeups have been processed
+ return;
+ }
+
+ // At this point, mWakeups will not be empty
+
+ TimePoint target = *(mWakeups.begin());
+ auto status = mCondition.wait_until(lock, target);
+ while (status == std::cv_status::no_timeout) {
+ // This was either a spurious wakeup or another wakeup was added, so grab the
+ // oldest point and wait again
+ target = *(mWakeups.begin());
+ status = mCondition.wait_until(lock, target);
+ }
+
+ // status must have been timeout, so we can finally clear this point
+ mWakeups.erase(target);
+ }
+ // Callback *without* locks!
+ mOnTimerExpired();
+ }
+ }
+
+ std::function<void()> mOnTimerExpired;
+ std::thread mThread;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mRunning = true;
+ std::set<TimePoint> mWakeups;
+};
+
+} // namespace
+
+FakeComposerClient::FakeComposerClient()
+ : mCallbacksOn(false),
+ mClient(nullptr),
+ mCurrentConfig(NULL_DISPLAY_CONFIG),
+ mVsyncEnabled(false),
+ mLayers(),
+ mDelayedEventGenerator(
+ std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
+ mSurfaceComposer(nullptr) {}
+
+FakeComposerClient::~FakeComposerClient() {}
+
+void FakeComposerClient::removeClient() {
+ ALOGV("removeClient");
+ // TODO: Ahooga! Only thing current lifetime management choices in
+ // APIs make possible. Sad.
+ delete this;
+}
+
+void FakeComposerClient::enableCallback(bool enable) {
+ ALOGV("enableCallback");
+ mCallbacksOn = enable;
+ if (mCallbacksOn) {
+ mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ }
+}
+
+void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
+ if (mCallbacksOn) {
+ mClient->onHotplug(display, state);
+ }
+}
+
+uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
+ ALOGV("getMaxVirtualDisplayCount");
+ return 1;
+}
+
+Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat* /*format*/, Display* /*outDisplay*/) {
+ ALOGV("createVirtualDisplay");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+ ALOGV("destroyVirtualDisplay");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+ ALOGV("createLayer");
+ *outLayer = mLayers.size();
+ auto newLayer = std::make_unique<LayerImpl>();
+ mLayers.push_back(std::move(newLayer));
+ return Error::NONE;
+}
+
+Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+ ALOGV("destroyLayer");
+ mLayers[layer]->mValid = false;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+ ALOGV("getActiveConfig");
+
+ // TODO Assert outConfig != nullptr
+
+ // TODO This is my reading of the
+ // IComposerClient::getActiveConfig, but returning BAD_CONFIG
+ // seems to not fit SurfaceFlinger plans. See version 2 below.
+ // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
+ // return Error::BAD_CONFIG;
+ // }
+ //*outConfig = mCurrentConfig;
+ *outConfig = 1; // Very special config for you my friend
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+ uint32_t /*height*/, PixelFormat /*format*/,
+ Dataspace /*dataspace*/) {
+ ALOGV("getClientTargetSupport");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+ ALOGV("getColorModes");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) {
+ ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
+ static_cast<int>(config), static_cast<int>(attribute), outValue);
+
+ // TODO: SOOO much fun to be had with these alone
+ switch (attribute) {
+ case IComposerClient::Attribute::WIDTH:
+ *outValue = 1920;
+ break;
+ case IComposerClient::Attribute::HEIGHT:
+ *outValue = 1080;
+ break;
+ case IComposerClient::Attribute::VSYNC_PERIOD:
+ *outValue = 1666666666;
+ break; // TOOD: Tests break down if lowered to 16ms?
+ case IComposerClient::Attribute::DPI_X:
+ *outValue = 240;
+ break;
+ case IComposerClient::Attribute::DPI_Y:
+ *outValue = 240;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Say what!?! New attribute");
+ }
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
+ ALOGV("getDisplayConfigs");
+ // TODO assert display == 1, outConfigs != nullptr
+
+ outConfigs->resize(1);
+ (*outConfigs)[0] = 1;
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+ ALOGV("getDisplayName");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayType(Display /*display*/,
+ IComposerClient::DisplayType* outType) {
+ ALOGV("getDisplayType");
+ // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+ // assumed to be physical?
+ *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+ ALOGV("getDozeSupport");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
+ float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/,
+ float* /*outMinLuminance*/) {
+ ALOGV("getHdrCapabilities");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
+ ALOGV("setActiveConfig");
+ mCurrentConfig = config;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
+ ALOGV("setColorMode");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
+ ALOGV("setPowerMode");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
+ mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+ ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+ int32_t /*hint*/) {
+ ALOGV("setColorTransform");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+ int32_t /*acquireFence*/, int32_t /*dataspace*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setClientTarget");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+ int32_t /*releaseFence*/) {
+ ALOGV("setOutputBuffer");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::validateDisplay(
+ Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+ std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+ uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+ std::vector<uint32_t>* /*outRequestMasks*/) {
+ ALOGV("validateDisplay");
+ // TODO: Assume touching nothing means All Korrekt!
+ return Error::NONE;
+}
+
+Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+ ALOGV("acceptDisplayChanges");
+ // Didn't ask for changes because software is omnipotent.
+ return Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+ return a->z <= b->z;
+}
+
+Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+ std::vector<Layer>* /*outLayers*/,
+ std::vector<int32_t>* /*outReleaseFences*/) {
+ ALOGV("presentDisplay");
+ // TODO Leaving layers and their fences out for now. Doing so
+ // means that we've already processed everything. Important to
+ // test that the fences are respected, though. (How?)
+
+ std::unique_ptr<Frame> newFrame(new Frame);
+ for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+ const LayerImpl& layerImpl = *mLayers[layer];
+
+ if (!layerImpl.mValid) continue;
+
+ auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+ newFrame->rectangles.push_back(std::move(rect));
+ }
+ std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+ {
+ Mutex::Autolock _l(mStateMutex);
+ mFrames.push_back(std::move(newFrame));
+ mFramesAvailable.broadcast();
+ }
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+ int32_t /*x*/, int32_t /*y*/) {
+ ALOGV("setLayerCursorPosition");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
+ int32_t acquireFence) {
+ ALOGV("setLayerBuffer");
+ LayerImpl& l = getLayerImpl(layer);
+ if (buffer != l.mRenderState.mBuffer) {
+ l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+ }
+ l.mRenderState.mBuffer = buffer;
+ l.mRenderState.mAcquireFence = acquireFence;
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setLayerSurfaceDamage");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+ ALOGV("setLayerBlendMode");
+ getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+ IComposerClient::Color color) {
+ ALOGV("setLayerColor");
+ getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+ getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+ getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+ getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+ int32_t /*type*/) {
+ ALOGV("setLayerCompositionType");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+ int32_t /*dataspace*/) {
+ ALOGV("setLayerDataspace");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+ const hwc_rect_t& frame) {
+ ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+ frame.bottom);
+ getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+ ALOGV("setLayerPlaneAlpha");
+ getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+ buffer_handle_t /*stream*/) {
+ ALOGV("setLayerSidebandStream");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+ const hwc_frect_t& crop) {
+ ALOGV("setLayerSourceCrop");
+ getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
+ ALOGV("setLayerTransform");
+ getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+ const std::vector<hwc_rect_t>& visible) {
+ ALOGV("setLayerVisibleRegion");
+ getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+ ALOGV("setLayerZOrder");
+ getLayerImpl(layer).mZ = z;
+ return Error::NONE;
+}
+
+//////////////////////////////////////////////////////////////////
+
+void FakeComposerClient::setClient(ComposerClient* client) {
+ mClient = client;
+}
+
+void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
+ if (mCallbacksOn) {
+ uint64_t timestamp = vsyncTime;
+ ALOGV("Vsync");
+ if (timestamp == 0) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ }
+ if (mSurfaceComposer != nullptr) {
+ mSurfaceComposer->injectVSync(timestamp);
+ } else {
+ mClient->onVsync(DEFAULT_DISPLAY, timestamp);
+ }
+ }
+}
+
+void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
+ mDelayedEventGenerator->wakeAfter(wait);
+}
+
+LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
+ // TODO Change these to an internal state check that can be
+ // invoked from the gtest? GTest macros do not seem all that safe
+ // when used outside the test class
+ EXPECT_GE(handle, static_cast<Layer>(0));
+ EXPECT_LT(handle, mLayers.size());
+ return *(mLayers[handle]);
+}
+
+int FakeComposerClient::getFrameCount() const {
+ return mFrames.size();
+}
+
+static std::vector<RenderState> extractRenderState(
+ const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
+ std::vector<RenderState> result;
+ result.reserve(internalRects.size());
+ for (const std::unique_ptr<FrameRect>& rect : internalRects) {
+ result.push_back(rect->renderState);
+ }
+ return result;
+}
+
+std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
+ Mutex::Autolock _l(mStateMutex);
+ return extractRenderState(mFrames[frame]->rectangles);
+}
+
+std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
+ Mutex::Autolock _l(mStateMutex);
+ return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
+}
+
+void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
+ int currentFrame = 0;
+ {
+ Mutex::Autolock _l(mStateMutex); // I hope this is ok...
+ currentFrame = static_cast<int>(mFrames.size());
+ requestVSync();
+ }
+ waitUntilFrame(currentFrame + 1, maxWait);
+}
+
+void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
+ Mutex::Autolock _l(mStateMutex);
+ while (mFrames.size() < static_cast<size_t>(targetFrame)) {
+ android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
+ if (result == android::TIMED_OUT) {
+ ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
+ mFrames.size(), maxWait.count());
+ return;
+ }
+ }
+}
+
+void FakeComposerClient::clearFrames() {
+ Mutex::Autolock _l(mStateMutex);
+ mFrames.clear();
+ for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
+ if (layer->mValid) {
+ layer->mRenderState.mSwapCount = 0;
+ }
+ }
+}
+
+void FakeComposerClient::onSurfaceFlingerStart() {
+ mSurfaceComposer == nullptr;
+ do {
+ mSurfaceComposer = new android::SurfaceComposerClient;
+ android::status_t initResult = mSurfaceComposer->initCheck();
+ if (initResult != android::NO_ERROR) {
+ ALOGD("Init result: %d", initResult);
+ mSurfaceComposer = nullptr;
+ std::this_thread::sleep_for(10ms);
+ }
+ } while (mSurfaceComposer == nullptr);
+ ALOGD("SurfaceComposerClient created");
+ mSurfaceComposer->enableVSyncInjections(true);
+}
+
+void FakeComposerClient::onSurfaceFlingerStop() {
+ mSurfaceComposer->dispose();
+ mSurfaceComposer.clear();
+}
+
+// Includes destroyed layers, stored in order of creation.
+int FakeComposerClient::getLayerCount() const {
+ return mLayers.size();
+}
+
+Layer FakeComposerClient::getLayer(size_t index) const {
+ // NOTE: If/when passing calls through to actual implementation,
+ // this might get more involving.
+ return static_cast<Layer>(index);
+}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
new file mode 100644
index 0000000..294abb2
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+#include "RenderState.h"
+
+#include <utils/Condition.h>
+
+#include <chrono>
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace {
+class LayerImpl;
+class Frame;
+class DelayedEventGenerator;
+} // namespace
+
+namespace android {
+class SurfaceComposerClient;
+} // namespace android
+
+namespace sftest {
+
+class FakeComposerClient : public ComposerBase {
+public:
+ FakeComposerClient();
+ virtual ~FakeComposerClient();
+
+ void removeClient() override;
+ void enableCallback(bool enable) override;
+ uint32_t getMaxVirtualDisplayCount() override;
+ Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) override;
+ Error destroyVirtualDisplay(Display display) override;
+ Error createLayer(Display display, Layer* outLayer) override;
+ Error destroyLayer(Display display, Layer layer) override;
+
+ Error getActiveConfig(Display display, Config* outConfig) override;
+ Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+ PixelFormat format, Dataspace dataspace) override;
+ Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+ Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+ Error getDisplayName(Display display, hidl_string* outName) override;
+ Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+ Error getDozeSupport(Display display, bool* outSupport) override;
+ Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+ Error setActiveConfig(Display display, Config config) override;
+ Error setColorMode(Display display, ColorMode mode) override;
+ Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+ Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+ Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+ Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+ int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+ Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
+ Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+ std::vector<IComposerClient::Composition>* outCompositionTypes,
+ uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+ std::vector<uint32_t>* outRequestMasks) override;
+ Error acceptDisplayChanges(Display display) override;
+ Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
+ std::vector<int32_t>* outReleaseFences) override;
+
+ Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+ Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+ int32_t acquireFence) override;
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& damage) override;
+ Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+ Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+ Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+ Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+ Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
+ Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+ Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
+ Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+ Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& visible) override;
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+ void setClient(ComposerClient* client);
+
+ void requestVSync(uint64_t vsyncTime = 0);
+ // We don't want tests hanging, so always use a timeout. Remember
+ // to always check the number of frames with test ASSERT_!
+ // Wait until next frame is rendered after requesting vsync.
+ void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
+ void runVSyncAfter(std::chrono::nanoseconds wait);
+
+ int getFrameCount() const;
+ // We don't want tests hanging, so always use a timeout. Remember
+ // to always check the number of frames with test ASSERT_!
+ void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
+ std::vector<RenderState> getFrameRects(int frame) const;
+ std::vector<RenderState> getLatestFrame() const;
+ void clearFrames();
+
+ void onSurfaceFlingerStart();
+ void onSurfaceFlingerStop();
+
+ int getLayerCount() const;
+ Layer getLayer(size_t index) const;
+
+ void hotplugDisplay(Display display, IComposerCallback::Connection state);
+
+private:
+ LayerImpl& getLayerImpl(Layer handle);
+
+ bool mCallbacksOn;
+ ComposerClient* mClient;
+ Config mCurrentConfig;
+ bool mVsyncEnabled;
+ std::vector<std::unique_ptr<LayerImpl>> mLayers;
+ std::vector<std::unique_ptr<Frame>> mFrames;
+ // Using a pointer to hide the implementation into the CPP file.
+ std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
+ android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
+ mutable android::Mutex mStateMutex;
+ mutable android::Condition mFramesAvailable;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
new file mode 100644
index 0000000..c411604
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcService"
+#include <log/log.h>
+
+#include "FakeComposerService.h"
+
+using namespace android::hardware;
+
+namespace sftest {
+
+FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+
+FakeComposerService::~FakeComposerService() {
+ ALOGI("Maybe killing client %p", mClient.get());
+ // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+ ALOGI("FakeComposerService::getCapabilities");
+ hidl_cb(hidl_vec<Capability>());
+ return Void();
+}
+
+Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+ ALOGI("FakeComposerService::dumpDebugInfo");
+ hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+ ALOGI("FakeComposerService::createClient %p", mClient.get());
+ mClient->initialize();
+ hidl_cb(Error::NONE, mClient);
+ return Void();
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
new file mode 100644
index 0000000..5204084
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using android::hardware::Return;
+
+namespace sftest {
+
+class FakeComposerService : public IComposer {
+public:
+ FakeComposerService(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService();
+
+ Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+ Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+ Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+ android::sp<ComposerClient> mClient;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
new file mode 100644
index 0000000..51956ec
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcUtil"
+#include <log/log.h>
+
+#include "FakeComposerUtils.h"
+#include "RenderState.h"
+
+#include "SurfaceFlinger.h" // Get the name of the service...
+
+#include <binder/IServiceManager.h>
+
+#include <cutils/properties.h>
+
+#include <iomanip>
+#include <thread>
+
+using android::String16;
+using android::sp;
+using namespace std::chrono_literals;
+using namespace sftest;
+using std::setw;
+
+namespace sftest {
+
+// clang-format off
+inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
+ os << std::fixed << std::setprecision(1) << "("
+ << setw(align) << sourceRect.left << setw(0) << ","
+ << setw(align) << sourceRect.top << setw(0) << ","
+ << setw(align) << sourceRect.right << setw(0) << ","
+ << setw(align) << sourceRect.bottom << setw(0) << ")";
+}
+
+inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
+ os << "("
+ << setw(align) << displayRect.left << setw(0) << ","
+ << setw(align) << displayRect.top << setw(0) << ","
+ << setw(align) << displayRect.right << setw(0) << ","
+ << setw(align) << displayRect.bottom << setw(0) << ")";
+}
+// clang-format on
+
+inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
+ printSourceRectAligned(os, state.mSourceCrop, 7);
+ os << "->";
+ printDisplayRectAligned(os, state.mDisplayFrame, 5);
+ return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
+ << state.mPlaneAlpha << " Xform:" << state.mTransform;
+}
+
+// Helper for verifying the parts of the RenderState
+template <typename T>
+bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
+ const char* name) {
+ if (ref != val) {
+ message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
+ return false;
+ }
+ return true;
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
+ // TODO: Message could start as success and be assigned as failure.
+ // Only problem is that utility assumes it to be failure and just adds stuff. Would
+ // need still special case the initial failure in the utility?
+ // TODO: ... or would it be possible to break this back to gtest primitives?
+ ::testing::AssertionResult message = ::testing::AssertionFailure();
+ bool passes = true;
+
+ // The work here is mostly about providing good log strings for differences
+ passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
+ passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
+ passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
+ passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
+ // ... add more
+ if (passes) {
+ return ::testing::AssertionSuccess();
+ }
+ return message;
+}
+
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+ const std::vector<RenderState>& val) {
+ ::testing::AssertionResult message = ::testing::AssertionFailure();
+ bool passed = true;
+ if (ref.size() != val.size()) {
+ message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
+ passed = false;
+ }
+ for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
+ ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
+ if (rectResult == false) {
+ message << "First different rect at " << rectIndex << ": " << rectResult.message();
+ passed = false;
+ break;
+ }
+ }
+
+ if (passed) {
+ return ::testing::AssertionSuccess();
+ } else {
+ message << "\nReference:";
+ for (auto state = ref.begin(); state != ref.end(); ++state) {
+ message << "\n" << *state;
+ }
+ message << "\nActual:";
+ for (auto state = val.begin(); state != val.end(); ++state) {
+ message << "\n" << *state;
+ }
+ }
+ return message;
+}
+
+void startSurfaceFlinger() {
+ ALOGI("Start SurfaceFlinger");
+ system("start surfaceflinger");
+
+ sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<android::IBinder> sf;
+ while (sf == nullptr) {
+ std::this_thread::sleep_for(10ms);
+ sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+ }
+ ALOGV("SurfaceFlinger running");
+}
+
+void stopSurfaceFlinger() {
+ ALOGI("Stop SurfaceFlinger");
+ system("stop surfaceflinger");
+ sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<android::IBinder> sf;
+ while (sf != nullptr) {
+ std::this_thread::sleep_for(10ms);
+ sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+ }
+ ALOGV("SurfaceFlinger stopped");
+}
+
+////////////////////////////////////////////////
+
+void FakeHwcEnvironment::SetUp() {
+ ALOGI("Test env setup");
+ system("setenforce 0");
+ system("stop");
+ property_set("debug.sf.nobootanimation", "1");
+ {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.nobootanimation", value, "0");
+ LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
+ }
+ // TODO: Try registering the mock as the default service instead.
+ property_set("debug.sf.hwc_service_name", "mock");
+ // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+ property_set("debug.sf.treble_testing_override", "true");
+}
+
+void FakeHwcEnvironment::TearDown() {
+ ALOGI("Test env tear down");
+ system("stop");
+ // Wait for mock call signaling teardown?
+ property_set("debug.sf.nobootanimation", "0");
+ property_set("debug.sf.hwc_service_name", "default");
+ ALOGI("Test env tear down - done");
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
new file mode 100644
index 0000000..74dc0e5
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <hardware/hwcomposer_defs.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+// clang-format off
+// Note: This needs to reside in the global namespace for the GTest to use it
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
+ return os << "(" << rect.left << ","
+ << rect.top << ","
+ << rect.right << ","
+ << rect.bottom << ")";
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
+ return os << "(" << rect.left << ","
+ << rect.top << ","
+ << rect.right << ","
+ << rect.bottom << ")";
+}
+// clang-format on
+
+namespace sftest {
+
+class RenderState;
+
+// clang-format off
+inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
+ return a.top == b.top &&
+ a.left == b.left &&
+ a.bottom == b.bottom &&
+ a.right == b.right;
+}
+
+inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
+ return a.top == b.top &&
+ a.left == b.left &&
+ a.bottom == b.bottom &&
+ a.right == b.right;
+}
+// clang-format on
+
+inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
+ return !(a == b);
+}
+
+inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
+ return !(a == b);
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+ const std::vector<RenderState>& val);
+
+void startSurfaceFlinger();
+void stopSurfaceFlinger();
+
+class FakeHwcEnvironment : public ::testing::Environment {
+public:
+ virtual ~FakeHwcEnvironment() {}
+ void SetUp() override;
+ void TearDown() override;
+};
+
+/*
+ * All surface state changes are supposed to happen inside a global
+ * transaction. GlobalTransactionScope object at the beginning of
+ * scope automates the process. The resulting scope gives a visual cue
+ * on the span of the transaction as well.
+ *
+ * Closing the transaction is synchronous, i.e., it waits for
+ * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
+ * is built to explicitly request vsyncs one at the time. A delayed
+ * request must be made before closing the transaction or the test
+ * thread stalls until SurfaceFlinger does an emergency vsync by
+ * itself. GlobalTransactionScope encapsulates this vsync magic.
+ */
+class GlobalTransactionScope {
+public:
+ GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) {
+ android::SurfaceComposerClient::openGlobalTransaction();
+ }
+ ~GlobalTransactionScope() {
+ int frameCount = mComposer.getFrameCount();
+ mComposer.runVSyncAfter(1ms);
+ android::SurfaceComposerClient::closeGlobalTransaction(true);
+ // Make sure that exactly one frame has been rendered.
+ mComposer.waitUntilFrame(frameCount + 1);
+ LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+ "Unexpected frame advance. Delta: %d",
+ mComposer.getFrameCount() - frameCount);
+ }
+ FakeComposerClient& mComposer;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
new file mode 100644
index 0000000..0059289
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/RenderState.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/hwcomposer2.h>
+
+#include <vector>
+
+namespace sftest {
+// Description of a rendered rectangle. Should only contain
+// instructions necessary to rasterize the rectangle. The full scene
+// is given as a sorted list of rectangles, bottom layer at index 0.
+class RenderState {
+public:
+ RenderState() = default;
+ // Default copy-ctor
+
+ hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
+ hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
+ std::vector<hwc_rect_t> mVisibleRegion;
+ hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
+ buffer_handle_t mBuffer = 0;
+ uint32_t mSwapCount = 0; // How many set buffer calls to the layer.
+ int32_t mAcquireFence = 0; // Probably should not be here.
+ float mPlaneAlpha = 0.f;
+ hwc_color_t mLayerColor = {0, 0, 0, 0};
+ hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
new file mode 100644
index 0000000..8902ede
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcTest"
+
+#include "FakeComposerClient.h"
+#include "FakeComposerService.h"
+#include "FakeComposerUtils.h"
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <android/native_window.h>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include <hwbinder/ProcessState.h>
+
+#include <binder/ProcessState.h>
+
+#include <log/log.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <limits>
+
+using namespace std::chrono_literals;
+
+using namespace android;
+using namespace android::hardware;
+
+using namespace sftest;
+
+namespace {
+
+// Mock test helpers
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+///////////////////////////////////////////////
+
+struct TestColor {
+public:
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+};
+
+constexpr static TestColor RED = {195, 63, 63, 255};
+constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
+constexpr static TestColor GREEN = {63, 195, 63, 255};
+constexpr static TestColor BLUE = {63, 63, 195, 255};
+constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
+constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
+ bool unlock = true) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+ pixel[0] = color.r;
+ pixel[1] = color.g;
+ pixel[2] = color.b;
+ pixel[3] = color.a;
+ }
+ }
+ if (unlock) {
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ }
+}
+
+inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
+ RenderState res;
+ res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
+ res.mPlaneAlpha = 1.0f;
+ res.mSwapCount = 0;
+ res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
+ static_cast<float>(bottom - top)};
+ return res;
+}
+
+inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
+ unsigned int bottom) {
+ EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
+ return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
+ static_cast<int>(bottom));
+}
+
+////////////////////////////////////////////////
+
+class DisplayTest : public ::testing::Test {
+public:
+ class MockComposerClient : public FakeComposerClient {
+ public:
+ MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
+ MOCK_METHOD4(getDisplayAttribute,
+ Error(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue));
+
+ // Re-routing to basic fake implementation
+ Error getDisplayAttributeFake(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue) {
+ return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
+ }
+ };
+
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ sp<IComposer> mFakeService;
+ sp<SurfaceComposerClient> mComposerClient;
+
+ MockComposerClient* mMockComposer;
+};
+
+void DisplayTest::SetUp() {
+ // TODO: The mMockComposer should be a unique_ptr, but it needs to
+ // outlive the test class. Currently ComposerClient only dies
+ // when the service is replaced. The Mock deletes itself when
+ // removeClient is called on it, which is ugly. This can be
+ // changed if HIDL ServiceManager allows removing services or
+ // ComposerClient starts taking the ownership of the contained
+ // implementation class. Moving the fake class to the HWC2
+ // interface instead of the current Composer interface might also
+ // change the situation.
+ mMockComposer = new MockComposerClient;
+ sp<ComposerClient> client = new ComposerClient(*mMockComposer);
+ mMockComposer->setClient(client.get());
+ mFakeService = new FakeComposerService(client);
+ mFakeService->registerAsService("mock");
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ EXPECT_CALL(*mMockComposer, getDisplayType(1, _))
+ .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+ Return(Error::NONE)));
+ // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy
+ // but encoding that here exactly.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _))
+ .Times(5)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+ // TODO: Find out what code is generating the ID 0.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _))
+ .Times(5)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ mMockComposer->onSurfaceFlingerStart();
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+}
+
+void DisplayTest::TearDown() {
+ mComposerClient->dispose();
+ mComposerClient = nullptr;
+
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ mMockComposer->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+
+ mFakeService = nullptr;
+ // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+ // management.
+ mMockComposer = nullptr;
+}
+
+TEST_F(DisplayTest, Hotplug) {
+ ALOGD("DisplayTest::Hotplug");
+
+ EXPECT_CALL(*mMockComposer, getDisplayType(2, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+ Return(Error::NONE)));
+ // The attribute queries will get done twice. This is for defaults
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _))
+ .Times(2 * 3)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+ // ... and then special handling for dimensions. Specifying this
+ // rules later means that gmock will try them first, i.e.,
+ // ordering of width/height vs. the default implementation for
+ // other queries is significant.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+ // TODO: Width and height queries are not actually called. Display
+ // info returns dimensions 0x0 in display info. Why?
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::CONNECTED);
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(surfaceControl != nullptr);
+ ASSERT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ GlobalTransactionScope gts(*mMockComposer);
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, surfaceControl->show());
+ }
+ }
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::DISCONNECTED);
+
+ mMockComposer->clearFrames();
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::CONNECTED);
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(surfaceControl != nullptr);
+ ASSERT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ GlobalTransactionScope gts(*mMockComposer);
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, surfaceControl->show());
+ }
+ }
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::DISCONNECTED);
+}
+
+////////////////////////////////////////////////
+
+class TransactionTest : public ::testing::Test {
+protected:
+ // Layer array indexing constants.
+ constexpr static int BG_LAYER = 0;
+ constexpr static int FG_LAYER = 1;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ void SetUp() override;
+ void TearDown() override;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mFGSurfaceControl;
+ std::vector<RenderState> mBaseFrame;
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
+
+ static FakeComposerClient* sFakeComposer;
+};
+
+FakeComposerClient* TransactionTest::sFakeComposer;
+
+void TransactionTest::SetUpTestCase() {
+ // TODO: See TODO comment at DisplayTest::SetUp for background on
+ // the lifetime of the FakeComposerClient.
+ sFakeComposer = new FakeComposerClient;
+ sp<ComposerClient> client = new ComposerClient(*sFakeComposer);
+ sFakeComposer->setClient(client.get());
+ sp<IComposer> fakeService = new FakeComposerService(client);
+ fakeService->registerAsService("mock");
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ sFakeComposer->onSurfaceFlingerStart();
+}
+
+void TransactionTest::TearDownTestCase() {
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ sFakeComposer->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+ // TODO: This is deleted when the ComposerClient calls
+ // removeClient. Devise better lifetime control.
+ sFakeComposer = nullptr;
+}
+
+void TransactionTest::SetUp() {
+ ALOGI("TransactionTest::SetUp");
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ ALOGI("TransactionTest::SetUp - display");
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+
+ mDisplayWidth = info.w;
+ mDisplayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+ // Foreground surface
+ mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+ SurfaceComposerClient::openGlobalTransaction();
+
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1));
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+
+ // Synchronous transaction will stop this thread, so we set up a
+ // delayed, off-thread vsync request before closing the
+ // transaction. In the test code this is usually done with
+ // GlobalTransactionScope. Leaving here in the 'vanilla' form for
+ // reference.
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+ sFakeComposer->runVSyncAfter(1ms);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ sFakeComposer->waitUntilFrame(1);
+
+ // Reference data. This is what the HWC should see.
+ static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+ mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+ mBaseFrame[BG_LAYER].mSwapCount = 1;
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+ auto frame = sFakeComposer->getFrameRects(0);
+ ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+}
+
+void TransactionTest::TearDown() {
+ ALOGD("TransactionTest::TearDown");
+
+ mComposerClient->dispose();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ mComposerClient = 0;
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.clear();
+ sFakeComposer->clearFrames();
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ std::vector<LayerDebugInfo> layers;
+ status_t result = sf->getLayerDebugInfo(&layers);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to get layers %s %d", strerror(-result), result);
+ } else {
+ // If this fails, the test being torn down leaked layers.
+ EXPECT_EQ(0u, layers.size());
+ if (layers.size() > 0) {
+ for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+ std::cout << to_string(*layer).c_str();
+ }
+ // To ensure the next test has clean slate, will run the class
+ // tear down and setup here.
+ TearDownTestCase();
+ SetUpTestCase();
+ }
+ }
+ ALOGD("TransactionTest::TearDown - complete");
+}
+
+TEST_F(TransactionTest, LayerMove) {
+ ALOGD("TransactionTest::LayerMove");
+
+ // The scope opens and closes a global transaction and, at the
+ // same time, makes sure the SurfaceFlinger progresses one frame
+ // after the transaction closes. The results of the transaction
+ // should be available in the latest frame stored by the fake
+ // composer.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+ // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+ // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+ //
+ // sFakeComposer->runVSyncAndWait();
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+ // no extra frames.
+
+ // NOTE: Frame 0 is produced in the SetUp.
+ auto frame1Ref = mBaseFrame;
+ frame1Ref[FG_LAYER].mDisplayFrame =
+ hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerResize) {
+ ALOGD("TransactionTest::LayerResize");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+ // no extra frames.
+
+ auto frame1Ref = mBaseFrame;
+ // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+ frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerCrop) {
+ // TODO: Add scaling to confirm that crop happens in buffer space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(16, 16, 32, 32);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCrop) {
+ // TODO: Add scaling to confirm that crop happens in display space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(32, 32, 32 + 64, 32 + 64);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // In display space we are cropping with [32, 32, 96, 96] against display rect
+ // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCropEmpty) {
+ // TODO: Add scaling to confirm that crop happens in display space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(16, 16, 32, 32);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // In display space we are cropping with [16, 16, 32, 32] against display rect
+ // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
+ std::vector<RenderState> referenceFrame(1);
+ referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayer) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The layers will switch order, but both are rendered because the background layer is
+ // transparent (RGBA8888).
+ std::vector<RenderState> referenceFrame(2);
+ referenceFrame[0] = mBaseFrame[FG_LAYER];
+ referenceFrame[1] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayerOpaque) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+ ASSERT_EQ(NO_ERROR,
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque,
+ layer_state_t::eLayerOpaque));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The former foreground layer is now covered with opaque layer - it should have disappeared
+ std::vector<RenderState> referenceFrame(1);
+ referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetLayerStack) {
+ ALOGD("TransactionTest::SetLayerStack");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1));
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerShowHide) {
+ ALOGD("TransactionTest::LayerShowHide");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide());
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+ }
+
+ // Foreground layer should be back
+ ASSERT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetAlpha) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f));
+ }
+
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetFlags) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR,
+ mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden,
+ layer_state_t::eLayerHidden));
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetMatrix) {
+ struct matrixTestData {
+ float matrix[4];
+ hwc_transform_t expectedTransform;
+ hwc_rect_t expectedDisplayFrame;
+ };
+
+ // The matrix operates on the display frame and is applied before
+ // the position is added. So, the foreground layer rect is (0, 0,
+ // 64, 64) is first transformed, potentially yielding negative
+ // coordinates and then the position (64, 64) is added yielding
+ // the final on-screen rectangles given.
+
+ const matrixTestData MATRIX_TESTS[7] = // clang-format off
+ {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
+ {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
+ {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
+ {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
+ {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
+ // clang-format on
+ constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData);
+
+ for (int i = 0; i < TEST_COUNT; i++) {
+ // TODO: How to leverage the HWC2 stringifiers?
+ const matrixTestData& xform = MATRIX_TESTS[i];
+ SCOPED_TRACE(i);
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR,
+ mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1],
+ xform.matrix[2], xform.matrix[3]));
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+ referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+}
+
+#if 0
+TEST_F(TransactionTest, LayerSetMatrix2) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ // TODO: PLEASE SPEC THE FUNCTION!
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f,
+ -2.33f, 0.22f));
+ }
+ auto referenceFrame = mBaseFrame;
+ // TODO: Is this correct for sure?
+ //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+#endif
+
+TEST_F(TransactionTest, DeferredTransaction) {
+ // Synchronization surface
+ constexpr static int SYNC_LAYER = 2;
+ auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(syncSurfaceControl != nullptr);
+ ASSERT_TRUE(syncSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1));
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2));
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->show());
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+ mDisplayWidth - 1, mDisplayHeight - 1));
+ referenceFrame[SYNC_LAYER].mSwapCount = 1;
+ EXPECT_EQ(2, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // set up two deferred transactions on different frames - these should not yield composited
+ // frames
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+ mFGSurfaceControl
+ ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber());
+ }
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+ mFGSurfaceControl
+ ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ }
+ EXPECT_EQ(4, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should trigger the first deferred transaction, but not the second one
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should show up immediately since it's not deferred
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+ }
+ referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+ EXPECT_EQ(6, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // trigger the second deferred transaction
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ // TODO: Compute from layer size?
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_EQ(7, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetRelativeLayer) {
+ constexpr int RELATIVE_LAYER = 2;
+ auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+ // Now we stack the surface above the foreground surface and make sure it is visible.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ relativeSurfaceControl->setPosition(64, 64);
+ relativeSurfaceControl->show();
+ relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1);
+ }
+ auto referenceFrame = mBaseFrame;
+ // NOTE: All three layers will be visible as the surfaces are
+ // transparent because of the RGBA format.
+ referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // A call to setLayer will override a call to setRelativeLayer
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ relativeSurfaceControl->setLayer(0);
+ }
+
+ // Previous top layer will now appear at the bottom.
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+ EXPECT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+class ChildLayerTest : public TransactionTest {
+protected:
+ constexpr static int CHILD_LAYER = 2;
+
+ void SetUp() override {
+ TransactionTest::SetUp();
+ mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(mChild, LIGHT_GRAY);
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+ mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ }
+ void TearDown() override {
+ mChild = 0;
+ TransactionTest::TearDown();
+ }
+
+ sp<SurfaceControl> mChild;
+};
+
+TEST_F(ChildLayerTest, Positioning) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ // Move to the same position as in the original setup.
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+ }
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Cropping) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+ }
+ // NOTE: The foreground surface would be occluded by the child
+ // now, but is included in the stack because the child is
+ // transparent.
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, FinalCropping) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Constraints) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mFGSurfaceControl->setPosition(0, 0);
+ mChild->setPosition(63, 63);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Scaling) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+ }
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, LayerAlpha) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5));
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5));
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+ referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->detachChildren();
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->hide();
+ }
+
+ // Nothing should have changed. The child control becomes a no-op
+ // zombie on detach. See comments for detachChildren in the
+ // SurfaceControl.h file.
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // We cause scaling by 2.
+ mFGSurfaceControl->setSize(128, 128);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+
+ // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+ // the WM specified state size.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 64);
+ }
+
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+
+ // The child should still be in the same place and not have any strange scaling as in
+ // b/37673612.
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+ referenceFrame[FG_LAYER].mSwapCount++;
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+ // Destroy the child layer
+ mChild.clear();
+
+ // Now recreate it as hidden
+ mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
+ mFGSurfaceControl.get());
+
+ // Show the child layer in a deferred transaction
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber());
+ mChild->show();
+ }
+
+ // Render the foreground surface a few times
+ //
+ // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+ // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+ // never acquire/release the first buffer
+ ALOGI("Filling 1");
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 2");
+ fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 3");
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 4");
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+}
+
+class LatchingTest : public TransactionTest {
+protected:
+ void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+
+ void unlockFGBuffer() {
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ sFakeComposer->runVSyncAndWait();
+ }
+
+ void completeFGResize() {
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+ }
+ void restoreInitialState() {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(64, 64);
+ mFGSurfaceControl->setPosition(64, 64);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64));
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+ }
+};
+
+TEST_F(LatchingTest, SurfacePositionLatching) {
+ // By default position can be updated even while
+ // a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(32, 32);
+ mFGSurfaceControl->setPosition(100, 100);
+ }
+
+ // The size should not have updated as we have not provided a new buffer.
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ // Now we repeat with setGeometryAppliesWithResize
+ // and verify the position DOESN'T latch.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setSize(32, 32);
+ mFGSurfaceControl->setPosition(100, 100);
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+ referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, CropLatching) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatching) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame1[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame2[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+// In this test we ensure that setGeometryAppliesWithResize actually demands
+// a buffer of the new size, and not just any size.
+TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame1[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
+ // initiating the resize.
+ lockAndFillFGBuffer();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ // We now submit our old buffer, at the old size, and ensure it doesn't
+ // trigger geometry latching.
+ unlockFGBuffer();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+ auto referenceFrame3 = referenceFrame2;
+ referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame3[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ referenceFrame3[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
+ // In this scenario, we attempt to set the final crop a second time while the resize
+ // is still pending, and ensure we are successful. Success meaning the second crop
+ // is the one which eventually latches and not the first.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+ ::testing::AddGlobalTestEnvironment(fakeEnvironment);
+ ::testing::InitGoogleMock(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
index 062485e..4055527 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -382,7 +382,9 @@
if (outErr) {
*outErr = err;
} else {
- ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position";
+ ASSERT_TRUE((err == HWC2_ERROR_NONE) ||
+ (err == HWC2_ERROR_BAD_LAYER)) <<
+ "failed to set cursor position";
}
}
@@ -652,7 +654,7 @@
hwc2_layer_request_t request = requests.at(i);
EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
- 0) << "get display requests returned an unknown layer";
+ 1) << "get display requests returned an unknown layer";
EXPECT_NE(request, 0) << "returned empty request for layer "
<< requestedLayer;
@@ -1603,9 +1605,10 @@
EXPECT_EQ(layers.size(), fences.size());
for (int32_t fence : fences) {
- EXPECT_GE(sync_wait(fence, msWait), 0);
- if (fence >= 0)
+ if (fence >= 0) {
+ EXPECT_GE(sync_wait(fence, msWait), 0);
close(fence);
+ }
}
}
@@ -1643,8 +1646,9 @@
testLayers->getBlendMode(layer)));
EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
testLayers->getColor(layer)));
- EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left,
- cursor.top));
+ if (composition == HWC2_COMPOSITION_CURSOR)
+ EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer,
+ cursor.left, cursor.top));
EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
testLayers->getDataspace(layer)));
EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
@@ -2895,7 +2899,6 @@
ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
[] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
cursorPosition.left, cursorPosition.top, outErr));
@@ -4406,11 +4409,11 @@
/* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
{
- hwc2_display_t display = HWC_DISPLAY_PRIMARY;
hwc2_error_t err = HWC2_ERROR_NONE;
-
- ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
- EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+ for (auto display : mDisplays) {
+ ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+ EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+ }
}
/* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 5e32af0..fd271d0 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -232,7 +232,7 @@
VrHwc::~VrHwc() {}
-bool VrHwc::hasCapability(Capability capability) const { return false; }
+bool VrHwc::hasCapability(Capability /* capability */) const { return false; }
void VrHwc::removeClient() {
std::lock_guard<std::mutex> guard(mutex_);
@@ -306,13 +306,15 @@
return Error::NONE;
}
-Error VrHwc::getClientTargetSupport(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) {
+Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */,
+ uint32_t /* height */,
+ PixelFormat /* format */,
+ Dataspace /* dataspace */) {
return Error::NONE;
}
-Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) {
+Error VrHwc::getColorModes(Display /* display */,
+ hidl_vec<ColorMode>* outModes) {
std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
*outModes = hidl_vec<ColorMode>(color_modes);
return Error::NONE;
@@ -379,7 +381,7 @@
return Error::NONE;
}
-Error VrHwc::getDisplayName(Display display, hidl_string* outName) {
+Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
*outName = hidl_string();
return Error::NONE;
}
@@ -409,7 +411,8 @@
return Error::NONE;
}
-Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+Error VrHwc::getHdrCapabilities(Display /* display */,
+ hidl_vec<Hdr>* /* outTypes */,
float* outMaxLuminance,
float* outMaxAverageLuminance,
float* outMinLuminance) {
@@ -473,8 +476,8 @@
}
Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t dataspace,
- const std::vector<hwc_rect_t>& damage) {
+ int32_t acquireFence, int32_t /* dataspace */,
+ const std::vector<hwc_rect_t>& /* damage */) {
base::unique_fd fence(acquireFence);
std::lock_guard<std::mutex> guard(mutex_);
auto display_ptr = FindDisplay(display);
@@ -490,7 +493,7 @@
return Error::NONE;
}
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
+Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
int32_t releaseFence) {
base::unique_fd fence(releaseFence);
std::lock_guard<std::mutex> guard(mutex_);
@@ -505,8 +508,9 @@
Error VrHwc::validateDisplay(
Display display, std::vector<Layer>* outChangedLayers,
std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) {
+ uint32_t* /* outDisplayRequestMask */,
+ std::vector<Layer>* /* outRequestedLayers */,
+ std::vector<uint32_t>* /* outRequestMasks */) {
std::lock_guard<std::mutex> guard(mutex_);
auto display_ptr = FindDisplay(display);
if (!display_ptr)
@@ -517,7 +521,7 @@
return Error::NONE;
}
-Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; }
+Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
std::vector<Layer>* outLayers,
@@ -709,8 +713,8 @@
return Error::NONE;
}
-Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
- buffer_handle_t stream) {
+Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
+ buffer_handle_t /* stream */) {
std::lock_guard<std::mutex> guard(mutex_);
if (!FindDisplay(display))
return Error::BAD_DISPLAY;