Merge "Report gui::DisplayInfo to clients with window info changes"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 75505f3..eab72f4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1237,22 +1237,29 @@
std::string path(title);
path.append(" - ").append(String8(service).c_str());
size_t bytes_written = 0;
- status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
- if (status == OK) {
- dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
- std::chrono::duration<double> elapsed_seconds;
- if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
- service == String16("meminfo")) {
- // Use a longer timeout for meminfo, since 30s is not always enough.
- status = dumpsys.writeDump(STDOUT_FILENO, service, 60s,
- /* as_proto = */ false, elapsed_seconds, bytes_written);
- } else {
- status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
- /* as_proto = */ false, elapsed_seconds, bytes_written);
+ if (PropertiesHelper::IsDryRun()) {
+ dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
+ dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1));
+ } else {
+ status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
+ if (status == OK) {
+ dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
+ std::chrono::duration<double> elapsed_seconds;
+ if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH &&
+ service == String16("meminfo")) {
+ // Use a longer timeout for meminfo, since 30s is not always enough.
+ status = dumpsys.writeDump(STDOUT_FILENO, service, 60s,
+ /* as_proto = */ false, elapsed_seconds,
+ bytes_written);
+ } else {
+ status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,
+ /* as_proto = */ false, elapsed_seconds,
+ bytes_written);
+ }
+ dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
+ bool dump_complete = (status == OK);
+ dumpsys.stopDumpThread(dump_complete);
}
- dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);
- bool dump_complete = (status == OK);
- dumpsys.stopDumpThread(dump_complete);
}
auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -1836,8 +1843,10 @@
}
/* Run some operations that require root. */
- ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
- ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping());
+ if (!PropertiesHelper::IsDryRun()) {
+ ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
+ ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping());
+ }
ds.AddDir(RECOVERY_DIR, true);
ds.AddDir(RECOVERY_DATA_DIR, true);
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 74be7ce..5082eb0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -307,6 +307,8 @@
}
}
+ out << "is_dexopt_blocked:" << android::installd::is_dexopt_blocked() << endl;
+
out << endl;
out.flush();
@@ -2401,7 +2403,8 @@
const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
const std::optional<std::string>& profileName,
const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason) {
+ const std::optional<std::string>& compilationReason,
+ bool* aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PATH(apkPath);
@@ -2429,12 +2432,20 @@
const char* dm_path = getCStr(dexMetadataPath);
const char* compilation_reason = getCStr(compilationReason);
std::string error_msg;
+ bool completed = false; // not necessary but for compiler
int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,
- downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg);
+ downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg,
+ &completed);
+ *aidl_return = completed;
return res ? error(res, error_msg) : ok();
}
+binder::Status InstalldNativeService::controlDexOptBlocking(bool block) {
+ android::installd::control_dexopt_blocking(block);
+ return ok();
+}
+
binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
const std::string& packageName,
const std ::string& outDexFile, int uid,
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ea0c945..ae257df 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -117,7 +117,10 @@
const std::optional<std::string>& seInfo, bool downgrade,
int32_t targetSdkVersion, const std::optional<std::string>& profileName,
const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason);
+ const std::optional<std::string>& compilationReason,
+ bool* aidl_return);
+
+ binder::Status controlDexOptBlocking(bool block);
binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
const std::string& outDexFile, int uid, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 3d32f61..637a9f2 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -55,7 +55,8 @@
@utf8InCpp String packageName, int appId,
@utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
- void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+ // Returns false if it is cancelled. Returns true if it is completed or have other errors.
+ boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
@utf8InCpp String instructionSet, int dexoptNeeded,
@nullable @utf8InCpp String outputPath, int dexFlags,
@utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
@@ -64,6 +65,9 @@
@nullable @utf8InCpp String profileName,
@nullable @utf8InCpp String dexMetadataPath,
@nullable @utf8InCpp String compilationReason);
+ // Blocks (when block is true) or unblock (when block is false) dexopt.
+ // Blocking also invloves cancelling the currently running dexopt.
+ void controlDexOptBlocking(boolean block);
boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName,
@utf8InCpp String outDexFile, int uid);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index d678281..f3ec63f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -15,8 +15,8 @@
*/
#define LOG_TAG "installd"
-#include <array>
#include <fcntl.h>
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
@@ -28,10 +28,14 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <array>
#include <iomanip>
+#include <mutex>
+#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/no_destructor.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -47,6 +51,7 @@
#include <selinux/android.h>
#include <server_configurable_flags/get_flags.h>
#include <system/thread_defs.h>
+#include <utils/Mutex.h>
#include "dexopt.h"
#include "dexopt_return_codes.h"
@@ -69,6 +74,76 @@
using android::base::WriteFully;
using android::base::unique_fd;
+namespace {
+
+class DexOptStatus {
+ public:
+ // Check if dexopt is cancelled and fork if it is not cancelled.
+ // cancelled is set to true if cancelled. Otherwise it will be set to false.
+ // If it is not cancelled, it will return the return value of fork() call.
+ // If cancelled, fork will not happen and it will return -1.
+ pid_t check_cancellation_and_fork(/* out */ bool *cancelled) {
+ std::lock_guard<std::mutex> lock(dexopt_lock_);
+ if (dexopt_blocked_) {
+ *cancelled = true;
+ return -1;
+ }
+ pid_t pid = fork();
+ *cancelled = false;
+ if (pid > 0) { // parent
+ dexopt_pids_.insert(pid);
+ }
+ return pid;
+ }
+
+ // Returns true if pid was killed (is in killed list). It could have finished if killing
+ // happened after the process is finished.
+ bool check_if_killed_and_remove_dexopt_pid(pid_t pid) {
+ std::lock_guard<std::mutex> lock(dexopt_lock_);
+ dexopt_pids_.erase(pid);
+ if (dexopt_killed_pids_.erase(pid) == 1) {
+ return true;
+ }
+ return false;
+ }
+
+ // Tells whether dexopt is blocked or not.
+ bool is_dexopt_blocked() {
+ std::lock_guard<std::mutex> lock(dexopt_lock_);
+ return dexopt_blocked_;
+ }
+
+ // Enable or disable dexopt blocking.
+ void control_dexopt_blocking(bool block) {
+ std::lock_guard<std::mutex> lock(dexopt_lock_);
+ dexopt_blocked_ = block;
+ if (!block) {
+ return;
+ }
+ // Blocked, also kill currently running tasks
+ for (auto pid : dexopt_pids_) {
+ LOG(INFO) << "control_dexopt_blocking kill pid:" << pid;
+ kill(pid, SIGKILL);
+ dexopt_killed_pids_.insert(pid);
+ }
+ dexopt_pids_.clear();
+ }
+
+ private:
+ std::mutex dexopt_lock_;
+ // when true, dexopt is blocked and will not run.
+ bool dexopt_blocked_ GUARDED_BY(dexopt_lock_) = false;
+ // PIDs of child process while runinng dexopt.
+ // If the child process is finished, it should be removed.
+ std::unordered_set<pid_t> dexopt_pids_ GUARDED_BY(dexopt_lock_);
+ // PIDs of child processes killed by cancellation.
+ std::unordered_set<pid_t> dexopt_killed_pids_ GUARDED_BY(dexopt_lock_);
+};
+
+android::base::NoDestructor<DexOptStatus> dexopt_status_;
+
+} // namespace
+
namespace android {
namespace installd {
@@ -1525,23 +1600,46 @@
return ss.str();
}
+void control_dexopt_blocking(bool block) {
+ dexopt_status_->control_dexopt_blocking(block);
+}
+
+bool is_dexopt_blocked() {
+ return dexopt_status_->is_dexopt_blocked();
+}
+
+enum SecondaryDexOptProcessResult {
+ kSecondaryDexOptProcessOk = 0,
+ kSecondaryDexOptProcessCancelled = 1,
+ kSecondaryDexOptProcessError = 2
+};
+
// Processes the dex_path as a secondary dex files and return true if the path dex file should
-// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
-// successfully.
-// When returning true, the output parameters will be:
+// be compiled.
+// Returns: kSecondaryDexOptProcessError for errors (logged).
+// kSecondaryDexOptProcessOk if the secondary dex path was process successfully.
+// kSecondaryDexOptProcessCancelled if the processing was cancelled.
+//
+// When returning kSecondaryDexOptProcessOk, the output parameters will be:
// - is_public_out: whether or not the oat file should not be made public
// - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
// - oat_dir_out: the oat dir path where the oat file should be stored
-static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname,
- int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
- const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
- std::string* oat_dir_out, bool downgrade, const char* class_loader_context,
- const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) {
+static SecondaryDexOptProcessResult process_secondary_dex_dexopt(const std::string& dex_path,
+ const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid,
+ const char* instruction_set, const char* compiler_filter, bool* is_public_out,
+ int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade,
+ const char* class_loader_context, const std::vector<std::string>& context_dex_paths,
+ /* out */ std::string* error_msg) {
LOG(DEBUG) << "Processing secondary dex path " << dex_path;
+
+ if (dexopt_status_->is_dexopt_blocked()) {
+ return kSecondaryDexOptProcessCancelled;
+ }
+
int storage_flag;
if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) {
LOG(ERROR) << *error_msg;
- return false;
+ return kSecondaryDexOptProcessError;
}
// Compute the oat dir as it's not easy to extract it from the child computation.
char oat_path[PKG_PATH_MAX];
@@ -1550,11 +1648,15 @@
if (!create_secondary_dex_oat_layout(
dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path, error_msg)) {
LOG(ERROR) << "Could not create secondary odex layout: " << *error_msg;
- return false;
+ return kSecondaryDexOptProcessError;
}
oat_dir_out->assign(oat_dir);
- pid_t pid = fork();
+ bool cancelled = false;
+ pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
+ if (cancelled) {
+ return kSecondaryDexOptProcessCancelled;
+ }
if (pid == 0) {
// child -- drop privileges before continuing.
drop_capabilities(uid);
@@ -1623,12 +1725,17 @@
/* parent */
int result = wait_child(pid);
+ cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
if (!WIFEXITED(result)) {
+ if ((WTERMSIG(result) == SIGKILL) && cancelled) {
+ LOG(INFO) << "dexoptanalyzer cancelled for path:" << dex_path;
+ return kSecondaryDexOptProcessCancelled;
+ }
*error_msg = StringPrintf("dexoptanalyzer failed for path %s: 0x%04x",
dex_path.c_str(),
result);
LOG(ERROR) << *error_msg;
- return false;
+ return kSecondaryDexOptProcessError;
}
result = WEXITSTATUS(result);
// Check that we successfully executed dexoptanalyzer.
@@ -1656,7 +1763,7 @@
// It is ok to check this flag outside in the parent process.
*is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path);
- return success;
+ return success ? kSecondaryDexOptProcessOk : kSecondaryDexOptProcessError;
}
static std::string format_dexopt_error(int status, const char* dex_path) {
@@ -1670,17 +1777,29 @@
return StringPrintf("Dex2oat invocation for %s failed with 0x%04x", dex_path, status);
}
+
int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
const char* volume_uuid, const char* class_loader_context, const char* se_info,
bool downgrade, int target_sdk_version, const char* profile_name,
- const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) {
+ const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg,
+ /* out */ bool* completed) {
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
CHECK(error_msg != nullptr);
CHECK_EQ(dexopt_flags & ~DEXOPT_MASK, 0)
<< "dexopt flags contains unknown fields: " << dexopt_flags;
+ bool local_completed; // local placeholder for nullptr case
+ if (completed == nullptr) {
+ completed = &local_completed;
+ }
+ *completed = true;
+ if (dexopt_status_->is_dexopt_blocked()) {
+ *completed = false;
+ return 0;
+ }
+
if (!validate_dex_path_size(dex_path)) {
*error_msg = StringPrintf("Failed to validate %s", dex_path);
return -1;
@@ -1712,14 +1831,19 @@
*error_msg = "Failed acquiring context dex paths";
return -1; // We had an error, logged in the process method.
}
-
- if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
- instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
- downgrade, class_loader_context, context_dex_paths, error_msg)) {
+ SecondaryDexOptProcessResult sec_dex_result = process_secondary_dex_dexopt(dex_path,
+ pkgname, dexopt_flags, volume_uuid, uid,instruction_set, compiler_filter,
+ &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context,
+ context_dex_paths, error_msg);
+ if (sec_dex_result == kSecondaryDexOptProcessOk) {
oat_dir = oat_dir_str.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
return 0; // Nothing to do, report success.
}
+ } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) {
+ // cancelled, not an error.
+ *completed = false;
+ return 0;
} else {
if (error_msg->empty()) { // TODO: Make this a CHECK.
*error_msg = "Failed processing secondary.";
@@ -1849,7 +1973,11 @@
use_jitzygote_image,
compilation_reason);
- pid_t pid = fork();
+ bool cancelled = false;
+ pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
+ if (cancelled) {
+ return 0;
+ }
if (pid == 0) {
// Need to set schedpolicy before dropping privileges
// for cgroup migration. See details at b/175178520.
@@ -1867,9 +1995,16 @@
runner.Exec(DexoptReturnCodes::kDex2oatExec);
} else {
int res = wait_child(pid);
+ bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid);
if (res == 0) {
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---";
} else {
+ if ((WTERMSIG(res) == SIGKILL) && cancelled) {
+ LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled";
+ // cancelled, not an error
+ *completed = false;
+ return 0;
+ }
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"
<< std::hex << std::setw(4) << res << ", process failed";
*error_msg = format_dexopt_error(res, dex_path);
@@ -1877,12 +2012,14 @@
}
}
+ // TODO(b/156537504) Implement SWAP of completed files
// We've been successful, don't delete output.
out_oat.DisableCleanup();
out_vdex.DisableCleanup();
out_image.DisableCleanup();
reference_profile.DisableCleanup();
+ *completed = true;
return 0;
}
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5a637b1..12579b0 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -121,11 +121,18 @@
const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid,
int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
+// completed pass false if it is canceled. Otherwise it will be true even if there is other
+// error.
int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
const char* volume_uuid, const char* class_loader_context, const char* se_info,
bool downgrade, int target_sdk_version, const char* profile_name,
- const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg);
+ const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg,
+ /* out */ bool* completed = nullptr);
+
+bool is_dexopt_blocked();
+
+void control_dexopt_blocking(bool block);
bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
const char *apk_path, const char *instruction_set);
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 7e7e513..ea26955 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -232,6 +232,7 @@
virtual void TearDown() {
if (!kDebug) {
+ service_->controlDexOptBlocking(false);
service_->destroyAppData(
volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_);
run_cmd("rm -rf " + app_apk_dir_);
@@ -347,7 +348,7 @@
void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag,
bool should_binder_call_succeed, bool should_dex_be_compiled = true,
/*out */ binder::Status* binder_result = nullptr, int32_t uid = -1,
- const char* class_loader_context = nullptr) {
+ const char* class_loader_context = nullptr, bool expect_completed = true) {
if (uid == -1) {
uid = kTestAppUid;
}
@@ -364,6 +365,7 @@
std::optional<std::string> dm_path;
std::optional<std::string> compilation_reason;
+ bool completed = false;
binder::Status result = service_->dexopt(path,
uid,
package_name_,
@@ -379,8 +381,10 @@
target_sdk_version,
profile_name,
dm_path,
- compilation_reason);
+ compilation_reason,
+ &completed);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
+ ASSERT_EQ(expect_completed, completed);
int expected_access = should_dex_be_compiled ? 0 : -1;
std::string odex = GetSecondaryDexArtifact(path, "odex");
std::string vdex = GetSecondaryDexArtifact(path, "vdex");
@@ -431,6 +435,11 @@
ASSERT_EQ(mode, st.st_mode);
}
+ void AssertNoFile(const std::string& file) {
+ struct stat st;
+ ASSERT_EQ(-1, stat(file.c_str(), &st));
+ }
+
void CompilePrimaryDexOk(std::string compiler_filter,
int32_t dex_flags,
const char* oat_dir,
@@ -447,6 +456,7 @@
dm_path,
downgrade,
true,
+ true,
binder_result);
}
@@ -466,6 +476,27 @@
dm_path,
downgrade,
false,
+ true,
+ binder_result);
+ }
+
+ void CompilePrimaryDexCancelled(std::string compiler_filter,
+ int32_t dex_flags,
+ const char* oat_dir,
+ int32_t uid,
+ int32_t dexopt_needed,
+ binder::Status* binder_result = nullptr,
+ const char* dm_path = nullptr,
+ bool downgrade = false) {
+ CompilePrimaryDex(compiler_filter,
+ dex_flags,
+ oat_dir,
+ uid,
+ dexopt_needed,
+ dm_path,
+ downgrade,
+ true, // should_binder_call_succeed
+ false, // expect_completed
binder_result);
}
@@ -477,6 +508,7 @@
const char* dm_path,
bool downgrade,
bool should_binder_call_succeed,
+ bool expect_completed,
/*out */ binder::Status* binder_result) {
std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
std::string class_loader_context = "PCL[]";
@@ -491,6 +523,7 @@
dm_path_opt, &prof_result));
ASSERT_TRUE(prof_result);
+ bool completed = false;
binder::Status result = service_->dexopt(apk_path_,
uid,
package_name_,
@@ -506,8 +539,10 @@
target_sdk_version,
profile_name,
dm_path_opt,
- compilation_reason);
+ compilation_reason,
+ &completed);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
+ ASSERT_EQ(expect_completed, completed);
if (!should_binder_call_succeed) {
if (binder_result != nullptr) {
@@ -525,11 +560,20 @@
bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0;
mode_t mode = S_IFREG | (is_public ? 0644 : 0640);
- CheckFileAccess(odex, kSystemUid, uid, mode);
- CheckFileAccess(vdex, kSystemUid, uid, mode);
+ if (expect_completed) {
+ CheckFileAccess(odex, kSystemUid, uid, mode);
+ CheckFileAccess(vdex, kSystemUid, uid, mode);
+ } else {
+ AssertNoFile(odex);
+ AssertNoFile(vdex);
+ }
if (compiler_filter == "speed-profile") {
- CheckFileAccess(art, kSystemUid, uid, mode);
+ if (expect_completed) {
+ CheckFileAccess(art, kSystemUid, uid, mode);
+ } else {
+ AssertNoFile(art);
+ }
}
if (binder_result != nullptr) {
*binder_result = result;
@@ -750,6 +794,28 @@
empty_dm_file_.c_str());
}
+TEST_F(DexoptTest, DexoptBlockPrimary) {
+ LOG(INFO) << "DexoptPrimaryPublic";
+ service_->controlDexOptBlocking(true);
+ CompilePrimaryDexCancelled("verify",
+ DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+ app_oat_dir_.c_str(),
+ kTestAppGid,
+ DEX2OAT_FROM_SCRATCH, nullptr, nullptr);
+ service_->controlDexOptBlocking(false);
+}
+
+TEST_F(DexoptTest, DexoptUnblockPrimary) {
+ LOG(INFO) << "DexoptPrimaryPublic";
+ service_->controlDexOptBlocking(true);
+ service_->controlDexOptBlocking(false);
+ CompilePrimaryDexOk("verify",
+ DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+ app_oat_dir_.c_str(),
+ kTestAppGid,
+ DEX2OAT_FROM_SCRATCH, nullptr, nullptr);
+}
+
TEST_F(DexoptTest, DeleteDexoptArtifactsData) {
LOG(INFO) << "DeleteDexoptArtifactsData";
TestDeleteOdex(/*in_dalvik_cache=*/ false);
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index c0cbad8..235990a 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -7,18 +7,227 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+prebuilt_defaults {
+ name: "frameworks_native_data_etc_defaults",
+ relative_install_path: "permissions",
+ soc_specific: true,
+}
+
+// Modules use the 'prebuilt.xml' suffix to prevent conflicting
+// overridden paths, so that this Android.bp can exist alongside
+// devices that use PRODUCT_COPY_FILES for these files.
+//
+// This override prevention is also possible using a soong_namespace,
+// but that requires every dependent module (e.g. an APEX that includes
+// one of these files) to also reference this namespace, and so on
+// for all dependent modules. It is simpler to just use new path names.
+
prebuilt_etc {
- name: "android.hardware.biometrics.face.xml",
- product_specific: true,
- sub_dir: "permissions",
- src: "android.hardware.biometrics.face.xml",
- filename_from_src: true,
+ name: "android.hardware.audio.low_latency.prebuilt.xml",
+ src: "android.hardware.audio.low_latency.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
}
prebuilt_etc {
- name: "android.software.app_compat_overrides.xml",
- product_specific: true,
- sub_dir: "permissions",
- src: "android.software.app_compat_overrides.xml",
- filename_from_src: true,
+ name: "android.hardware.biometrics.face.prebuilt.xml",
+ src: "android.hardware.biometrics.face.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.bluetooth_le.prebuilt.xml",
+ src: "android.hardware.bluetooth_le.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.bluetooth.prebuilt.xml",
+ src: "android.hardware.bluetooth.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.camera.concurrent.prebuilt.xml",
+ src: "android.hardware.camera.concurrent.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.camera.flash-autofocus.prebuilt.xml",
+ src: "android.hardware.camera.flash-autofocus.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.camera.front.prebuilt.xml",
+ src: "android.hardware.camera.front.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.camera.full.prebuilt.xml",
+ src: "android.hardware.camera.full.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.camera.raw.prebuilt.xml",
+ src: "android.hardware.camera.raw.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.ethernet.prebuilt.xml",
+ src: "android.hardware.ethernet.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.faketouch.prebuilt.xml",
+ src: "android.hardware.faketouch.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.location.gps.prebuilt.xml",
+ src: "android.hardware.location.gps.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.reboot_escrow.prebuilt.xml",
+ src: "android.hardware.reboot_escrow.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.ambient_temperature.prebuilt.xml",
+ src: "android.hardware.sensor.ambient_temperature.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.barometer.prebuilt.xml",
+ src: "android.hardware.sensor.barometer.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.gyroscope.prebuilt.xml",
+ src: "android.hardware.sensor.gyroscope.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.hinge_angle.prebuilt.xml",
+ src: "android.hardware.sensor.hinge_angle.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.light.prebuilt.xml",
+ src: "android.hardware.sensor.light.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.proximity.prebuilt.xml",
+ src: "android.hardware.sensor.proximity.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.sensor.relative_humidity.prebuilt.xml",
+ src: "android.hardware.sensor.relative_humidity.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.telephony.gsm.prebuilt.xml",
+ src: "android.hardware.telephony.gsm.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.telephony.ims.prebuilt.xml",
+ src: "android.hardware.telephony.ims.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.usb.accessory.prebuilt.xml",
+ src: "android.hardware.usb.accessory.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.usb.host.prebuilt.xml",
+ src: "android.hardware.usb.host.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.vulkan.level-0.prebuilt.xml",
+ src: "android.hardware.vulkan.level-0.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.vulkan.version-1_0_3.prebuilt.xml",
+ src: "android.hardware.vulkan.version-1_0_3.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.wifi.prebuilt.xml",
+ src: "android.hardware.wifi.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.device_id_attestation.prebuilt.xml",
+ src: "android.software.device_id_attestation.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.ipsec_tunnels.prebuilt.xml",
+ src: "android.software.ipsec_tunnels.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.opengles.deqp.level-2021-03-01.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-2021-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.sip.voip.prebuilt.xml",
+ src: "android.software.sip.voip.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.verified_boot.prebuilt.xml",
+ src: "android.software.verified_boot.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-2021-03-01.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-2021-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "aosp_excluded_hardware.prebuilt.xml",
+ src: "aosp_excluded_hardware.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "handheld_core_hardware.prebuilt.xml",
+ src: "handheld_core_hardware.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
}
diff --git a/data/etc/apex/Android.bp b/data/etc/apex/Android.bp
new file mode 100644
index 0000000..8c4929c
--- /dev/null
+++ b/data/etc/apex/Android.bp
@@ -0,0 +1,34 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+apex_key {
+ name: "com.android.hardware.core_permissions.key",
+ public_key: "com.android.hardware.core_permissions.avbpubkey",
+ private_key: "com.android.hardware.core_permissions.pem",
+}
+
+android_app_certificate {
+ name: "com.android.hardware.core_permissions.certificate",
+ certificate: "com.android.hardware.core_permissions",
+}
+
+apex {
+ name: "com.android.hardware.core_permissions",
+ manifest: "apex_manifest.json",
+ key: "com.android.hardware.core_permissions.key",
+ certificate: ":com.android.hardware.core_permissions.certificate",
+ file_contexts: "file_contexts",
+ updatable: false,
+ // Install the apex in /vendor/apex
+ soc_specific: true,
+ prebuilts: [
+ "handheld_core_hardware.prebuilt.xml",
+ "aosp_excluded_hardware.prebuilt.xml",
+ ],
+}
diff --git a/data/etc/apex/apex_manifest.json b/data/etc/apex/apex_manifest.json
new file mode 100644
index 0000000..5bbf229
--- /dev/null
+++ b/data/etc/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.hardware.core_permissions",
+ "version": 1
+}
diff --git a/data/etc/apex/com.android.hardware.core_permissions.avbpubkey b/data/etc/apex/com.android.hardware.core_permissions.avbpubkey
new file mode 100644
index 0000000..b9164fb
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.avbpubkey
Binary files differ
diff --git a/data/etc/apex/com.android.hardware.core_permissions.pem b/data/etc/apex/com.android.hardware.core_permissions.pem
new file mode 100644
index 0000000..7e2826d
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAuJjZgFCp6uX+xgKET1FsXYqEPGfYEWUWJ4VSP+Y5fgth/Om3
+XCRBhKSD4CPyL/E9ohh7eSLRqIzw/idUazgCqk+yYLiVkYZiuY02jcui1/Vze9er
+Nwi0ZSwA+zcvKCEOwZ3PBT6W1kehiQ5PU0IS+78po8LoUrvycmvbXJTHlVt1x2bo
+y2DQmxRjIH9xfACwFwh/JnKyr1O2NGjFbk+z3CYx9l7c0Ew9U/kGL3teSbMEi1le
+2PApAHYUA+kXiwjF07aRUN+zzSdZsI7goQXEsmqGXNy7Fzdp/UDocayBmCdI3a35
+igcPRUryBIf1YdSS+E9DwoXRR7pwzs4ajvVTrzuv7UohTnhPwj95TD0E3Z64r7S7
+AT4jtm9gbkAsKNkKOqioGTEIdmvj2prOA4wOUmVBGwOjGcjsyEPJaa56s0WEXUop
++OD2ZMV3StAIwQ5c/gpFzxWl+qATv3MH9MgwAIjT1TDyXv0R+q9JbFbgTtO748RS
+MZUb1i2odggNQCWRtv8fqJ7c51H7pUpHXCXElXHysFq2oBOY4J1jXr1craxsPn1P
+RcImVwAYTV80jOfmYtWhdJhDavDeD4uinLw+4HeZnFNwZXqCJl0LtLxMDRxoDCr7
+YgT2znh9BM6XXg8jekfkDYb5wyloU1eOZJMxF04pGecDB9n1w/OFBA4v0WECAwEA
+AQKCAgEArjhsRsNaqvzo6L7lWurxCJO73Drx3PD36NLWXsKNjl113LpEOO1q/KI8
+aKXkZMUdM0hB+IEZOSfUJzq9XPge49iV9N0hJJidwpv5WfhQN9xLYx2YVTec8kOG
+pZJeqlQQ1kF3am6484HlfjIIQf8BZaH0zb8df0AtQTp0bTtp5pfMYCbLHW/BUiv6
+pmhBlhQcHZECWCo2ZGzwcSRU+Zi1mthdnTXI17qswv0rjlK0GYCgkFgHwV1ghTPs
+DgjHFIxyES+klJyc2MoDxzQB41dLXkxVhX06Al5lZQUGnIqAQTcKeVZCRrgE/JQQ
+OKCMwglbsIk23XdonnbjEvvIaxY1JHjcnPBpOC4+O11fE0DiHXK3GBj5KlfVvB5D
+oMWko3R/Ky/T/KJhHYXbC1H71oYueCaY57kHFKk2k3qRJG4DU4MY20cfUZ0vp14H
+KJ++gDy0pxxjl4ZUiryBCti5k5GPU8Mm46MLJ/YPdX6Dj1nMtOgGpZkGQYIKPhEI
+qZjZBRyZlHeTJsTMc8Eh3/Krimmra5ID95HfJnTTHHshbRcLwL6/zMTU5fkwarsC
+f4HQ0602/9HqyI8Ty1S9Z4oByjwfW2uDcosnuOPfk/8XwfLWxrf2+TsAd3cXhOKw
+rwUfELzcgYNueLGTJOCsEJfo8NIIEGJCNSgMnWXmIAUIAlrMP8ECggEBAOt9X4Lb
+fkrQLjcQ+K1oOI0Q+43htNRYXiG2IDhmDlA+UnqPP+9j2cVyuo4QYhKTFXRbbezR
+blUHAowd4hKHtODaHMuq90KbeByVNB8idcIb+ELAOK4Ff+qd9ijrBIOuZZQB+KOo
+SlxVjy1LM0QGtUTJRHx4dkCmp+hMqrIc4LQOWd4hV5h85Oj8kp1L1eMvxEStUtwP
+tYR80OoOdDxgXcBHLdPs4rc0WunRabGE+cnCMrTy31D95OWg6aw/+XKSTUS5Hfdy
+4jDIwP8DR6JU71qNgen+fwrHFeDienM40sSpi85/WQndQW5TwOMbDlEfmgn6D4+s
+rVWqFk1XElfwwSkCggEBAMisvdv5fH4UeH+tz5cF5f3ZNY+NX8uj85wCsJAPaAVx
+i3t8BqEKUPA6FPt9SDMWg4QtgSSl0NmhH2F9CcAZUCiTZUrtgqyIo80ALONW1FR9
+ofElvZEeV2IzKn3Ci4HA2pKRtMcjjVLwxzdoHakbPS9CbdCGKYE3W75Huw410IW6
+gV4zQ2mWHT+FxXaatl6Arj0x7DHLeSrMV33uxcYWoclmrAK24uhq2gwhtC8U0SvY
+rtJ7+KpCRd5v3Cyzo2AEbZKyJYVKbMDu9RHDZwkZw6wVqaOKBPJVyC++yidksexX
+ZT0WGX0f23t+2jbbsNY6H27pJm9ApLuAYwiMGv9n/XkCggEADLlAcNyVLUukQ5tq
+JExuScjyHo9kati/dUjW4tU4zsMfR7n3tWKKwK1bQRPHiMNjtF7ASLxkHrn7PEDd
+Fy0367I9Pg/lvjaSPdEd+NSu0icaudiS92wapj2UsE9Kdib1HBMjMQyFwAlrbAIV
+KgbGwomxZpxHn2Shy95glrESvwfLeUIJ7pZI9AG5lkAjtVu+WguXX4aFwzvPOeZA
+B4cZaasu4bV55nYwt1N2R34s1ObmQHqi8EhXlsSj+4eVXchj3mO2J8mQSRx/uQef
+VjkKmbTtoQv8J0PsfbMe9JzMXo3enPCqiernfyONV3f9xQpVE1bsglHNJ8TB4bnj
+pta+SQKCAQEArDqNrFkAftkk3jgXnX9jeC3O6UilugoZj4FDdjCyz1E3LCEzM02+
+T58Z2QoaSDZ/Y5cGaqShjdbaLvp4vtU61chDPD6CU3/mTZBj9i3UiDtXHLeObhlD
+WDWft1WcFB2nufmx1OPvbArYf/Ys1rFZHtF9nGU5A/y2EaZQpY6MS+nZFDcdGWbL
+7XPrGLMJ6Cu63yyUkdwXPyMnyB6AwVU1P7yNzrqWHnFueNEIawwLxfzvdhkOP1on
+yxPoPLlkc4j5XdjlmPNaSXANB1TUfpwNMwlYkdJoEnCLImc16v9iMPyFGBt6fsgz
+wFcMA98jc4lo5vDVmtA5Ue+Lj49nsGLYyQKCAQEAoB21vLDm7sLcZhzHFnh7h3L0
+wLMFMyQ0QDMWOqrlABcME21CWhibu8XwouF+hHLKX9GjaBB3ujkyeun1x8MpGQwT
+1SrMVN4osHpFrCh85uK5mabsqC8YjICIncU1U1ykxDV9v7TXuXgMtUIMKPFcUQLv
+ckf/PNE1Fvt5ESn2GIxk+ibM0L2kzHgDFgwiPx+k8GmJt5VZSXwehPmH6jgyCBIv
+kPHos1Q/z2LtfdUZcGhwX88mBNBpk3UXjiU8qO+ddoXCRgbThFDqYvOcdacbKGc0
+upFMhNsTWocn7CW0rbzusTsTt6bSWCGas8f9G9CMNN6rp8SW7Qc04m6sXVjPbw==
+-----END RSA PRIVATE KEY-----
diff --git a/data/etc/apex/com.android.hardware.core_permissions.pk8 b/data/etc/apex/com.android.hardware.core_permissions.pk8
new file mode 100644
index 0000000..4497844
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.pk8
Binary files differ
diff --git a/data/etc/apex/com.android.hardware.core_permissions.x509.pem b/data/etc/apex/com.android.hardware.core_permissions.x509.pem
new file mode 100644
index 0000000..57a311d
--- /dev/null
+++ b/data/etc/apex/com.android.hardware.core_permissions.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF7zCCA9cCFD7UZbcK7ZVyJegJvOs8e5T/n0DLMA0GCSqGSIb3DQEBCwUAMIGy
+MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
+MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEuMCwGA1UEAwwlY29t
+LmFuZHJvaWQuaGFyZHdhcmUuY29yZV9wZXJtaXNzaW9uczAgFw0yMTA4MjYyMDM2
+NTBaGA80NzU5MDcyMzIwMzY1MFowgbIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
+YWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRy
+b2lkMRAwDgYDVQQLDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMS4wLAYDVQQDDCVjb20uYW5kcm9pZC5oYXJkd2FyZS5jb3JlX3Bl
+cm1pc3Npb25zMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1ZGPs2s2
+R9/+jd3dHLH0nt4Z5WWduQ4Wqy6H9oojktTCv0IJekfiC6L0VHOvW9DKtSEGWTJg
+sJdoEpd2KDhNxAdjbE6k50AfKD0CAE0MAX2MyJIpUz7b6p6p7kxB7xpJohSGDOSZ
+otuMJ16W99PElwOtQr99Wztf+WehmEkKYDUVA0V3+UP3s0quAo3ZrFYgZxXy51YF
+/rcM+HFNrpBRNZRhm3n/2uM4vqt+HTjUFvPuxFIq44DH64ofD7OYBOTbq7rU3lFr
+jEP4IlVH1mjyirh0pWIzXtqhcj2yfV8HjqnwHz+DhIA7kI1hZNOTotTiYKZszboy
+mvo8YA+fuSDCohFxVJVHZGFu+FA+m353ZPLTDTee+d3F9JGbLxa3NoPAgDIyU/tR
+KD7+fKxfr32coZzf3bDi9T4CHBoHKoikRWqOAhkZ/xGvjX12bI6eS1QP9THGu3d6
+o+ZzSZDPR9GSu8ggFtkRGgaIIbgV3y4XUKqLK3+pyg14tHwqvSYMItZ7oOYKMT47
+R9qb8MlvAWS2ooZlJwSkGSM3y0pTwy3rKXug+Cpvx7zjc3Nm/srzdPAmrEvBTZZN
+k0J1oYKqiNHV1N3PIlMRE2d4g7MVF4GEwrAgL126JZOOmzefkP59eAPNrH6Coyka
+3pAVnifemHYZlhWpbKO6pnuxEwvBGXvJlgkCAwEAATANBgkqhkiG9w0BAQsFAAOC
+AgEAfc6TD6fURc9sKHn5TSi30rIlarR6h1TbxFL4PQroEXWafkNmOMujdyV6YZlZ
+j/lzU4ko+Pn3y1aU4r88PkrjkR2Ttf01H3wj8IkTVyl3kN3o/Ud2jJygL3SbjMS1
+CAxiotvPA3lst3KN3nnyFLe02EB4OIi8fS14/kUVFVz+djpGDhi/lJUzcTCzO1NR
+yoC3RuAHBrly1+0QYcXiRTtvgnXrUqsZery8pysR7OK8fWkJzQ6I6OipElrcRzfK
+qoRq/u7nn9FJxXauVjM5HNCPq1VsB8+/FQqP2CgvxNPtkxy8AkXS3hKMBC0Id0Mo
+sDsAr88QDnCb5DqrWSMDKTKA0k+2a6QwS72ANkK5Uq2Hf/xUVjn+yHLSFuo4pCYe
+TX4ZCjK8My+XPWEiHBV/AELAWA9SxxZFhHmyuPj3MwN5uJcPNPsQepnhpeGv0Dlp
+J7UfzkdAZvOyQ+9CAQfu8+e2MilLHC3uqfnGLpHGkcHoN93rmH1x14JdapKKk3XL
+4Wo0RzEjGQekjMF0topQSqDTukOMdtZ1T0WTVOstYLlNqlhn0ZVbwVANGklN/fDW
+d6OpayLPuuaLBAM6Ivnv7n4XA49ojd9Gw305Ha/ATEoH9KwImFjvUa4d9SkR0haP
+hAiGhpaWViEe2zXOpzLhePgqc2gJ/IO7vIgrVks0iVeBoWE=
+-----END CERTIFICATE-----
diff --git a/data/etc/apex/file_contexts b/data/etc/apex/file_contexts
new file mode 100644
index 0000000..6524a5e
--- /dev/null
+++ b/data/etc/apex/file_contexts
@@ -0,0 +1,2 @@
+(/.*)? u:object_r:vendor_file:s0
+/etc(/.*)? u:object_r:vendor_configs_file:s0
diff --git a/include/android/input.h b/include/android/input.h
index f03facb..6d2c1b3 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -950,9 +950,10 @@
* and {@link AMotionEvent_fromJava()}.
* After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
* The underlying Java object remains valid and does not change its state.
+ *
+ * Available since API level 31.
*/
-
-void AInputEvent_release(const AInputEvent* event);
+void AInputEvent_release(const AInputEvent* event) __INTRODUCED_IN(31);
/*** Accessors for key events only. ***/
@@ -1004,8 +1005,10 @@
* Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
* The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
* returned by this function must be disposed using {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
*/
-const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) __INTRODUCED_IN(31);
/*** Accessors for motion events only. ***/
@@ -1327,8 +1330,10 @@
* android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
* AInputEvent_* functions. The object returned by this function must be disposed using
* {@link AInputEvent_release()}.
+ *
+ * Available since API level 31.
*/
-const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31);
struct AInputQueue;
/**
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index 27c8476..ae70831 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,79 +14,22 @@
* limitations under the License.
*/
-#include <android-base/stringprintf.h>
+#pragma once
-#include <array>
+#include <ftl/enum.h>
+#include <ftl/string.h>
+
#include <cstdint>
-#include <optional>
+#include <iterator>
#include <string>
#include <type_traits>
-#include <ftl/NamedEnum.h>
#include "utils/BitSet.h"
-#pragma once
+// TODO(b/185536303): Align with FTL style and namespace.
namespace android {
-namespace details {
-
-template <typename F>
-inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
-
-template <typename F, typename T, T... I>
-constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
- constexpr size_t count = seq.size();
-
- std::array<F, count> values{};
- for (size_t i = 0, v = 0; v < count; ++i) {
- values[v++] = static_cast<F>(T{1} << i);
- }
-
- return values;
-}
-
-template <typename F>
-inline constexpr auto flag_values = generate_flag_values<F>(
- std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
-
-template <typename F, std::size_t... I>
-constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
- return std::array<std::optional<std::string_view>, sizeof...(I)>{
- {enum_value_name<F, flag_values<F>[I]>()...}};
-}
-
-template <typename F>
-inline constexpr auto flag_names =
- generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
-
-// A trait for determining whether a type is specifically an enum class or not.
-template <typename T, bool = std::is_enum_v<T>>
-struct is_enum_class : std::false_type {};
-
-// By definition, an enum class is an enum that is not implicitly convertible to its underlying
-// type.
-template <typename T>
-struct is_enum_class<T, true>
- : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
-
-template <typename T>
-inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
-} // namespace details
-
-template <auto V>
-constexpr auto flag_name() {
- using F = decltype(V);
- return details::enum_value_name<F, V>();
-}
-
-template <typename F>
-constexpr std::optional<std::string_view> flag_name(F flag) {
- using U = std::underlying_type_t<F>;
- auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
- return details::flag_names<F>[idx];
-}
-
/* A class for handling flags defined by an enum or enum class in a type-safe way. */
template <typename F>
class Flags {
@@ -94,7 +37,7 @@
// further to avoid this restriction but in general we want to encourage the use of enums
// anyways.
static_assert(std::is_enum_v<F>, "Flags type must be an enum");
- using U = typename std::underlying_type_t<F>;
+ using U = std::underlying_type_t<F>;
public:
constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
@@ -106,11 +49,10 @@
// should force them to be explicitly constructed from their underlying types to make full use
// of the type checker.
template <typename T = U>
- constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
- : mFlags(t) {}
+ constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {}
+
template <typename T = U>
- explicit constexpr Flags(T t,
- typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+ explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr)
: mFlags(t) {}
class Iterator {
@@ -229,16 +171,16 @@
bool first = true;
U unstringified = 0;
for (const F f : *this) {
- std::optional<std::string_view> flagString = flag_name(f);
- if (flagString) {
- appendFlag(result, flagString.value(), first);
+ if (const auto flagName = ftl::flag_name(f)) {
+ appendFlag(result, flagName.value(), first);
} else {
unstringified |= static_cast<U>(f);
}
}
if (unstringified != 0) {
- appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+ constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex;
+ appendFlag(result, ftl::to_string(unstringified, radix), first);
}
if (first) {
@@ -265,15 +207,14 @@
// as flags. In order to use these, add them via a `using namespace` declaration.
namespace flag_operators {
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
inline Flags<F> operator~(F f) {
- using U = typename std::underlying_type_t<F>;
- return static_cast<F>(~static_cast<U>(f));
+ return static_cast<F>(~ftl::enum_cast(f));
}
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
Flags<F> operator|(F lhs, F rhs) {
- using U = typename std::underlying_type_t<F>;
- return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+ return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs));
}
} // namespace flag_operators
diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h
deleted file mode 100644
index 6e98fee..0000000
--- a/include/ftl/NamedEnum.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/stringprintf.h>
-
-#include <array>
-#include <cstdint>
-#include <optional>
-#include <string>
-
-#pragma once
-
-namespace android {
-
-namespace details {
-template <typename E, E V>
-constexpr std::optional<std::string_view> enum_value_name() {
- // Should look something like (but all on one line):
- // std::optional<std::string_view>
- // android::details::enum_value_name()
- // [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
- std::string_view view = __PRETTY_FUNCTION__;
- size_t templateStart = view.rfind("[");
- size_t templateEnd = view.rfind("]");
- if (templateStart == std::string::npos || templateEnd == std::string::npos) {
- return std::nullopt;
- }
-
- // Extract the template parameters without the enclosing braces.
- // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
- view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
- size_t valStart = view.rfind("V = ");
- if (valStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Example (cont'd): V = android::test::TestEnums::ONE
- view = view.substr(valStart);
- // Check invalid enum values with cast, like V = (android::test::TestEnums)8.
- if (view.find('(') != std::string::npos) {
- return std::nullopt;
- }
- size_t nameStart = view.rfind("::");
- if (nameStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Chop off the initial "::"
- nameStart += 2;
- return view.substr(nameStart);
-}
-
-template <typename E, typename T, T... I>
-constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
- constexpr size_t count = seq.size();
-
- std::array<E, count> values{};
- for (size_t i = 0, v = 0; v < count; ++i) {
- values[v++] = static_cast<E>(T{0} + i);
- }
-
- return values;
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_values =
- generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
-
-template <typename E, std::size_t N, std::size_t... I>
-constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
- return std::array<std::optional<std::string_view>, sizeof...(I)>{
- {enum_value_name<E, enum_values<E, N>[I]>()...}};
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
-
-} // namespace details
-
-class NamedEnum {
-public:
- // By default allowed enum value range is 0 ~ 7.
- template <typename E>
- static constexpr size_t max = 8;
-
- template <auto V>
- static constexpr auto enum_name() {
- using E = decltype(V);
- return details::enum_value_name<E, V>();
- }
-
- template <typename E>
- static constexpr std::optional<std::string_view> enum_name(E val) {
- auto idx = static_cast<size_t>(val);
- return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
- }
-
- // Helper function for parsing enum value to string.
- // Example : enum class TestEnums { ZERO = 0x0 };
- // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
- // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
- // it should be declared to specialized the maximum enum by below:
- // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
- // If the enum class definition is sparse and contains enum values starting from a large value,
- // Do not specialize it to a large number to avoid performance issues.
- // The recommended maximum enum number to specialize is 64.
- template <typename E>
- static const std::string string(E val, const char* fallbackFormat = "%02d") {
- std::string result;
- std::optional<std::string_view> enumString = enum_name(val);
- result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
- return result;
- }
-};
-
-} // namespace android
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
new file mode 100644
index 0000000..dfe3a09
--- /dev/null
+++ b/include/ftl/enum.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <limits>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/string.h>
+
+// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
+// compiler-generated string literal for the signature of this function. The function is defined in
+// the global namespace with a short name and inferred return type to reduce bloat in the read-only
+// data segment.
+template <typename E, E V>
+constexpr auto ftl_enum() {
+ static_assert(std::is_enum_v<E>);
+
+ using R = std::optional<std::string_view>;
+ using namespace std::literals;
+
+ // The "pretty" signature has the following format:
+ //
+ // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue]
+ //
+ std::string_view view = __PRETTY_FUNCTION__;
+ const auto template_begin = view.rfind('[');
+ const auto template_end = view.rfind(']');
+ if (template_begin == view.npos || template_end == view.npos) return R{};
+
+ // Extract the template parameters without the enclosing brackets. Example (cont'd):
+ //
+ // E = android::test::Enum, V = android::test::Enum::kValue
+ //
+ view = view.substr(template_begin + 1, template_end - template_begin - 1);
+ const auto value_begin = view.rfind("V = "sv);
+ if (value_begin == view.npos) return R{};
+
+ // Example (cont'd):
+ //
+ // V = android::test::Enum::kValue
+ //
+ view = view.substr(value_begin);
+ const auto name_begin = view.rfind("::"sv);
+ if (name_begin == view.npos) return R{};
+
+ // Chop off the leading "::".
+ const auto name = view.substr(name_begin + 2);
+
+ // A value that is not enumerated has the format "Enum)42".
+ return name.find(')') == view.npos ? R{name} : R{};
+}
+
+namespace android::ftl {
+
+// Trait for determining whether a type is specifically a scoped enum or not. By definition, a
+// scoped enum is one that is not implicitly convertible to its underlying type.
+//
+// TODO: Replace with std::is_scoped_enum in C++23.
+//
+template <typename T, bool = std::is_enum_v<T>>
+struct is_scoped_enum : std::false_type {};
+
+template <typename T>
+struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
+};
+
+template <typename T>
+inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
+
+// Shorthand for casting an enumerator to its integral value.
+//
+// enum class E { A, B, C };
+// static_assert(ftl::enum_cast(E::B) == 1);
+//
+template <typename E>
+constexpr auto enum_cast(E v) {
+ return static_cast<std::underlying_type_t<E>>(v);
+}
+
+// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
+// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
+// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
+// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
+// range values results in undefined behavior if the underlying type is not fixed.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// static_assert(ftl::enum_begin_v<E> == E::A);
+// static_assert(ftl::enum_last_v<E> == E::F);
+// static_assert(ftl::enum_size_v<E> == 6);
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// static_assert(ftl::enum_begin_v<F> == F{0});
+// static_assert(ftl::enum_last_v<F> == F{15});
+// static_assert(ftl::enum_size_v<F> == 16);
+//
+template <typename E, typename = void>
+struct enum_begin {
+ static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
+ static constexpr E value{0};
+};
+
+template <typename E>
+struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
+ static constexpr E value = E::ftl_first;
+};
+
+template <typename E>
+inline constexpr E enum_begin_v = enum_begin<E>::value;
+
+template <typename E, typename = void>
+struct enum_end {
+ using U = std::underlying_type_t<E>;
+ static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
+
+ static constexpr E value{std::numeric_limits<U>::digits};
+};
+
+template <typename E>
+struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
+ static constexpr E value = E{enum_cast(E::ftl_last) + 1};
+};
+
+template <typename E>
+inline constexpr E enum_end_v = enum_end<E>::value;
+
+template <typename E>
+inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};
+
+template <typename E>
+struct enum_size {
+ static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ static constexpr auto kEnd = enum_cast(enum_end_v<E>);
+ static_assert(kBegin < kEnd, "Invalid range");
+
+ static constexpr std::size_t value = kEnd - kBegin;
+ static_assert(value <= 64, "Excessive range size");
+};
+
+template <typename E>
+inline constexpr std::size_t enum_size_v = enum_size<E>::value;
+
+namespace details {
+
+template <auto V>
+struct Identity {
+ static constexpr auto value = V;
+};
+
+template <typename E>
+using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
+
+template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
+struct EnumRange;
+
+template <typename E, template <E> class F, typename T, T... Vs>
+struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
+ static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ static constexpr auto kSize = enum_size_v<E>;
+
+ using R = decltype(F<E{}>::value);
+ const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
+
+ constexpr const auto* begin() const { return values; }
+ constexpr const auto* end() const { return values + kSize; }
+};
+
+template <auto V>
+struct EnumName {
+ static constexpr auto value = ftl_enum<decltype(V), V>();
+};
+
+template <auto I>
+struct FlagName {
+ using E = decltype(I);
+ using U = std::underlying_type_t<E>;
+
+ static constexpr E V{U{1} << enum_cast(I)};
+ static constexpr auto value = ftl_enum<E, V>();
+};
+
+} // namespace details
+
+// Returns an iterable over the range of an enum.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// std::string string;
+// for (E v : ftl::enum_range<E>()) {
+// string += ftl::enum_name(v).value_or("?");
+// }
+//
+// assert(string == "ABC??F");
+//
+template <typename E>
+constexpr auto enum_range() {
+ return details::EnumRange<E>{};
+}
+
+// Returns a stringified enumerator at compile time.
+//
+// enum class E { A, B, C };
+// static_assert(ftl::enum_name<E::B>() == "B");
+//
+template <auto V>
+constexpr std::string_view enum_name() {
+ constexpr auto kName = ftl_enum<decltype(V), V>();
+ static_assert(kName, "Unknown enumerator");
+ return *kName;
+}
+
+// Returns a stringified enumerator, possibly at compile time.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+// static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> enum_name(E v) {
+ const auto value = enum_cast(v);
+
+ constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ constexpr auto kLast = enum_cast(enum_last_v<E>);
+ if (value < kBegin || value > kLast) return {};
+
+ constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
+ return kRange.values[value - kBegin];
+}
+
+// Returns a stringified flag enumerator, possibly at compile time.
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> flag_name(E v) {
+ const auto value = enum_cast(v);
+
+ // TODO: Replace with std::popcount and std::countr_zero in C++20.
+ if (__builtin_popcountl(value) != 1) return {};
+
+ constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
+ return kRange.values[__builtin_ctzl(value)];
+}
+
+// Returns a stringified enumerator, or its integral value if not named.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// assert(ftl::enum_string(E::C) == "C");
+// assert(ftl::enum_string(E{3}) == "3");
+//
+template <typename E>
+inline std::string enum_string(E v) {
+ if (const auto name = enum_name(v)) {
+ return std::string(*name);
+ }
+ return to_string(enum_cast(v));
+}
+
+// Returns a stringified flag enumerator, or its integral value if not named.
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// assert(ftl::flag_string(F::Z) == "Z");
+// assert(ftl::flag_string(F{7}) == "0b111");
+//
+template <typename E>
+inline std::string flag_string(E v) {
+ if (const auto name = flag_name(v)) {
+ return std::string(*name);
+ }
+ constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
+ return to_string(enum_cast(v), radix);
+}
+
+} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index a6213f3..9148fee 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -18,7 +18,8 @@
#define _LIBINPUT_DISPLAY_VIEWPORT_H
#include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
+#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>
@@ -44,6 +45,8 @@
INTERNAL = 1,
EXTERNAL = 2,
VIRTUAL = 3,
+
+ ftl_last = VIRTUAL
};
/*
@@ -132,9 +135,8 @@
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
- physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
- : "<none>",
+ ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
+ physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
deviceHeight, isActive);
diff --git a/include/input/Input.h b/include/input/Input.h
index 2e42409..f170f0f 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -524,13 +524,17 @@
inline int32_t getAction() const { return mAction; }
- inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+ static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; }
- inline int32_t getActionIndex() const {
- return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ inline int32_t getActionMasked() const { return getActionMasked(mAction); }
+
+ static int32_t getActionIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
+ inline int32_t getActionIndex() const { return getActionIndex(mAction); }
+
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
@@ -1025,6 +1029,25 @@
std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
};
+/*
+ * Describes a unique request to enable or disable Pointer Capture.
+ */
+struct PointerCaptureRequest {
+public:
+ inline PointerCaptureRequest() : enable(false), seq(0) {}
+ inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+ inline bool operator==(const PointerCaptureRequest& other) const {
+ return enable == other.enable && seq == other.seq;
+ }
+ explicit inline operator bool() const { return enable; }
+
+ // True iff this is a request to enable Pointer Capture.
+ bool enable;
+
+ // The sequence number for the request.
+ uint32_t seq;
+};
+
} // namespace android
#endif // _LIBINPUT_INPUT_H
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7f0324a..22aae19 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -84,6 +84,9 @@
GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,
+
+ ftl_first = ACCELEROMETER,
+ ftl_last = SIGNIFICANT_MOTION
};
enum class InputDeviceSensorAccuracy : int32_t {
@@ -105,6 +108,8 @@
PLAYER_ID = 1,
RGB = 2,
MULTI_COLOR = 3,
+
+ ftl_last = MULTI_COLOR
};
struct InputDeviceSensorInfo {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 9a150eb..7632b30 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -73,6 +73,8 @@
DRAG,
TIMELINE,
TOUCH_MODE,
+
+ ftl_last = TOUCH_MODE
};
struct Header {
diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h
index 37a476e..7e6c515 100644
--- a/include/private/surface_control_private.h
+++ b/include/private/surface_control_private.h
@@ -29,8 +29,8 @@
/**
* Callback to be notified when surface stats for a specific surface control are available.
*/
-typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context,
- ASurfaceControl* control, ASurfaceControlStats* stats);
+typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, int32_t id,
+ ASurfaceControlStats* stats);
/**
* Registers a callback to be invoked when surface stats from a specific surface are available.
@@ -42,7 +42,7 @@
*
* \param func The callback to be invoked when surface stats are available.
*/
-void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id, void* context,
ASurfaceControl_SurfaceStatsListener func);
/**
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 68e0883..125cfaf 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -62,7 +62,7 @@
template <>
std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
std::stringstream s;
- s << "{ ";
+ s << "{";
bool first = true;
for (uint64_t n : v) {
if (!first) {
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
index 24cb437..e4e6b2a 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,7 +24,9 @@
class LongArrayMultiStateCounterTest : public testing::Test {};
TEST_F(LongArrayMultiStateCounterTest, stateChange) {
- LongArrayMultiStateCounter testCounter(2, 0, std::vector<uint64_t>(4), 1000);
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
@@ -34,7 +36,9 @@
}
TEST_F(LongArrayMultiStateCounterTest, accumulation) {
- LongArrayMultiStateCounter testCounter(2, 0, std::vector<uint64_t>(4), 1000);
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
testCounter.setState(0, 4000);
@@ -50,5 +54,16 @@
EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
}
+TEST_F(LongArrayMultiStateCounterTest, toString) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
} // namespace battery
} // namespace android
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 9f56b29..e1ee07c 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -42,6 +42,7 @@
T lastValue;
time_t lastUpdateTimestamp;
T deltaValue;
+ bool isEnabled;
struct State {
time_t timeInStateSinceUpdate;
@@ -51,15 +52,22 @@
State* states;
public:
- MultiStateCounter(uint16_t stateCount, state_t initialState, const T& emptyValue,
- time_t timestamp);
+ MultiStateCounter(uint16_t stateCount, const T& emptyValue);
virtual ~MultiStateCounter();
+ void setEnabled(bool enabled, time_t timestamp);
+
void setState(state_t state, time_t timestamp);
+ void setValue(state_t state, const T& value);
+
void updateValue(const T& value, time_t timestamp);
+ void reset();
+
+ uint16_t getStateCount();
+
const T& getCount(state_t state);
std::string toString();
@@ -86,15 +94,15 @@
// Since MultiStateCounter is a template, the implementation must be inlined.
template <class T>
-MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, state_t initialState,
- const T& emptyValue, time_t timestamp)
+MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
: stateCount(stateCount),
- currentState(initialState),
- lastStateChangeTimestamp(timestamp),
+ currentState(0),
+ lastStateChangeTimestamp(-1),
emptyValue(emptyValue),
lastValue(emptyValue),
- lastUpdateTimestamp(timestamp),
- deltaValue(emptyValue) {
+ lastUpdateTimestamp(-1),
+ deltaValue(emptyValue),
+ isEnabled(true) {
states = new State[stateCount];
for (int i = 0; i < stateCount; i++) {
states[i].timeInStateSinceUpdate = 0;
@@ -108,18 +116,39 @@
};
template <class T>
+void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+ if (enabled == isEnabled) {
+ return;
+ }
+
+ if (!enabled) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+ }
+
+ isEnabled = enabled;
+
+ if (lastStateChangeTimestamp >= 0) {
+ lastStateChangeTimestamp = timestamp;
+ }
+}
+
+template <class T>
void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
- if (timestamp >= lastStateChangeTimestamp) {
- states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
- } else {
- ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
- // The accumulated durations have become unreliable. For example, if the timestamp
- // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
- // we would get 4000, which is greater than (last - first). This could lead to
- // counts exceeding 100%.
- for (int i = 0; i < stateCount; i++) {
- states[i].timeInStateSinceUpdate = 0;
+ if (isEnabled && lastStateChangeTimestamp >= 0) {
+ if (timestamp >= lastStateChangeTimestamp) {
+ states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
+ } else {
+ ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ // The accumulated durations have become unreliable. For example, if the timestamp
+ // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
+ // we would get 4000, which is greater than (last - first). This could lead to
+ // counts exceeding 100%.
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
}
}
currentState = state;
@@ -127,36 +156,63 @@
}
template <class T>
-void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
- // Confirm the current state for the side-effect of updating the time-in-state
- // counter for the current state.
- setState(currentState, timestamp);
+void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+ states[state].counter = value;
+}
- if (timestamp > lastUpdateTimestamp) {
- if (delta(lastValue, value, &deltaValue)) {
- time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
- for (int i = 0; i < stateCount; i++) {
- time_t timeInState = states[i].timeInStateSinceUpdate;
- if (timeInState) {
- add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
- states[i].timeInStateSinceUpdate = 0;
+template <class T>
+void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
+ // If the counter is disabled, we ignore the update, except when the counter got disabled after
+ // the previous update, in which case we still need to pick up the residual delta.
+ if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+
+ if (lastUpdateTimestamp >= 0) {
+ if (timestamp > lastUpdateTimestamp) {
+ if (delta(lastValue, value, &deltaValue)) {
+ time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
+ for (int i = 0; i < stateCount; i++) {
+ time_t timeInState = states[i].timeInStateSinceUpdate;
+ if (timeInState) {
+ add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else {
+ std::stringstream str;
+ str << "updateValue is called with a value " << valueToString(value)
+ << ", which is lower than the previous value " << valueToString(lastValue)
+ << "\n";
+ ALOGE("%s", str.str().c_str());
}
+ } else if (timestamp < lastUpdateTimestamp) {
+ ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
}
- } else {
- std::stringstream str;
- str << "updateValue is called with a value " << valueToString(value)
- << ", which is lower than the previous value " << valueToString(lastValue) << "\n";
- ALOGE("%s", str.str().c_str());
}
- } else if (timestamp < lastUpdateTimestamp) {
- ALOGE("updateValue is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
}
lastValue = value;
lastUpdateTimestamp = timestamp;
}
template <class T>
+void MultiStateCounter<T>::reset() {
+ lastStateChangeTimestamp = -1;
+ lastUpdateTimestamp = -1;
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+uint16_t MultiStateCounter<T>::getStateCount() {
+ return stateCount;
+}
+
+template <class T>
const T& MultiStateCounter<T>::getCount(state_t state) {
return states[state].counter;
}
@@ -164,17 +220,29 @@
template <class T>
std::string MultiStateCounter<T>::toString() {
std::stringstream str;
- str << "currentState: " << currentState
- << " lastStateChangeTimestamp: " << lastStateChangeTimestamp
- << " lastUpdateTimestamp: " << lastUpdateTimestamp << " states: [";
+ str << "[";
for (int i = 0; i < stateCount; i++) {
if (i != 0) {
str << ", ";
}
- str << i << ": time: " << states[i].timeInStateSinceUpdate
- << " counter: " << valueToString(states[i].counter);
+ str << i << ": " << valueToString(states[i].counter);
+ if (states[i].timeInStateSinceUpdate > 0) {
+ str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
+ }
}
str << "]";
+ if (lastUpdateTimestamp >= 0) {
+ str << " updated: " << lastUpdateTimestamp;
+ }
+ if (lastStateChangeTimestamp >= 0) {
+ str << " currentState: " << currentState;
+ if (lastStateChangeTimestamp > lastUpdateTimestamp) {
+ str << " stateChanged: " << lastStateChangeTimestamp;
+ }
+ } else {
+ str << " currentState: none";
+ }
+
return str.str();
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index 942d5ca..319ba76 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -49,8 +49,9 @@
class MultiStateCounterTest : public testing::Test {};
TEST_F(MultiStateCounterTest, constructor) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 1000);
- testCounter.setState(1, 2000);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
testCounter.updateValue(3.14, 3000);
EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
@@ -59,7 +60,9 @@
}
TEST_F(MultiStateCounterTest, stateChange) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
testCounter.setState(2, 1000);
testCounter.updateValue(6.0, 3000);
@@ -68,8 +71,87 @@
EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
}
+TEST_F(MultiStateCounterTest, setEnabled) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setEnabled(false, 1000);
+ testCounter.setState(2, 2000);
+ testCounter.updateValue(6.0, 3000);
+
+ // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
+ // In state 2: 0, since it is still disabled
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Should have no effect since the counter is disabled
+ testCounter.setState(0, 3500);
+
+ // Should have no effect since the counter is disabled
+ testCounter.updateValue(10.0, 4000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ testCounter.setState(2, 4500);
+
+ // Enable the counter to partially accumulate deltas for the current state, 2
+ testCounter.setEnabled(true, 5000);
+ testCounter.setEnabled(false, 6000);
+ testCounter.setEnabled(true, 7000);
+ testCounter.updateValue(20.0, 8000);
+
+ // The delta is 10.0 over 5000-3000=2000.
+ // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
+ // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));
+
+ testCounter.reset();
+ testCounter.setState(0, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 2000);
+ testCounter.setEnabled(false, 3000);
+ testCounter.updateValue(200, 5000);
+
+ // 200 over 5000 = 40 per second
+ // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
+ // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
+ // so the count for state 1 should be 40 * 1 = 40.
+ // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
+ EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, reset) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.updateValue(2.72, 3000);
+
+ testCounter.reset();
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Assert that we can still continue accumulating after a reset
+ testCounter.updateValue(0, 4000);
+ testCounter.updateValue(3.14, 5000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
testCounter.setState(2, 2000);
// Time moves back
@@ -88,7 +170,9 @@
}
TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
- DoubleMultiStateCounter testCounter(1, 0, 0, 0);
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
testCounter.updateValue(6.0, 2000);
// Time moves back. The negative delta from 2000 to 1000 is ignored
@@ -101,5 +185,23 @@
EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
}
+TEST_F(MultiStateCounterTest, toString) {
+ DoubleMultiStateCounter testCounter(2, 0);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str());
+
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(1, 2000);
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]"
+ " updated: 0 currentState: 1 stateChanged: 2000",
+ testCounter.toString().c_str());
+
+ testCounter.updateValue(3.14, 3000);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
} // namespace battery
} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 9592562..06594d7 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -114,7 +114,6 @@
"ParcelFileDescriptor.cpp",
"PersistableBundle.cpp",
"ProcessState.cpp",
- "RpcAddress.cpp",
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
@@ -257,6 +256,7 @@
],
srcs: [
"RpcTransportTls.cpp",
+ "RpcCertificateUtils.cpp",
],
}
@@ -325,6 +325,7 @@
"libbinder_ndk",
"libutils",
],
+ export_include_dirs: ["include_rpc_unstable"],
// enumerate stable entry points, for apex use
stubs: {
@@ -334,14 +335,15 @@
// This library is intentionally limited to these targets, and it will be removed later.
// Do not expand the visibility.
visibility: [
- "//packages/modules/Virtualization/authfs:__subpackages__",
- "//packages/modules/Virtualization/compos:__subpackages__",
- "//packages/modules/Virtualization/microdroid",
- "//packages/modules/Virtualization/microdroid_manager",
- "//packages/modules/Virtualization/virtualizationservice",
+ "//packages/modules/Virtualization:__subpackages__",
],
}
+filegroup {
+ name: "libbinder_rpc_unstable_header",
+ srcs: ["include_rpc_unstable/binder_rpc_unstable.hpp"],
+}
+
// libbinder historically contained additional interfaces that provided specific
// functionality in the platform but have nothing to do with binder itself. These
// are moved out of libbinder in order to avoid the overhead of their vtables.
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 55566e2..92df874 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -36,7 +36,8 @@
// ---------------------------------------------------------------------------
Mutex BpBinder::sTrackingLock;
-std::unordered_map<int32_t,uint32_t> BpBinder::sTrackingMap;
+std::unordered_map<int32_t, uint32_t> BpBinder::sTrackingMap;
+std::unordered_map<int32_t, uint32_t> BpBinder::sLastLimitCallbackMap;
int BpBinder::sNumTrackedUids = 0;
std::atomic_bool BpBinder::sCountByUidEnabled(false);
binder_proxy_limit_callback BpBinder::sLimitCallback;
@@ -47,6 +48,10 @@
// Another arbitrary value a binder count needs to drop below before another callback will be called
uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000;
+// Once the limit has been exceeded, keep calling the limit callback for every this many new proxies
+// created over the limit.
+constexpr uint32_t REPEAT_LIMIT_CALLBACK_INTERVAL = 1000;
+
enum {
LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached
COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value
@@ -120,12 +125,24 @@
if (sBinderProxyThrottleCreate) {
return nullptr;
}
+ trackedValue = trackedValue & COUNTING_VALUE_MASK;
+ uint32_t lastLimitCallbackAt = sLastLimitCallbackMap[trackedUid];
+
+ if (trackedValue > lastLimitCallbackAt &&
+ (trackedValue - lastLimitCallbackAt > REPEAT_LIMIT_CALLBACK_INTERVAL)) {
+ ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies "
+ "held)",
+ getuid(), trackedUid, trackedValue);
+ if (sLimitCallback) sLimitCallback(trackedUid);
+ sLastLimitCallbackMap[trackedUid] = trackedValue;
+ }
} else {
if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
if (sLimitCallback) sLimitCallback(trackedUid);
+ sLastLimitCallbackMap[trackedUid] = trackedValue & COUNTING_VALUE_MASK;
if (sBinderProxyThrottleCreate) {
ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
" count drops below %d",
@@ -139,7 +156,7 @@
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
-sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, uint64_t address) {
LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
// These are not currently tracked, since there is no UID or other
@@ -176,7 +193,7 @@
return std::holds_alternative<RpcHandle>(mHandle);
}
-const RpcAddress& BpBinder::rpcAddress() const {
+uint64_t BpBinder::rpcAddress() const {
return std::get<RpcHandle>(mHandle).address;
}
@@ -465,8 +482,9 @@
((trackedValue & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark)
)) {
ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)",
- getuid(), mTrackedUid, sBinderProxyCountLowWatermark);
+ getuid(), sBinderProxyCountLowWatermark, mTrackedUid);
sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK;
+ sLastLimitCallbackMap.erase(mTrackedUid);
}
if (--sTrackingMap[mTrackedUid] == 0) {
sTrackingMap.erase(mTrackedUid);
@@ -492,7 +510,7 @@
{
ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
if (CC_UNLIKELY(isRpcBinder())) {
- (void)rpcSession()->sendDecStrong(rpcAddress());
+ (void)rpcSession()->sendDecStrong(this);
return;
}
IF_ALOGV() {
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index b197a6a..ecf13dc 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -59,19 +59,4 @@
}
}
-android::base::Result<bool> FdTrigger::isTriggeredPolled() {
- pollfd pfd{.fd = mRead.get(), .events = 0, .revents = 0};
- int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 0));
- if (ret < 0) {
- return android::base::ErrnoError() << "FdTrigger::isTriggeredPolled: Error in poll()";
- }
- if (ret == 0) {
- return false;
- }
- if (pfd.revents & POLLHUP) {
- return true;
- }
- return android::base::Error() << "FdTrigger::isTriggeredPolled: poll() returns " << pfd.revents;
-}
-
} // namespace android
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a428417..a545d6c 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -35,9 +35,13 @@
void trigger();
/**
- * Check whether this has been triggered by checking the write end.
+ * Check whether this has been triggered by checking the write end. Note:
+ * this has no internal locking, and it is inherently racey, but this is
+ * okay, because if we accidentally return false when a trigger has already
+ * happened, we can imagine that instead, the scheduler actually executed
+ * the code which is polling isTriggered earlier.
*/
- bool isTriggered();
+ [[nodiscard]] bool isTriggered();
/**
* Poll for a read event.
@@ -48,17 +52,7 @@
* true - time to read!
* false - trigger happened
*/
- status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
-
- /**
- * Check whether this has been triggered by poll()ing the read end.
- *
- * Return:
- * true - triggered
- * false - not triggered
- * error - error when polling
- */
- android::base::Result<bool> isTriggeredPolled();
+ [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
private:
base::unique_fd mWrite;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index fa9f3a9..9e04ffe 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1426,6 +1426,25 @@
return ret;
}
+#ifndef __ANDROID_VNDK__
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+ uint32_t *async_received)
+{
+ int ret = 0;
+ binder_frozen_status_info info;
+ info.pid = pid;
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+ ret = -errno;
+#endif
+ *sync_received = info.sync_recv;
+ *async_received = info.async_recv;
+
+ return ret;
+}
+#endif
+
status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
struct binder_freeze_info info;
int ret = 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 99b6be8..7575252 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -205,11 +205,11 @@
if (binder) {
status_t status = writeInt32(1); // non-null
if (status != OK) return status;
- RpcAddress address = RpcAddress::zero();
+ uint64_t address;
// TODO(b/167966510): need to undo this if the Parcel is not sent
status = mSession->state()->onBinderLeaving(mSession, binder, &address);
if (status != OK) return status;
- status = address.writeToParcel(this);
+ status = writeUint64(address);
if (status != OK) return status;
} else {
status_t status = writeInt32(0); // null
@@ -237,7 +237,7 @@
return INVALID_OPERATION;
}
}
- const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0;
+ const int32_t handle = proxy ? proxy->getPrivateAccessor().binderHandle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
@@ -279,15 +279,15 @@
if (isForRpc()) {
LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
- int32_t isNull;
- status_t status = readInt32(&isNull);
+ int32_t isPresent;
+ status_t status = readInt32(&isPresent);
if (status != OK) return status;
sp<IBinder> binder;
- if (isNull & 1) {
- auto addr = RpcAddress::zero();
- if (status_t status = addr.readFromParcel(*this); status != OK) return status;
+ if (isPresent & 1) {
+ uint64_t addr;
+ if (status_t status = readUint64(&addr); status != OK) return status;
if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder);
status != OK)
return status;
@@ -572,7 +572,7 @@
LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
- markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
+ markForRpc(binder->remoteBinder()->getPrivateAccessor().rpcSession());
}
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ab0e88..94b2806 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -212,7 +212,7 @@
binder_node_info_for_ref info;
memset(&info, 0, sizeof(binder_node_info_for_ref));
- info.handle = binder->getPrivateAccessorForId().binderHandle();
+ info.handle = binder->getPrivateAccessor().binderHandle();
status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
@@ -301,7 +301,7 @@
return nullptr;
}
- sp<BpBinder> b = BpBinder::create(handle);
+ sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
deleted file mode 100644
index ffc94b9..0000000
--- a/libs/binder/RpcAddress.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <binder/RpcAddress.h>
-
-#include <android-base/hex.h>
-#include <binder/Parcel.h>
-
-#include "Debug.h"
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-namespace android {
-
-RpcAddress RpcAddress::zero() {
- return RpcAddress();
-}
-
-bool RpcAddress::isZero() const {
- RpcWireAddress ZERO{.options = 0};
- return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0;
-}
-
-static void ReadRandomBytes(uint8_t* buf, size_t len) {
- int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (fd == -1) {
- ALOGE("%s: cannot read /dev/urandom", __func__);
- return;
- }
-
- size_t n;
- while ((n = TEMP_FAILURE_RETRY(read(fd, buf, len))) > 0) {
- len -= n;
- buf += n;
- }
- if (len > 0) {
- ALOGW("%s: there are %d bytes skipped", __func__, (int)len);
- }
- close(fd);
-}
-
-RpcAddress RpcAddress::random(bool forServer) {
- // The remainder of this header acts as reserved space for different kinds
- // of binder objects.
- uint64_t options = RPC_WIRE_ADDRESS_OPTION_CREATED;
-
- // servers and clients allocate addresses independently, so this bit can
- // tell you where an address originates
- if (forServer) options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
-
- RpcAddress ret;
- RpcWireAddress* raw = ret.mRawAddr.get();
-
- raw->options = options;
- ReadRandomBytes(raw->address, sizeof(raw->address));
-
- LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str());
- return ret;
-}
-
-bool RpcAddress::isForServer() const {
- return mRawAddr.get()->options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
-}
-
-bool RpcAddress::isRecognizedType() const {
- uint64_t allKnownOptions = RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
- return (mRawAddr.get()->options & ~allKnownOptions) == 0;
-}
-
-RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) {
- RpcAddress addr;
- memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress));
- return addr;
-}
-
-const RpcWireAddress& RpcAddress::viewRawEmbedded() const {
- return *mRawAddr.get();
-}
-
-bool RpcAddress::operator<(const RpcAddress& rhs) const {
- return std::memcmp(mRawAddr.get(), rhs.mRawAddr.get(), sizeof(RpcWireAddress)) < 0;
-}
-
-std::string RpcAddress::toString() const {
- return base::HexString(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-status_t RpcAddress::writeToParcel(Parcel* parcel) const {
- return parcel->write(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-status_t RpcAddress::readFromParcel(const Parcel& parcel) {
- return parcel.read(mRawAddr.get(), sizeof(RpcWireAddress));
-}
-
-RpcAddress::~RpcAddress() {}
-RpcAddress::RpcAddress() : mRawAddr(std::make_shared<RpcWireAddress>()) {}
-
-} // namespace android
diff --git a/libs/binder/RpcCertificateUtils.cpp b/libs/binder/RpcCertificateUtils.cpp
new file mode 100644
index 0000000..d91736c
--- /dev/null
+++ b/libs/binder/RpcCertificateUtils.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcCertificateUtils"
+#include <log/log.h>
+
+#include <binder/RpcCertificateUtils.h>
+
+#include "Utils.h"
+
+namespace android {
+
+namespace {
+
+bssl::UniquePtr<X509> fromPem(const std::vector<uint8_t>& cert) {
+ if (cert.size() > std::numeric_limits<int>::max()) return nullptr;
+ bssl::UniquePtr<BIO> certBio(BIO_new_mem_buf(cert.data(), static_cast<int>(cert.size())));
+ return bssl::UniquePtr<X509>(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));
+}
+
+bssl::UniquePtr<X509> fromDer(const std::vector<uint8_t>& cert) {
+ if (cert.size() > std::numeric_limits<long>::max()) return nullptr;
+ const unsigned char* data = cert.data();
+ auto expectedEnd = data + cert.size();
+ bssl::UniquePtr<X509> ret(d2i_X509(nullptr, &data, static_cast<long>(cert.size())));
+ if (data != expectedEnd) {
+ ALOGE("%s: %td bytes remaining!", __PRETTY_FUNCTION__, expectedEnd - data);
+ return nullptr;
+ }
+ return ret;
+}
+
+} // namespace
+
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
+ RpcCertificateFormat format) {
+ switch (format) {
+ case RpcCertificateFormat::PEM:
+ return fromPem(cert);
+ case RpcCertificateFormat::DER:
+ return fromDer(cert);
+ }
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format) {
+ bssl::UniquePtr<BIO> certBio(BIO_new(BIO_s_mem()));
+ switch (format) {
+ case RpcCertificateFormat::PEM: {
+ TEST_AND_RETURN({}, PEM_write_bio_X509(certBio.get(), x509));
+ } break;
+ case RpcCertificateFormat::DER: {
+ TEST_AND_RETURN({}, i2d_X509_bio(certBio.get(), x509));
+ } break;
+ default: {
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+ }
+ }
+ const uint8_t* data;
+ size_t len;
+ TEST_AND_RETURN({}, BIO_mem_contents(certBio.get(), &data, &len));
+ return std::vector<uint8_t>(data, data + len);
+}
+
+} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index ad9ba96..5733993 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -23,6 +23,8 @@
#include <thread>
#include <vector>
+#include <android-base/file.h>
+#include <android-base/hex.h>
#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
@@ -139,20 +141,11 @@
return ret;
}
-std::string RpcServer::getCertificate(CertificateFormat format) {
+std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) {
std::lock_guard<std::mutex> _l(mLock);
return mCtx->getCertificate(format);
}
-status_t RpcServer::addTrustedPeerCertificate(CertificateFormat format, std::string_view cert) {
- std::lock_guard<std::mutex> _l(mLock);
- // Ensure that join thread is not running or shutdown trigger is not set up. In either case,
- // it means there are child threads running. It is invalid to add trusted peer certificates
- // after join thread and/or child threads are running to avoid race condition.
- if (mJoinThreadRunning || mShutdownTrigger != nullptr) return INVALID_OPERATION;
- return mCtx->addTrustedPeerCertificate(format, cert);
-}
-
static void joinRpcServer(sp<RpcServer>&& thiz) {
thiz->join();
}
@@ -290,17 +283,29 @@
}
}
+ std::vector<uint8_t> sessionId;
+ if (status == OK) {
+ if (header.sessionIdSize > 0) {
+ sessionId.resize(header.sessionIdSize);
+ status = client->interruptableReadFully(server->mShutdownTrigger.get(),
+ sessionId.data(), sessionId.size());
+ if (status != OK) {
+ ALOGE("Failed to read session ID for client connecting to RPC server: %s",
+ statusToString(status).c_str());
+ // still need to cleanup before we can return
+ }
+ }
+ }
+
bool incoming = false;
uint32_t protocolVersion = 0;
- RpcAddress sessionId = RpcAddress::zero();
bool requestingNewSession = false;
if (status == OK) {
incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
protocolVersion = std::min(header.version,
server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
- sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
- requestingNewSession = sessionId.isZero();
+ requestingNewSession = sessionId.empty();
if (requestingNewSession) {
RpcNewSessionResponse response{
@@ -342,15 +347,26 @@
return;
}
+ // Uniquely identify session at the application layer. Even if a
+ // client/server use the same certificates, if they create multiple
+ // sessions, we still want to distinguish between them.
+ constexpr size_t kSessionIdSize = 32;
+ sessionId.resize(kSessionIdSize);
size_t tries = 0;
do {
// don't block if there is some entropy issue
if (tries++ > 5) {
- ALOGE("Cannot find new address: %s", sessionId.toString().c_str());
+ ALOGE("Cannot find new address: %s",
+ base::HexString(sessionId.data(), sessionId.size()).c_str());
return;
}
- sessionId = RpcAddress::random(true /*forServer*/);
+ base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
+ if (!base::ReadFully(fd, sessionId.data(), sessionId.size())) {
+ ALOGE("Could not read from /dev/urandom to create session ID");
+ return;
+ }
} while (server->mSessions.end() != server->mSessions.find(sessionId));
session = RpcSession::make();
@@ -370,7 +386,7 @@
auto it = server->mSessions.find(sessionId);
if (it == server->mSessions.end()) {
ALOGE("Cannot add thread, no record of session with ID %s",
- sessionId.toString().c_str());
+ base::HexString(sessionId.data(), sessionId.size()).c_str());
return;
}
session = it->second;
@@ -432,16 +448,17 @@
}
void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) {
- auto id = session->mId;
- LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
- LOG_RPC_DETAIL("Dropping session with address %s", id->toString().c_str());
+ const std::vector<uint8_t>& id = session->mId;
+ LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID");
+ LOG_RPC_DETAIL("Dropping session with address %s",
+ base::HexString(id.data(), id.size()).c_str());
std::lock_guard<std::mutex> _l(mLock);
- auto it = mSessions.find(*id);
+ auto it = mSessions.find(id);
LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s",
- id->toString().c_str());
+ base::HexString(id.data(), id.size()).c_str());
LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %s",
- id->toString().c_str());
+ base::HexString(id.data(), id.size()).c_str());
(void)mSessions.erase(it);
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c57b749..38958c9 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -26,8 +26,10 @@
#include <string_view>
+#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android_runtime/vm.h>
+#include <binder/BpBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
@@ -64,23 +66,12 @@
sp<RpcSession> RpcSession::make() {
// Default is without TLS.
- return make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+ return make(RpcTransportCtxFactoryRaw::make());
}
-sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory,
- std::optional<CertificateFormat> serverCertificateFormat,
- std::optional<std::string> serverCertificate) {
+sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
auto ctx = rpcTransportCtxFactory->newClientCtx();
if (ctx == nullptr) return nullptr;
- LOG_ALWAYS_FATAL_IF(serverCertificateFormat.has_value() != serverCertificate.has_value());
- if (serverCertificateFormat.has_value() && serverCertificate.has_value()) {
- status_t status =
- ctx->addTrustedPeerCertificate(*serverCertificateFormat, *serverCertificate);
- if (status != OK) {
- ALOGE("Cannot add trusted server certificate: %s", statusToString(status).c_str());
- return nullptr;
- }
- }
return sp<RpcSession>::make(std::move(ctx));
}
@@ -143,7 +134,7 @@
}
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
- return setupClient([&](const RpcAddress& sessionId, bool incoming) -> status_t {
+ return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
// std::move'd from fd becomes -1 (!ok())
if (!fd.ok()) {
fd = request();
@@ -201,7 +192,7 @@
if (wait) {
LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
- mShutdownListener->waitForShutdown(_l);
+ mShutdownListener->waitForShutdown(_l, sp<RpcSession>::fromExisting(this));
LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed");
}
@@ -225,7 +216,11 @@
sp<RpcSession>::fromExisting(this), reply, flags);
}
-status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+status_t RpcSession::sendDecStrong(const BpBinder* binder) {
+ return sendDecStrong(binder->getPrivateAccessor().rpcAddress());
+}
+
+status_t RpcSession::sendDecStrong(uint64_t address) {
ExclusiveConnection connection;
status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
ConnectionUse::CLIENT_REFCOUNT, &connection);
@@ -244,29 +239,30 @@
ConnectionUse::CLIENT, &connection);
if (status != OK) return status;
- mId = RpcAddress::zero();
- status = state()->getSessionId(connection.get(), sp<RpcSession>::fromExisting(this),
- &mId.value());
+ status = state()->getSessionId(connection.get(), sp<RpcSession>::fromExisting(this), &mId);
if (status != OK) return status;
- LOG_RPC_DETAIL("RpcSession %p has id %s", this, mId->toString().c_str());
+ LOG_RPC_DETAIL("RpcSession %p has id %s", this,
+ base::HexString(mId.data(), mId.size()).c_str());
return OK;
}
void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded(
const sp<RpcSession>& session) {
(void)session;
- mShutdown = true;
}
void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() {
mCv.notify_all();
}
-void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock) {
- while (!mShutdown) {
+void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock,
+ const sp<RpcSession>& session) {
+ while (session->mIncomingConnections.size() > 0) {
if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
- ALOGE("Waiting for RpcSession to shut down (1s w/o progress).");
+ ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections "
+ "still.",
+ session->mIncomingConnections.size());
}
}
}
@@ -408,8 +404,8 @@
return server;
}
-status_t RpcSession::setupClient(
- const std::function<status_t(const RpcAddress& sessionId, bool incoming)>& connectAndInit) {
+status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
+ bool incoming)>& connectAndInit) {
{
std::lock_guard<std::mutex> _l(mMutex);
LOG_ALWAYS_FATAL_IF(mOutgoingConnections.size() != 0,
@@ -418,8 +414,7 @@
}
if (auto status = initShutdownTrigger(); status != OK) return status;
- if (status_t status = connectAndInit(RpcAddress::zero(), false /*incoming*/); status != OK)
- return status;
+ if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status;
{
ExclusiveConnection connection;
@@ -460,26 +455,25 @@
// we've already setup one client
for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
- if (status_t status = connectAndInit(mId.value(), false /*incoming*/); status != OK)
- return status;
+ if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
}
for (size_t i = 0; i < mMaxThreads; i++) {
- if (status_t status = connectAndInit(mId.value(), true /*incoming*/); status != OK)
- return status;
+ if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status;
}
return OK;
}
status_t RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
- return setupClient([&](const RpcAddress& sessionId, bool incoming) {
+ return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) {
return setupOneSocketConnection(addr, sessionId, incoming);
});
}
status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
- const RpcAddress& sessionId, bool incoming) {
+ const std::vector<uint8_t>& sessionId,
+ bool incoming) {
for (size_t tries = 0; tries < 5; tries++) {
if (tries > 0) usleep(10000);
@@ -537,7 +531,7 @@
return UNKNOWN_ERROR;
}
-status_t RpcSession::initAndAddConnection(unique_fd fd, const RpcAddress& sessionId,
+status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_t>& sessionId,
bool incoming) {
LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr);
auto server = mCtx->newTransport(std::move(fd), mShutdownTrigger.get());
@@ -548,13 +542,20 @@
LOG_RPC_DETAIL("Socket at client with RpcTransport %p", server.get());
+ if (sessionId.size() > std::numeric_limits<uint16_t>::max()) {
+ ALOGE("Session ID too big %zu", sessionId.size());
+ return BAD_VALUE;
+ }
+
RpcConnectionHeader header{
.version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
.options = 0,
+ .sessionIdSize = static_cast<uint16_t>(sessionId.size()),
};
- memcpy(&header.sessionId, &sessionId.viewRawEmbedded(), sizeof(RpcWireAddress));
- if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
+ if (incoming) {
+ header.options |= RPC_CONNECTION_OPTION_INCOMING;
+ }
auto sendHeaderStatus =
server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header));
@@ -564,6 +565,18 @@
return sendHeaderStatus;
}
+ if (sessionId.size() > 0) {
+ auto sendSessionIdStatus =
+ server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
+ sessionId.size());
+ if (sendSessionIdStatus != OK) {
+ ALOGE("Could not write session ID ('%s') to socket: %s",
+ base::HexString(sessionId.data(), sessionId.size()).c_str(),
+ statusToString(sendSessionIdStatus).c_str());
+ return sendSessionIdStatus;
+ }
+ }
+
LOG_RPC_DETAIL("Socket at client: header sent");
if (incoming) {
@@ -636,7 +649,7 @@
}
bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListener>& eventListener,
- const RpcAddress& sessionId) {
+ const std::vector<uint8_t>& sessionId) {
LOG_ALWAYS_FATAL_IF(mForServer != nullptr);
LOG_ALWAYS_FATAL_IF(server == nullptr);
LOG_ALWAYS_FATAL_IF(mEventListener != nullptr);
@@ -697,7 +710,7 @@
return false;
}
-std::string RpcSession::getCertificate(CertificateFormat format) {
+std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) {
return mCtx->getCertificate(format);
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b58f1b3..11a083a 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -52,11 +52,11 @@
RpcState::~RpcState() {}
status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
- RpcAddress* outAddress) {
+ uint64_t* outAddress) {
bool isRemote = binder->remoteBinder();
bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
- if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
+ if (isRpc && binder->remoteBinder()->getPrivateAccessor().rpcSession() != session) {
// We need to be able to send instructions over the socket for how to
// connect to a different server, and we also need to let the host
// process know that this is happening.
@@ -84,12 +84,10 @@
for (auto& [addr, node] : mNodeForAddress) {
if (binder == node.binder) {
if (isRpc) {
- const RpcAddress& actualAddr =
- binder->remoteBinder()->getPrivateAccessorForId().rpcAddress();
- // TODO(b/182939933): this is only checking integrity of data structure
- // a different data structure doesn't need this
- LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch");
- LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch");
+ // check integrity of data structure
+ uint64_t actualAddr = binder->remoteBinder()->getPrivateAccessor().rpcAddress();
+ LOG_ALWAYS_FATAL_IF(addr != actualAddr, "Address mismatch %" PRIu64 " vs %" PRIu64,
+ addr, actualAddr);
}
node.timesSent++;
node.sentRef = binder; // might already be set
@@ -101,8 +99,29 @@
bool forServer = session->server() != nullptr;
- for (size_t tries = 0; tries < 5; tries++) {
- auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::random(forServer),
+ // arbitrary limit for maximum number of nodes in a process (otherwise we
+ // might run out of addresses)
+ if (mNodeForAddress.size() > 100000) {
+ return NO_MEMORY;
+ }
+
+ while (true) {
+ RpcWireAddress address{
+ .options = RPC_WIRE_ADDRESS_OPTION_CREATED,
+ .address = mNextId,
+ };
+ if (forServer) {
+ address.options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
+ }
+
+ // avoid ubsan abort
+ if (mNextId >= std::numeric_limits<uint32_t>::max()) {
+ mNextId = 0;
+ } else {
+ mNextId++;
+ }
+
+ auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address),
BinderNode{
.binder = binder,
.timesSent = 1,
@@ -112,18 +131,10 @@
*outAddress = it->first;
return OK;
}
-
- // well, we don't have visibility into the header here, but still
- static_assert(sizeof(RpcWireAddress) == 40, "this log needs updating");
- ALOGW("2**256 is 1e77. If you see this log, you probably have some entropy issue, or maybe "
- "you witness something incredible!");
}
-
- ALOGE("Unable to create an address in order to send out %p", binder.get());
- return WOULD_BLOCK;
}
-status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address,
+status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t address,
sp<IBinder>* out) {
// ensure that: if we want to use addresses for something else in the future (for
// instance, allowing transitive binder sends), that we don't accidentally
@@ -133,8 +144,11 @@
// if we communicate with a binder, it could always be proxying
// information. However, we want to make sure that isn't done on accident
// by a client.
- if (!address.isRecognizedType()) {
- ALOGE("Address is of an unknown type, rejecting: %s", address.toString().c_str());
+ RpcWireAddress addr = RpcWireAddress::fromRaw(address);
+ constexpr uint32_t kKnownOptions =
+ RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
+ if (addr.options & ~kKnownOptions) {
+ ALOGE("Address is of an unknown type, rejecting: %" PRIu64, address);
return BAD_VALUE;
}
@@ -159,9 +173,9 @@
// we don't know about this binder, so the other side of the connection
// should have created it.
- if (address.isForServer() == !!session->server()) {
- ALOGE("Server received unrecognized address which we should own the creation of %s.",
- address.toString().c_str());
+ if ((addr.options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER) == !!session->server()) {
+ ALOGE("Server received unrecognized address which we should own the creation of %" PRIu64,
+ address);
return BAD_VALUE;
}
@@ -170,7 +184,7 @@
// Currently, all binders are assumed to be part of the same session (no
// device global binders in the RPC world).
- it->second.binder = *out = BpBinder::create(session, it->first);
+ it->second.binder = *out = BpBinder::PrivateAccessor::create(session, it->first);
it->second.timesRecd = 1;
return OK;
}
@@ -241,9 +255,8 @@
desc = "(null)";
}
- ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s",
- node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(),
- desc);
+ ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s",
+ node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc);
}
ALOGE("END DUMP OF RpcState");
}
@@ -360,8 +373,8 @@
data.markForRpc(session);
Parcel reply;
- status_t status = transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT,
- data, session, &reply, 0);
+ status_t status =
+ transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_ROOT, data, session, &reply, 0);
if (status != OK) {
ALOGE("Error getting root object: %s", statusToString(status).c_str());
return nullptr;
@@ -376,9 +389,8 @@
data.markForRpc(session);
Parcel reply;
- status_t status =
- transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS,
- data, session, &reply, 0);
+ status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+ session, &reply, 0);
if (status != OK) {
ALOGE("Error getting max threads: %s", statusToString(status).c_str());
return status;
@@ -397,20 +409,19 @@
}
status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, RpcAddress* sessionIdOut) {
+ const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut) {
Parcel data;
data.markForRpc(session);
Parcel reply;
- status_t status =
- transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID,
- data, session, &reply, 0);
+ status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+ session, &reply, 0);
if (status != OK) {
ALOGE("Error getting session ID: %s", statusToString(status).c_str());
return status;
}
- return sessionIdOut->readFromParcel(reply);
+ return reply.readByteVector(sessionIdOut);
}
status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection,
@@ -426,26 +437,26 @@
return BAD_TYPE;
}
- RpcAddress address = RpcAddress::zero();
+ uint64_t address;
if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status;
return transactAddress(connection, address, code, data, session, reply, flags);
}
status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connection,
- const RpcAddress& address, uint32_t code, const Parcel& data,
+ uint64_t address, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
LOG_ALWAYS_FATAL_IF(!data.isForRpc());
LOG_ALWAYS_FATAL_IF(data.objectsCount() != 0);
uint64_t asyncNumber = 0;
- if (!address.isZero()) {
+ if (address != 0) {
std::unique_lock<std::mutex> _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(address);
- LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s",
- address.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
+ "Sending transact on unknown address %" PRIu64, address);
if (flags & IBinder::FLAG_ONEWAY) {
asyncNumber = it->second.asyncNumber;
@@ -466,8 +477,9 @@
.command = RPC_COMMAND_TRANSACT,
.bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()),
};
+
RpcWireTransaction transaction{
- .address = address.viewRawEmbedded(),
+ .address = RpcWireAddress::fromRaw(address),
.code = code,
.flags = flags,
.asyncNumber = asyncNumber,
@@ -557,15 +569,14 @@
}
status_t RpcState::sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcAddress& addr) {
+ const sp<RpcSession>& session, uint64_t addr) {
{
std::lock_guard<std::mutex> _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
- LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s",
- addr.toString().c_str());
- LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s",
- addr.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
+ "Sending dec strong on unknown address %" PRIu64, addr);
+ LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %" PRIu64, addr);
it->second.timesRecd--;
LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
@@ -579,8 +590,7 @@
if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd));
status != OK)
return status;
- if (status_t status = rpcSend(connection, session, "dec ref body", &addr.viewRawEmbedded(),
- sizeof(RpcWireAddress));
+ if (status_t status = rpcSend(connection, session, "dec ref body", &addr, sizeof(addr));
status != OK)
return status;
return OK;
@@ -685,14 +695,12 @@
}
RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data());
- // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress,
- // maybe add an RpcAddress 'view' if the type remains 'heavy'
- auto addr = RpcAddress::fromRawEmbedded(&transaction->address);
+ uint64_t addr = RpcWireAddress::toRaw(transaction->address);
bool oneway = transaction->flags & IBinder::FLAG_ONEWAY;
status_t replyStatus = OK;
sp<IBinder> target;
- if (!addr.isZero()) {
+ if (addr != 0) {
if (!targetRef) {
replyStatus = onBinderEntering(session, addr, &target);
} else {
@@ -708,21 +716,21 @@
// (any binder which is being transacted on should be holding a
// strong ref count), so in either case, terminating the
// session.
- ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
- addr.toString().c_str());
+ ALOGE("While transacting, binder has been deleted at address %" PRIu64 ". Terminating!",
+ addr);
(void)session->shutdownAndWait(false);
replyStatus = BAD_VALUE;
} else if (target->localBinder() == nullptr) {
- ALOGE("Unknown binder address or non-local binder, not address %s. Terminating!",
- addr.toString().c_str());
+ ALOGE("Unknown binder address or non-local binder, not address %" PRIu64
+ ". Terminating!",
+ addr);
(void)session->shutdownAndWait(false);
replyStatus = BAD_VALUE;
} else if (oneway) {
std::unique_lock<std::mutex> _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it->second.binder.promote() != target) {
- ALOGE("Binder became invalid during transaction. Bad client? %s",
- addr.toString().c_str());
+ ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr);
replyStatus = BAD_VALUE;
} else if (transaction->asyncNumber != it->second.asyncNumber) {
// we need to process some other asynchronous transaction
@@ -734,8 +742,8 @@
});
size_t numPending = it->second.asyncTodo.size();
- LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s (%zu pending)",
- transaction->asyncNumber, addr.toString().c_str(), numPending);
+ LOG_RPC_DETAIL("Enqueuing %" PRIu64 " on %" PRIu64 " (%zu pending)",
+ transaction->asyncNumber, addr, numPending);
constexpr size_t kArbitraryOnewayCallTerminateLevel = 10000;
constexpr size_t kArbitraryOnewayCallWarnLevel = 1000;
@@ -792,7 +800,7 @@
// for client connections, this should always report the value
// originally returned from the server, so this is asserting
// that it exists
- replyStatus = session->mId.value().writeToParcel(&reply);
+ replyStatus = reply.writeByteVector(session->mId);
break;
}
default: {
@@ -820,8 +828,8 @@
ALOGW("Oneway call failed with error: %d", replyStatus);
}
- LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber,
- addr.toString().c_str());
+ LOG_RPC_DETAIL("Processed async transaction %" PRIu64 " on %" PRIu64,
+ transaction->asyncNumber, addr);
// Check to see if there is another asynchronous transaction to process.
// This behavior differs from binder behavior, since in the binder
@@ -847,8 +855,8 @@
if (it->second.asyncTodo.size() == 0) return OK;
if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
- LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s",
- it->second.asyncNumber, addr.toString().c_str());
+ LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
+ it->second.asyncNumber, addr);
// justification for const_cast (consider avoiding priority_queue):
// - AsyncTodo operator< doesn't depend on 'data' or 'ref' objects
@@ -904,7 +912,7 @@
status != OK)
return status;
- if (command.bodySize < sizeof(RpcWireAddress)) {
+ if (command.bodySize != sizeof(RpcWireAddress)) {
ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
sizeof(RpcWireAddress), command.bodySize);
(void)session->shutdownAndWait(false);
@@ -912,31 +920,32 @@
}
RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data());
- // TODO(b/182939933): heap allocation just for lookup
- auto addr = RpcAddress::fromRawEmbedded(address);
+ uint64_t addr = RpcWireAddress::toRaw(*address);
+
std::unique_lock<std::mutex> _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
- ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
+ ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr);
return OK;
}
sp<IBinder> target = it->second.binder.promote();
if (target == nullptr) {
- ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!",
- addr.toString().c_str());
+ ALOGE("While requesting dec strong, binder has been deleted at address %" PRIu64
+ ". Terminating!",
+ addr);
_l.unlock();
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
if (it->second.timesSent == 0) {
- ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str());
+ ALOGE("No record of sending binder, but requested decStrong: %" PRIu64, addr);
return OK;
}
- LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s",
- addr.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64,
+ addr);
it->second.timesSent--;
sp<IBinder> tempHold = tryEraseNode(it);
@@ -946,7 +955,7 @@
return OK;
}
-sp<IBinder> RpcState::tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it) {
+sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) {
sp<IBinder> ref;
if (it->second.timesSent == 0) {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 1446eec..dcfb569 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -73,17 +73,17 @@
status_t getMaxThreads(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, size_t* maxThreadsOut);
status_t getSessionId(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, RpcAddress* sessionIdOut);
+ const sp<RpcSession>& session, std::vector<uint8_t>* sessionIdOut);
[[nodiscard]] status_t transact(const sp<RpcSession::RpcConnection>& connection,
const sp<IBinder>& address, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
[[nodiscard]] status_t transactAddress(const sp<RpcSession::RpcConnection>& connection,
- const RpcAddress& address, uint32_t code,
- const Parcel& data, const sp<RpcSession>& session,
- Parcel* reply, uint32_t flags);
+ uint64_t address, uint32_t code, const Parcel& data,
+ const sp<RpcSession>& session, Parcel* reply,
+ uint32_t flags);
[[nodiscard]] status_t sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, const RpcAddress& address);
+ const sp<RpcSession>& session, uint64_t address);
enum class CommandType {
ANY,
@@ -99,15 +99,15 @@
* ownership to the outgoing binder.
*/
[[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
- RpcAddress* outAddress);
+ uint64_t* outAddress);
/**
* Called by Parcel for incoming binders. This either returns the refcount
* to the process, if this process already has one, or it takes ownership of
* that refcount
*/
- [[nodiscard]] status_t onBinderEntering(const sp<RpcSession>& session,
- const RpcAddress& address, sp<IBinder>* out);
+ [[nodiscard]] status_t onBinderEntering(const sp<RpcSession>& session, uint64_t address,
+ sp<IBinder>* out);
size_t countBinders();
void dump();
@@ -221,15 +221,16 @@
// happens, and there is a strong reference to the binder kept by
// binderNode, this returns that strong reference, so that it can be
// dropped after any locks are removed.
- sp<IBinder> tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it);
+ sp<IBinder> tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it);
// true - success
// false - session shutdown, halt
[[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node);
std::mutex mNodeMutex;
bool mTerminated = false;
+ uint32_t mNextId = 0;
// binders known by both sides of a session
- std::map<RpcAddress, BinderNode> mNodeForAddress;
+ std::map<uint64_t, BinderNode> mNodeForAddress;
};
} // namespace android
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 930df12..41f4a9f 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -35,20 +35,6 @@
class RpcTransportRaw : public RpcTransport {
public:
explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
- Result<size_t> send(const void* buf, size_t size) {
- ssize_t ret = TEMP_FAILURE_RETRY(::send(mSocket.get(), buf, size, MSG_NOSIGNAL));
- if (ret < 0) {
- return ErrnoError() << "send()";
- }
- return ret;
- }
- Result<size_t> recv(void* buf, size_t size) {
- ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_NOSIGNAL));
- if (ret < 0) {
- return ErrnoError() << "recv()";
- }
- return ret;
- }
Result<size_t> peek(void *buf, size_t size) override {
ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
if (ret < 0) {
@@ -65,15 +51,17 @@
status_t status;
while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLOUT)) == OK) {
- auto writeSize = this->send(buffer, end - buffer);
- if (!writeSize.ok()) {
- LOG_RPC_DETAIL("RpcTransport::send(): %s", writeSize.error().message().c_str());
- return writeSize.error().code() == 0 ? UNKNOWN_ERROR : -writeSize.error().code();
+ ssize_t writeSize =
+ TEMP_FAILURE_RETRY(::send(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+ if (writeSize < 0) {
+ int savedErrno = errno;
+ LOG_RPC_DETAIL("RpcTransport send(): %s", strerror(savedErrno));
+ return -savedErrno;
}
- if (*writeSize == 0) return DEAD_OBJECT;
+ if (writeSize == 0) return DEAD_OBJECT;
- buffer += *writeSize;
+ buffer += writeSize;
if (buffer == end) return OK;
}
return status;
@@ -87,15 +75,17 @@
status_t status;
while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLIN)) == OK) {
- auto readSize = this->recv(buffer, end - buffer);
- if (!readSize.ok()) {
- LOG_RPC_DETAIL("RpcTransport::recv(): %s", readSize.error().message().c_str());
- return readSize.error().code() == 0 ? UNKNOWN_ERROR : -readSize.error().code();
+ ssize_t readSize =
+ TEMP_FAILURE_RETRY(::recv(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+ if (readSize < 0) {
+ int savedErrno = errno;
+ LOG_RPC_DETAIL("RpcTransport recv(): %s", strerror(savedErrno));
+ return -savedErrno;
}
- if (*readSize == 0) return DEAD_OBJECT; // EOF
+ if (readSize == 0) return DEAD_OBJECT; // EOF
- buffer += *readSize;
+ buffer += readSize;
if (buffer == end) return OK;
}
return status;
@@ -111,8 +101,7 @@
std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const {
return std::make_unique<RpcTransportRaw>(std::move(fd));
}
- std::string getCertificate(CertificateFormat) const override { return {}; }
- status_t addTrustedPeerCertificate(CertificateFormat, std::string_view) override { return OK; }
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
};
} // namespace
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index e6cb04e..23088ad 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -22,10 +22,12 @@
#include <openssl/bn.h>
#include <openssl/ssl.h>
+#include <binder/RpcCertificateUtils.h>
#include <binder/RpcTransportTls.h>
#include "FdTrigger.h"
#include "RpcState.h"
+#include "Utils.h"
#define SHOULD_LOG_TLS_DETAIL false
@@ -35,14 +37,6 @@
#define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
#endif
-#define TEST_AND_RETURN(value, expr) \
- do { \
- if (!(expr)) { \
- ALOGE("Failed to call: %s", #expr); \
- return value; \
- } \
- } while (0)
-
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
@@ -325,8 +319,6 @@
private:
android::base::unique_fd mSocket;
Ssl mSsl;
-
- static status_t isTriggered(FdTrigger* fdTrigger);
};
// Error code is errno.
@@ -347,15 +339,6 @@
return ret;
}
-status_t RpcTransportTls::isTriggered(FdTrigger* fdTrigger) {
- auto ret = fdTrigger->isTriggeredPolled();
- if (!ret.ok()) {
- ALOGE("%s: %s", __PRETTY_FUNCTION__, ret.error().message().c_str());
- return ret.error().code() == 0 ? UNKNOWN_ERROR : -ret.error().code();
- }
- return OK;
-}
-
status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
size_t size) {
auto buffer = reinterpret_cast<const uint8_t*>(data);
@@ -365,7 +348,7 @@
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
- if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+ if (fdTrigger->isTriggered()) return -ECANCELED;
while (buffer < end) {
size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
@@ -396,7 +379,7 @@
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
- if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+ if (fdTrigger->isTriggered()) return -ECANCELED;
while (buffer < end) {
size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
@@ -453,31 +436,54 @@
public:
template <typename Impl,
typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>>
- static std::unique_ptr<RpcTransportCtxTls> create();
+ static std::unique_ptr<RpcTransportCtxTls> create(
+ std::shared_ptr<RpcCertificateVerifier> verifier);
std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
FdTrigger* fdTrigger) const override;
- std::string getCertificate(CertificateFormat) const override;
- status_t addTrustedPeerCertificate(CertificateFormat, std::string_view cert) override;
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override;
protected:
+ static ssl_verify_result_t sslCustomVerify(SSL* ssl, uint8_t* outAlert);
virtual void preHandshake(Ssl* ssl) const = 0;
bssl::UniquePtr<SSL_CTX> mCtx;
+ std::shared_ptr<RpcCertificateVerifier> mCertVerifier;
};
-std::string RpcTransportCtxTls::getCertificate(CertificateFormat) const {
- // TODO(b/195166979): return certificate here
- return {};
+std::vector<uint8_t> RpcTransportCtxTls::getCertificate(RpcCertificateFormat format) const {
+ X509* x509 = SSL_CTX_get0_certificate(mCtx.get()); // does not own
+ return serializeCertificate(x509, format);
}
-status_t RpcTransportCtxTls::addTrustedPeerCertificate(CertificateFormat, std::string_view) {
- // TODO(b/195166979): set certificate here
- return OK;
+// Verify by comparing the leaf of peer certificate with every certificate in
+// mTrustedPeerCertificates. Does not support certificate chains.
+ssl_verify_result_t RpcTransportCtxTls::sslCustomVerify(SSL* ssl, uint8_t* outAlert) {
+ LOG_ALWAYS_FATAL_IF(outAlert == nullptr);
+ const char* logPrefix = SSL_is_server(ssl) ? "Server" : "Client";
+
+ bssl::UniquePtr<X509> peerCert(SSL_get_peer_certificate(ssl)); // Does not set error queue
+ LOG_ALWAYS_FATAL_IF(peerCert == nullptr,
+ "%s: libssl should not ask to verify non-existing cert", logPrefix);
+
+ auto ctx = SSL_get_SSL_CTX(ssl); // Does not set error queue
+ LOG_ALWAYS_FATAL_IF(ctx == nullptr);
+ // void* -> RpcTransportCtxTls*
+ auto rpcTransportCtxTls = reinterpret_cast<RpcTransportCtxTls*>(SSL_CTX_get_app_data(ctx));
+ LOG_ALWAYS_FATAL_IF(rpcTransportCtxTls == nullptr);
+
+ status_t verifyStatus = rpcTransportCtxTls->mCertVerifier->verify(peerCert.get(), outAlert);
+ if (verifyStatus == OK) {
+ return ssl_verify_ok;
+ }
+ LOG_TLS_DETAIL("%s: Failed to verify client: status = %s, alert = %s", logPrefix,
+ statusToString(verifyStatus).c_str(), SSL_alert_desc_string_long(*outAlert));
+ return ssl_verify_invalid;
}
// Common implementation for creating server and client contexts. The child class, |Impl|, is
// provided as a template argument so that this function can initialize an |Impl| object.
template <typename Impl, typename>
-std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create() {
+std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create(
+ std::shared_ptr<RpcCertificateVerifier> verifier) {
bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
TEST_AND_RETURN(nullptr, ctx != nullptr);
@@ -488,10 +494,10 @@
TEST_AND_RETURN(nullptr, SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get()));
TEST_AND_RETURN(nullptr, SSL_CTX_use_certificate(ctx.get(), cert.get()));
- // TODO(b/195166979): peer should send certificate in a different channel, and this class
- // should verify it here.
- SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER,
- [](SSL*, uint8_t*) -> ssl_verify_result_t { return ssl_verify_ok; });
+ // Enable two-way authentication by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT on server.
+ // Client ignores SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
+ SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ sslCustomVerify);
// Require at least TLS 1.3
TEST_AND_RETURN(nullptr, SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION));
@@ -501,7 +507,10 @@
}
auto ret = std::make_unique<Impl>();
+ // RpcTransportCtxTls* -> void*
+ TEST_AND_RETURN(nullptr, SSL_CTX_set_app_data(ctx.get(), reinterpret_cast<void*>(ret.get())));
ret->mCtx = std::move(ctx);
+ ret->mCertVerifier = std::move(verifier);
return ret;
}
@@ -533,19 +542,25 @@
} // namespace
std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
- return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>();
+ return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier);
}
std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newClientCtx() const {
- return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>();
+ return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(mCertVerifier);
}
const char* RpcTransportCtxFactoryTls::toCString() const {
return "tls";
}
-std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make() {
- return std::unique_ptr<RpcTransportCtxFactoryTls>(new RpcTransportCtxFactoryTls());
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make(
+ std::shared_ptr<RpcCertificateVerifier> verifier) {
+ if (verifier == nullptr) {
+ ALOGE("%s: Must provide a certificate verifier", __PRETTY_FUNCTION__);
+ return nullptr;
+ }
+ return std::unique_ptr<RpcTransportCtxFactoryTls>(
+ new RpcTransportCtxFactoryTls(std::move(verifier)));
}
} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index c73d8c2..a87aa07 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -20,17 +20,23 @@
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wpadded"
-enum : uint8_t {
- RPC_CONNECTION_OPTION_INCOMING = 0x1, // default is outgoing
-};
+constexpr uint8_t RPC_CONNECTION_OPTION_INCOMING = 0x1; // default is outgoing
-constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
-constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_FOR_SERVER = 1 << 1;
+constexpr uint32_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
+constexpr uint32_t RPC_WIRE_ADDRESS_OPTION_FOR_SERVER = 1 << 1;
struct RpcWireAddress {
- uint64_t options;
- uint8_t address[32];
+ uint32_t options;
+ uint32_t address;
+
+ static inline RpcWireAddress fromRaw(uint64_t raw) {
+ return *reinterpret_cast<RpcWireAddress*>(&raw);
+ }
+ static inline uint64_t toRaw(RpcWireAddress addr) {
+ return *reinterpret_cast<uint64_t*>(&addr);
+ }
};
+static_assert(sizeof(RpcWireAddress) == sizeof(uint64_t));
/**
* This is sent to an RpcServer in order to request a new connection is created,
@@ -38,11 +44,13 @@
*/
struct RpcConnectionHeader {
uint32_t version; // maximum supported by caller
- uint8_t reserver0[4];
- RpcWireAddress sessionId;
uint8_t options;
- uint8_t reserved1[7];
+ uint8_t reservered[9];
+ // Follows is sessionIdSize bytes.
+ // if size is 0, this is requesting a new session.
+ uint16_t sessionIdSize;
};
+static_assert(sizeof(RpcConnectionHeader) == 16);
/**
* In response to an RpcConnectionHeader which corresponds to a new session,
@@ -52,6 +60,7 @@
uint32_t version; // maximum supported by callee <= maximum supported by caller
uint8_t reserved[4];
};
+static_assert(sizeof(RpcNewSessionResponse) == 8);
#define RPC_CONNECTION_INIT_OKAY "cci"
@@ -64,6 +73,7 @@
char msg[4];
uint8_t reserved[4];
};
+static_assert(sizeof(RpcOutgoingConnectionInit) == 8);
enum : uint32_t {
/**
@@ -105,6 +115,7 @@
uint32_t reserved[2];
};
+static_assert(sizeof(RpcWireHeader) == 16);
struct RpcWireTransaction {
RpcWireAddress address;
@@ -117,11 +128,13 @@
uint8_t data[];
};
+static_assert(sizeof(RpcWireTransaction) == 40);
struct RpcWireReply {
int32_t status; // transact return
uint8_t data[];
};
+static_assert(sizeof(RpcWireReply) == 4);
#pragma clang diagnostic pop
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 59334b7..27cc563 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -74,12 +74,12 @@
result->toString().c_str());
return std::nullopt;
}
- if (!result->stderr.empty()) {
+ if (!result->stderrStr.empty()) {
LOG_HOST("`adb forward tcp:0 tcp:%d` writes to stderr: %s", devicePort,
- result->stderr.c_str());
+ result->stderrStr.c_str());
}
- unsigned int hostPort = parsePortNumber(result->stdout, "host port");
+ unsigned int hostPort = parsePortNumber(result->stdoutStr, "host port");
if (hostPort == 0) return std::nullopt;
return AdbForwarder(hostPort);
@@ -105,9 +105,9 @@
result->toString().c_str());
return;
}
- if (!result->stderr.empty()) {
+ if (!result->stderrStr.empty()) {
LOG_HOST("`adb forward --remove tcp:%d` writes to stderr: %s", *mPort,
- result->stderr.c_str());
+ result->stderrStr.c_str());
}
LOG_HOST("Successfully run `adb forward --remove tcp:%d`", *mPort);
@@ -139,8 +139,8 @@
ALOGE("Command exits with: %s", result->toString().c_str());
return nullptr;
}
- if (!result->stderr.empty()) {
- LOG_HOST("servicedispatcher writes to stderr: %s", result->stderr.c_str());
+ if (!result->stderrStr.empty()) {
+ LOG_HOST("servicedispatcher writes to stderr: %s", result->stderrStr.c_str());
}
if (!result->stdoutEndsWithNewLine()) {
@@ -148,7 +148,7 @@
return nullptr;
}
- unsigned int devicePort = parsePortNumber(result->stdout, "device port");
+ unsigned int devicePort = parsePortNumber(result->stdoutStr, "device port");
if (devicePort == 0) return nullptr;
auto forwardResult = AdbForwarder::forward(devicePort);
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index 1e383da..ff2fad8 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -19,6 +19,15 @@
#include <android-base/result.h>
#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+#define TEST_AND_RETURN(value, expr) \
+ do { \
+ if (!(expr)) { \
+ ALOGE("Failed to call: %s", #expr); \
+ return value; \
+ } \
+ } while (0)
namespace android {
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index d121ce2..52b8f69 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -63,7 +63,7 @@
if (res.exitCode) os << "code=" << *res.exitCode;
if (res.signal) os << "signal=" << *res.signal;
if (res.pid) os << ", pid=" << *res.pid;
- return os << ", stdout=" << res.stdout << ", stderr=" << res.stderr;
+ return os << ", stdout=" << res.stdoutStr << ", stderr=" << res.stderrStr;
}
std::string CommandResult::toString() const {
@@ -142,9 +142,9 @@
int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
if (pollRet == -1) return android::base::ErrnoError() << "poll()";
- if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdout))
+ if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr))
return android::base::ErrnoError() << "read(stdout)";
- if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderr))
+ if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr))
return android::base::ErrnoError() << "read(stderr)";
if (end && end(ret)) return ret;
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index 0f29f60..98ac4e0 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -43,8 +43,8 @@
std::optional<int32_t> exitCode;
std::optional<int32_t> signal;
std::optional<pid_t> pid;
- std::string stdout;
- std::string stderr;
+ std::string stdoutStr;
+ std::string stderrStr;
android::base::unique_fd outPipe;
android::base::unique_fd errPipe;
@@ -55,15 +55,15 @@
std::swap(exitCode, other.exitCode);
std::swap(signal, other.signal);
std::swap(pid, other.pid);
- std::swap(stdout, other.stdout);
- std::swap(stderr, other.stderr);
+ std::swap(stdoutStr, other.stdoutStr);
+ std::swap(stderrStr, other.stderrStr);
return *this;
}
~CommandResult();
[[nodiscard]] std::string toString() const;
[[nodiscard]] bool stdoutEndsWithNewLine() const {
- return !stdout.empty() && stdout.back() == '\n';
+ return !stdoutStr.empty() && stdoutStr.back() == '\n';
}
private:
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 9dea3b4..793795e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -74,6 +74,8 @@
//
// Indicates whether the process has received any sync calls since last
// freeze (cleared at freeze/unfreeze)
+ // bit 0: received sync transaction after being frozen
+ // bit 1: new pending sync transaction during freezing
//
__u32 sync_recv;
//
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index a6d35c7..c0454b6 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,7 +17,6 @@
#pragma once
#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
#include <utils/threads.h>
@@ -40,9 +39,6 @@
class BpBinder : public IBinder
{
public:
- static sp<BpBinder> create(int32_t handle);
- static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address);
-
/**
* Return value:
* true - this is associated with a socket RpcSession
@@ -117,42 +113,49 @@
KeyedVector<const void*, entry_t> mObjects;
};
- class PrivateAccessorForId {
+ class PrivateAccessor {
private:
friend class BpBinder;
friend class ::android::Parcel;
friend class ::android::ProcessState;
+ friend class ::android::RpcSession;
friend class ::android::RpcState;
- explicit PrivateAccessorForId(const BpBinder* binder) : mBinder(binder) {}
+ explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+
+ static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
+ static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
+ return BpBinder::create(session, address);
+ }
// valid if !isRpcBinder
int32_t binderHandle() const { return mBinder->binderHandle(); }
// valid if isRpcBinder
- const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
+ uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
const BpBinder* mBinder;
};
- const PrivateAccessorForId getPrivateAccessorForId() const {
- return PrivateAccessorForId(this);
- }
+ const PrivateAccessor getPrivateAccessor() const { return PrivateAccessor(this); }
private:
- friend PrivateAccessorForId;
+ friend PrivateAccessor;
friend class sp<BpBinder>;
+ static sp<BpBinder> create(int32_t handle);
+ static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address);
+
struct BinderHandle {
int32_t handle;
};
struct RpcHandle {
sp<RpcSession> session;
- RpcAddress address;
+ uint64_t address;
};
using Handle = std::variant<BinderHandle, RpcHandle>;
int32_t binderHandle() const;
- const RpcAddress& rpcAddress() const;
+ uint64_t rpcAddress() const;
const sp<RpcSession>& rpcSession() const;
explicit BpBinder(Handle&& handle);
@@ -194,6 +197,7 @@
static uint32_t sBinderProxyCountHighWatermark;
static uint32_t sBinderProxyCountLowWatermark;
static bool sBinderProxyThrottleCreate;
+ static std::unordered_map<int32_t,uint32_t> sLastLimitCallbackMap;
};
} // namespace android
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 20a9f36..065e6e3 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -53,6 +53,13 @@
// Provide information about the state of a frozen process
static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received,
bool *async_received);
+
+ // TODO: Remove the above legacy duplicated function in next version
+#ifndef __ANDROID_VNDK__
+ static status_t getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+ uint32_t *async_received);
+#endif
+
sp<ProcessState> process();
status_t clearLastError();
diff --git a/libs/binder/include/binder/RpcAddress.h b/libs/binder/include/binder/RpcAddress.h
deleted file mode 100644
index e428908..0000000
--- a/libs/binder/include/binder/RpcAddress.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-
-#include <utils/Errors.h>
-
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
-namespace android {
-
-class Parcel;
-struct RpcWireAddress;
-
-/**
- * This class represents an identifier across an RPC boundary.
- */
-class RpcAddress {
-public:
- /**
- * The zero address is used for special RPC transactions, but it might also
- * be used in conjunction with readFromParcel.
- */
- static RpcAddress zero();
-
- bool isZero() const;
-
- /**
- * Create a new random address.
- */
- static RpcAddress random(bool forServer);
-
- /**
- * Whether this address was created with 'bool forServer' true
- */
- bool isForServer() const;
-
- /**
- * Whether this address is one that could be created with this version of
- * libbinder.
- */
- bool isRecognizedType() const;
-
- /**
- * Creates a new address as a copy of an embedded object.
- */
- static RpcAddress fromRawEmbedded(const RpcWireAddress* raw);
- const RpcWireAddress& viewRawEmbedded() const;
-
- bool operator<(const RpcAddress& rhs) const;
- std::string toString() const;
-
- status_t writeToParcel(Parcel* parcel) const;
- status_t readFromParcel(const Parcel& parcel);
-
- ~RpcAddress();
-
-private:
- RpcAddress();
-
- std::shared_ptr<RpcWireAddress> mRawAddr;
-};
-
-} // namespace android
diff --git a/libs/binder/include/binder/RpcCertificateFormat.h b/libs/binder/include/binder/RpcCertificateFormat.h
new file mode 100644
index 0000000..bc9d814
--- /dev/null
+++ b/libs/binder/include/binder/RpcCertificateFormat.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Formats for serializing TLS certificate.
+
+#pragma once
+
+#include <string>
+
+namespace android {
+
+enum class RpcCertificateFormat {
+ PEM,
+ DER,
+};
+
+static inline std::string PrintToString(RpcCertificateFormat format) {
+ switch (format) {
+ case RpcCertificateFormat::PEM:
+ return "PEM";
+ case RpcCertificateFormat::DER:
+ return "DER";
+ default:
+ return "<unknown>";
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/rust/src/binder_rpc_unstable.hpp b/libs/binder/include/binder/RpcCertificateVerifier.h
similarity index 60%
copy from libs/binder/rust/src/binder_rpc_unstable.hpp
copy to libs/binder/include/binder/RpcCertificateVerifier.h
index 7932d0f..97af31c 100644
--- a/libs/binder/rust/src/binder_rpc_unstable.hpp
+++ b/libs/binder/include/binder/RpcCertificateVerifier.h
@@ -16,11 +16,17 @@
#pragma once
-extern "C" {
+#include <openssl/ssl.h>
+#include <utils/Errors.h>
-struct AIBinder;
+namespace android {
-bool RunRpcServer(AIBinder* service, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
+// An interface with a function that verifies a peer certificate. It is a wrapper over the custom
+// verify function (see SSL_CTX_set_custom_verify).
+class RpcCertificateVerifier {
+public:
+ virtual ~RpcCertificateVerifier() = default;
+ virtual status_t verify(const X509* peerCert, uint8_t* outAlert) = 0;
+};
-}
+} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index d0e4e27..fb2cf23 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,7 +17,6 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
@@ -136,13 +135,7 @@
/**
* See RpcTransportCtx::getCertificate
*/
- std::string getCertificate(CertificateFormat);
-
- /**
- * See RpcTransportCtx::addTrustedPeerCertificate.
- * Thread-safe. This is only possible before the server is join()-ing.
- */
- status_t addTrustedPeerCertificate(CertificateFormat, std::string_view cert);
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat);
/**
* Runs join() in a background thread. Immediately returns.
@@ -201,7 +194,7 @@
std::map<std::thread::id, std::thread> mConnectingThreads;
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
- std::map<RpcAddress, sp<RpcSession>> mSessions;
+ std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
std::condition_variable mShutdownCv;
};
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index d92af0a..6a29c05 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -17,7 +17,6 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -54,12 +53,10 @@
// Create an RpcSession with default configuration (raw sockets).
static sp<RpcSession> make();
- // Create an RpcSession with the given configuration. |serverCertificateFormat| and
+ // Create an RpcSession with the given configuration. |serverRpcCertificateFormat| and
// |serverCertificate| must have values or be nullopt simultaneously. If they have values, set
// server certificate.
- static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory,
- std::optional<CertificateFormat> serverCertificateFormat,
- std::optional<std::string> serverCertificate);
+ static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
/**
* Set the maximum number of threads allowed to be made (for things like callbacks).
@@ -134,7 +131,7 @@
/**
* See RpcTransportCtx::getCertificate
*/
- std::string getCertificate(CertificateFormat);
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat);
/**
* Shuts down the service.
@@ -154,7 +151,13 @@
[[nodiscard]] status_t transact(const sp<IBinder>& binder, uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
- [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+ /**
+ * Generally, you should not call this, unless you are testing error
+ * conditions, as this is called automatically by BpBinders when they are
+ * deleted (this is also why a raw pointer is used here)
+ */
+ [[nodiscard]] status_t sendDecStrong(const BpBinder* binder);
~RpcSession();
@@ -173,6 +176,8 @@
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
+ [[nodiscard]] status_t sendDecStrong(uint64_t address);
+
class EventListener : public virtual RefBase {
public:
virtual void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
@@ -183,12 +188,12 @@
public:
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
- void waitForShutdown(std::unique_lock<std::mutex>& lock);
+ void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session);
private:
std::condition_variable mCv;
- volatile bool mShutdown = false;
};
+ friend WaitForShutdownListener;
struct RpcConnection : public RefBase {
std::unique_ptr<RpcTransport> rpcTransport;
@@ -222,19 +227,21 @@
static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
[[nodiscard]] status_t setupClient(
- const std::function<status_t(const RpcAddress& sessionId, bool incoming)>&
+ const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
connectAndInit);
[[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
[[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
- const RpcAddress& sessionId, bool incoming);
- [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd, const RpcAddress& sessionId,
+ const std::vector<uint8_t>& sessionId,
+ bool incoming);
+ [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd,
+ const std::vector<uint8_t>& sessionId,
bool incoming);
[[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] status_t addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport,
bool init);
[[nodiscard]] bool setForServer(const wp<RpcServer>& server,
const wp<RpcSession::EventListener>& eventListener,
- const RpcAddress& sessionId);
+ const std::vector<uint8_t>& sessionId);
sp<RpcConnection> assignIncomingConnectionToThisThread(
std::unique_ptr<RpcTransport> rpcTransport);
[[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
@@ -291,7 +298,7 @@
sp<WaitForShutdownListener> mShutdownListener; // used for client sessions
wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client
- std::optional<RpcAddress> mId;
+ std::vector<uint8_t> mId;
std::unique_ptr<FdTrigger> mShutdownTrigger;
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 8d08b34..4fe2324 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -25,15 +25,12 @@
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
+#include <binder/RpcCertificateFormat.h>
+
namespace android {
class FdTrigger;
-enum class CertificateFormat {
- PEM,
- // TODO(b/195166979): support other formats, e.g. DER
-};
-
// Represents a socket connection.
// No thread-safety is guaranteed for these APIs.
class RpcTransport {
@@ -41,7 +38,7 @@
virtual ~RpcTransport() = default;
// replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
- virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+ [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
/**
* Read (or write), but allow to be interrupted by a trigger.
@@ -50,9 +47,10 @@
* OK - succeeded in completely processing 'size'
* error - interrupted (failure or trigger)
*/
- virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
- size_t size) = 0;
- virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf, size_t size) = 0;
+ [[nodiscard]] virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
+ size_t size) = 0;
+ [[nodiscard]] virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf,
+ size_t size) = 0;
protected:
RpcTransport() = default;
@@ -76,19 +74,8 @@
// Implementation details:
// - For raw sockets, this always returns empty string.
// - For TLS, this returns the certificate. See RpcTransportTls for details.
- [[nodiscard]] virtual std::string getCertificate(CertificateFormat format) const = 0;
-
- // Add a trusted peer certificate. Peers presenting this certificate are accepted.
- //
- // Caller must ensure that newTransport() are called after all trusted peer certificates
- // are added. Otherwise, RpcTransport-s created before may not trust peer certificates
- // added later.
- //
- // Implementation details:
- // - For raw sockets, this always returns OK.
- // - For TLS, this adds trusted peer certificate. See RpcTransportTls for details.
- [[nodiscard]] virtual status_t addTrustedPeerCertificate(CertificateFormat format,
- std::string_view cert) = 0;
+ [[nodiscard]] virtual std::vector<uint8_t> getCertificate(
+ RpcCertificateFormat format) const = 0;
protected:
RpcTransportCtx() = default;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
new file mode 100644
index 0000000..08f5eed
--- /dev/null
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+extern "C" {
+
+struct AIBinder;
+
+// Starts an RPC server on a given port and a given root IBinder object.
+// This function sets up the server and joins before returning.
+bool RunRpcServer(AIBinder* service, unsigned int port);
+
+// Starts an RPC server on a given port and a given root IBinder object.
+// This function sets up the server, calls readyCallback with a given param, and
+// then joins before returning.
+bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
+ void* param);
+
+AIBinder* RpcClient(unsigned int cid, unsigned int port);
+
+// Connect to an RPC server with preconnected file descriptors.
+//
+// requestFd should connect to the server and return a valid file descriptor, or
+// -1 if connection fails.
+//
+// param will be passed to requestFd. Callers can use param to pass contexts to
+// the requestFd function.
+AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
+
+}
diff --git a/libs/binder/include_tls/binder/RpcCertificateUtils.h b/libs/binder/include_tls/binder/RpcCertificateUtils.h
new file mode 100644
index 0000000..8d07835
--- /dev/null
+++ b/libs/binder/include_tls/binder/RpcCertificateUtils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Utilities for serializing and deserializing X509 certificates.
+
+#pragma once
+
+#include <vector>
+
+#include <openssl/ssl.h>
+
+#include <binder/RpcCertificateFormat.h>
+
+namespace android {
+
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
+ RpcCertificateFormat format);
+
+std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format);
+
+} // namespace android
diff --git a/libs/binder/include_tls/binder/RpcTransportTls.h b/libs/binder/include_tls/binder/RpcTransportTls.h
index 531aaa9..f26a3e9 100644
--- a/libs/binder/include_tls/binder/RpcTransportTls.h
+++ b/libs/binder/include_tls/binder/RpcTransportTls.h
@@ -18,6 +18,7 @@
#pragma once
+#include <binder/RpcCertificateVerifier.h>
#include <binder/RpcTransport.h>
namespace android {
@@ -25,14 +26,17 @@
// RpcTransportCtxFactory with TLS enabled with self-signed certificate.
class RpcTransportCtxFactoryTls : public RpcTransportCtxFactory {
public:
- static std::unique_ptr<RpcTransportCtxFactory> make();
+ static std::unique_ptr<RpcTransportCtxFactory> make(std::shared_ptr<RpcCertificateVerifier>);
std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
const char* toCString() const override;
private:
- RpcTransportCtxFactoryTls() = default;
+ RpcTransportCtxFactoryTls(std::shared_ptr<RpcCertificateVerifier> verifier)
+ : mCertVerifier(std::move(verifier)){};
+
+ std::shared_ptr<RpcCertificateVerifier> mCertVerifier;
};
} // namespace android
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index bcb13ae..cad55fb 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -15,6 +15,7 @@
*/
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <android/binder_libbinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
@@ -24,10 +25,12 @@
using android::RpcSession;
using android::status_t;
using android::statusToString;
+using android::base::unique_fd;
extern "C" {
-bool RunRpcServer(AIBinder* service, unsigned int port) {
+bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
+ void* param) {
auto server = RpcServer::make();
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
if (status_t status = server->setupVsockServer(port); status != OK) {
@@ -36,6 +39,8 @@
return false;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
+
+ if (readyCallback) readyCallback(param);
server->join();
// Shutdown any open sessions since server failed.
@@ -43,6 +48,10 @@
return true;
}
+bool RunRpcServer(AIBinder* service, unsigned int port) {
+ return RunRpcServerCallback(service, port, nullptr, nullptr);
+}
+
AIBinder* RpcClient(unsigned int cid, unsigned int port) {
auto session = RpcSession::make();
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
@@ -52,4 +61,14 @@
}
return AIBinder_fromPlatformBinder(session->getRootObject());
}
+
+AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
+ auto session = RpcSession::make();
+ auto request = [=] { return unique_fd{requestFd(param)}; };
+ if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ return AIBinder_fromPlatformBinder(session->getRootObject());
+}
}
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index 3921a4d..e856569 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -1,7 +1,9 @@
LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
global:
RunRpcServer;
+ RunRpcServerCallback;
RpcClient;
+ RpcPreconnectedClient;
local:
*;
};
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index b03e24c..9c04e58 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -117,6 +117,9 @@
"30",
],
},
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
tidy: true,
tidy_flags: [
// Only check our headers
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index f88896f..49c7b7c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -373,6 +373,12 @@
return clazz->getInterfaceDescriptorUtf8();
}
+AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() {
+ if (mOnUnlinked != nullptr) {
+ mOnUnlinked(mCookie);
+ }
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
<< " (" << mWho.get_refs() << ")";
@@ -394,7 +400,7 @@
}
AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
- : mOnDied(onDied) {
+ : mOnDied(onDied), mOnUnlinked(nullptr) {
CHECK(onDied != nullptr);
}
@@ -412,10 +418,12 @@
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
sp<TransferDeathRecipient> recipient =
- new TransferDeathRecipient(binder, cookie, this, mOnDied);
+ new TransferDeathRecipient(binder, cookie, this, mOnDied, mOnUnlinked);
status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/);
if (status != STATUS_OK) {
+ // When we failed to link, the destructor of TransferDeathRecipient runs here, which
+ // ensures that mOnUnlinked is called before we return with an error from this method.
return PruneStatusT(status);
}
@@ -448,6 +456,10 @@
return STATUS_NAME_NOT_FOUND;
}
+void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+ mOnUnlinked = onUnlinked;
+}
+
// start of C-API methods
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
@@ -689,6 +701,15 @@
return ret;
}
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+ AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+ if (recipient == nullptr) {
+ return;
+ }
+
+ recipient->setOnUnlinked(onUnlinked);
+}
+
void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) {
if (recipient == nullptr) {
return;
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 3cb95ea..730e51b 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -148,8 +148,14 @@
struct TransferDeathRecipient : ::android::IBinder::DeathRecipient {
TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie,
const ::android::wp<AIBinder_DeathRecipient>& parentRecipient,
- const AIBinder_DeathRecipient_onBinderDied onDied)
- : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {}
+ const AIBinder_DeathRecipient_onBinderDied onDied,
+ const AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+ : mWho(who),
+ mCookie(cookie),
+ mParentRecipient(parentRecipient),
+ mOnDied(onDied),
+ mOnUnlinked(onUnlinked) {}
+ ~TransferDeathRecipient();
void binderDied(const ::android::wp<::android::IBinder>& who) override;
@@ -165,11 +171,13 @@
// This is kept separately from AIBinder_DeathRecipient in case the death recipient is
// deleted while the death notification is fired
const AIBinder_DeathRecipient_onBinderDied mOnDied;
+ const AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
};
explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
+ void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
@@ -180,4 +188,5 @@
std::mutex mDeathRecipientsMutex;
std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients;
AIBinder_DeathRecipient_onBinderDied mOnDied;
+ AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
};
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index c82df83..43533c5 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -227,11 +227,11 @@
* must have a separate way of determining the binder you are talking to is the right type. Must
* be called before any instance of the class is created.
*
- * Available since API level 32.
+ * Available since API level 33.
*
* \param clazz class to disable interface header on.
*/
-void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(32);
+void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(33);
/**
* Creates a new binder object of the appropriate class.
@@ -319,9 +319,9 @@
/**
* Registers for notifications that the associated binder is dead. The same death recipient may be
* associated with multiple different binders. If the binder is local, then no death recipient will
- * be given (since if the local process dies, then no recipient will exist to recieve a
+ * be given (since if the local process dies, then no recipient will exist to receive a
* transaction). The cookie is passed to recipient in the case that this binder dies and can be
- * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath).
+ * null. The exact cookie must also be used to unlink this transaction (see AIBinder_unlinkToDeath).
* This function may return a binder transaction failure. The cookie can be used both for
* identification and holding user data.
*
@@ -348,6 +348,10 @@
* If the binder dies, it will automatically unlink. If the binder is deleted, it will be
* automatically unlinked.
*
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
* Available since API level 29.
*
* \param binder the binder object to remove a previously linked death recipient from.
@@ -568,6 +572,22 @@
typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29);
/**
+ * This function is intended for cleaning up the data in the provided cookie, and it is executed
+ * when the DeathRecipient is unlinked. When the DeathRecipient is unlinked due to a death receipt,
+ * this method is called after the call to onBinderDied.
+ *
+ * This method is called once for each binder that is unlinked. Hence, if the same cookie is passed
+ * to multiple binders, then the caller is responsible for reference counting the cookie.
+ *
+ * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath.
+ *
+ * Available since API level 33.
+ *
+ * \param cookie the cookie passed to AIBinder_linkToDeath.
+ */
+typedef void (*AIBinder_DeathRecipient_onBinderUnlinked)(void* cookie) __INTRODUCED_IN(33);
+
+/**
* Creates a new binder death recipient. This can be attached to multiple different binder objects.
*
* Available since API level 29.
@@ -580,9 +600,47 @@
AIBinder_DeathRecipient_onBinderDied onBinderDied) __INTRODUCED_IN(29);
/**
+ * Set the callback to be called when this DeathRecipient is unlinked from a binder. The callback is
+ * called in the following situations:
+ *
+ * 1. If the binder died, shortly after the call to onBinderDied.
+ * 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
+ * AIBinder_DeathRecipient_delete.
+ * 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
+ *
+ * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
+ * process is aborted before the binder is unlinked.
+ *
+ * Be aware that when the binder is explicitly unlinked, it is not guaranteed that onUnlinked has
+ * been called before the call to AIBinder_unlinkToDeath or AIBinder_DeathRecipient_delete returns.
+ * For example, if the binder dies concurrently with a call to AIBinder_unlinkToDeath, the binder is
+ * not unlinked until after the death notification is delivered, even if AIBinder_unlinkToDeath
+ * returns before that happens.
+ *
+ * This method should be called before linking the DeathRecipient to a binder because the function
+ * pointer is cached. If you change it after linking to a binder, it is unspecified whether the old
+ * binder will call the old or new onUnlinked callback.
+ *
+ * The onUnlinked argument may be null. In this case, no notification is given when the binder is
+ * unlinked.
+ *
+ * Available since API level 33.
+ *
+ * \param recipient the DeathRecipient to set the onUnlinked callback for.
+ * \param onUnlinked the callback to call when a binder is unlinked from recipient.
+ */
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+ AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+ __INTRODUCED_IN(33);
+
+/**
* Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before
* calling this as these will all be automatically unlinked.
*
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
* Available since API level 29.
*
* \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new).
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 527b151..8457581 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1163,6 +1163,45 @@
* \return A parcel which is not related to any IBinder objects.
*/
AParcel* AParcel_create() __INTRODUCED_IN(31);
+
+/**
+ * Marshals the raw bytes of the Parcel to a buffer.
+ *
+ * Available since API level 33.
+ *
+ * The parcel must not contain any binders or file descriptors.
+ *
+ * The data you retrieve here must not be placed in any kind of persistent storage. (on local disk,
+ * across a network, etc). For that, you should use standard serialization or another kind of
+ * general serialization mechanism. The Parcel marshalled representation is highly optimized for
+ * local IPC, and as such does not attempt to maintain compatibility with data created in different
+ * versions of the platform.
+ *
+ * \param parcel The parcel of which to get the data.
+ * \param buffer The buffer to copy the raw bytes to.
+ * \param start The start position in the buffer to copy from.
+ * \param len The size of the data to copy, buffer size must be larger or equal to this.
+ *
+ * \return STATUS_OK on success, STATUS_INVALID_OPERATION if parcel contains binders or file
+ * descriptors. STATUS_BAD_VALUE if the buffer size is less than parcel size.
+ */
+binder_status_t AParcel_marshal(const AParcel* parcel, uint8_t* buffer, size_t start, size_t len)
+ __INTRODUCED_IN(33);
+
+/**
+ * Set the data in the parcel to the raw bytes from the buffer.
+ *
+ * Available since API level 33.
+ *
+ * \param parcel The parcel to set data.
+ * \param buffer The data buffer to set.
+ * \param len The size of the data to set.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_unmarshal(AParcel* parcel, const uint8_t* buffer, size_t len)
+ __INTRODUCED_IN(33);
+
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1975bdc..8605686 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -141,9 +141,12 @@
AParcel_reset;
};
-LIBBINDER_NDK32 { # introduced=32
+LIBBINDER_NDK33 { # introduced=33
global:
AIBinder_Class_disableInterfaceTokenHeader;
+ AIBinder_DeathRecipient_setOnUnlinked;
+ AParcel_marshal;
+ AParcel_unmarshal;
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index b2f21c7..c320e8d 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -673,4 +673,32 @@
return new AParcel(nullptr);
}
+binder_status_t AParcel_marshal(const AParcel* parcel, uint8_t* buffer, size_t start, size_t len) {
+ if (parcel->get()->objectsCount()) {
+ return STATUS_INVALID_OPERATION;
+ }
+ int32_t dataSize = AParcel_getDataSize(parcel);
+ if (len > static_cast<size_t>(dataSize) || start > static_cast<size_t>(dataSize) - len) {
+ return STATUS_BAD_VALUE;
+ }
+ const uint8_t* internalBuffer = parcel->get()->data();
+ memcpy(buffer, internalBuffer + start, len);
+ return STATUS_OK;
+}
+
+binder_status_t AParcel_unmarshal(AParcel* parcel, const uint8_t* buffer, size_t len) {
+ status_t status = parcel->get()->setDataSize(len);
+ if (status != ::android::OK) {
+ return PruneStatusT(status);
+ }
+ parcel->get()->setDataPosition(0);
+
+ void* raw = parcel->get()->writeInplace(len);
+ if (raw == nullptr) {
+ return STATUS_NO_MEMORY;
+ }
+ memcpy(raw, buffer, len);
+ return STATUS_OK;
+}
+
// @END
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 4b36530..499f88e 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -395,9 +395,16 @@
<< "Service failed to shut down.";
}
+struct DeathRecipientCookie {
+ std::function<void(void)>*onDeath, *onUnlink;
+};
void LambdaOnDeath(void* cookie) {
- auto onDeath = static_cast<std::function<void(void)>*>(cookie);
- (*onDeath)();
+ auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+ (*funcs->onDeath)();
+};
+void LambdaOnUnlink(void* cookie) {
+ auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+ (*funcs->onUnlink)();
};
TEST(NdkBinder, DeathRecipient) {
using namespace std::chrono_literals;
@@ -409,26 +416,46 @@
std::mutex deathMutex;
std::condition_variable deathCv;
- bool deathRecieved = false;
+ bool deathReceived = false;
std::function<void(void)> onDeath = [&] {
std::cerr << "Binder died (as requested)." << std::endl;
- deathRecieved = true;
+ deathReceived = true;
deathCv.notify_one();
};
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
+ std::function<void(void)> onUnlink = [&] {
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+ AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
// the binder driver should return this if the service dies during the transaction
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
- std::unique_lock<std::mutex> lock(deathMutex);
- EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
- EXPECT_TRUE(deathRecieved);
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+ EXPECT_TRUE(deathReceived);
+
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_TRUE(wasDeathReceivedFirst);
AIBinder_DeathRecipient_delete(recipient);
AIBinder_decStrong(binder);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 8d27eed..d9d7caf 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -109,7 +109,7 @@
// TODO(b/184872979): remove once the Rust API is created.
rust_bindgen {
name: "libbinder_rpc_unstable_bindgen",
- wrapper_src: "src/binder_rpc_unstable.hpp",
+ wrapper_src: ":libbinder_rpc_unstable_header",
crate_name: "binder_rpc_unstable_bindgen",
source_stem: "bindings",
shared_libs: [
@@ -135,3 +135,23 @@
"libbinder_ndk_sys",
],
}
+
+rust_test {
+ name: "libbinder_ndk_bindgen_test",
+ srcs: [":libbinder_ndk_bindgen"],
+ crate_name: "binder_ndk_bindgen",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_test {
+ name: "libbinder_rpc_unstable_bindgen_test",
+ srcs: [":libbinder_rpc_unstable_bindgen"],
+ crate_name: "binder_rpc_unstable_bindgen",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index dd0c7b8..41ceee5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -152,20 +152,46 @@
/// available.
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
+ /// Create a Parcel that can be used with `submit_transact`.
+ fn prepare_transact(&self) -> Result<Parcel>;
+
/// Perform a generic operation with the object.
///
+ /// The provided [`Parcel`] must have been created by a call to
+ /// `prepare_transact` on the same binder.
+ ///
+ /// # Arguments
+ ///
+ /// * `code` - Transaction code for the operation.
+ /// * `data` - [`Parcel`] with input data.
+ /// * `flags` - Transaction flags, e.g. marking the transaction as
+ /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
+ fn submit_transact(
+ &self,
+ code: TransactionCode,
+ data: Parcel,
+ flags: TransactionFlags,
+ ) -> Result<Parcel>;
+
+ /// Perform a generic operation with the object. This is a convenience
+ /// method that internally calls `prepare_transact` followed by
+ /// `submit_transact.
+ ///
/// # Arguments
/// * `code` - Transaction code for the operation
- /// * `data` - [`Parcel`] with input data
- /// * `reply` - Optional [`Parcel`] for reply data
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
+ /// * `input_callback` A callback for building the `Parcel`.
fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
&self,
code: TransactionCode,
flags: TransactionFlags,
input_callback: F,
- ) -> Result<Parcel>;
+ ) -> Result<Parcel> {
+ let mut parcel = self.prepare_transact()?;
+ input_callback(&mut parcel)?;
+ self.submit_transact(code, parcel, flags)
+ }
}
/// Interface of binder local or remote objects.
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index cb330a6..7e8e3a5 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -112,8 +112,7 @@
FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::add_service;
-pub use native::Binder;
+pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
pub use parcel::Parcel;
pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
@@ -129,7 +128,10 @@
/// The public API usable outside AIDL-generated interface crates.
pub mod public_api {
pub use super::parcel::ParcelFileDescriptor;
- pub use super::{add_service, get_interface, wait_for_interface};
+ pub use super::{
+ add_service, force_lazy_services_persist, get_interface, register_lazy_service,
+ wait_for_interface,
+ };
pub use super::{
BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index a0dfeec..e7c3396 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode};
+use crate::binder::{
+ AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
+};
use crate::error::{status_result, status_t, Result, StatusCode};
use crate::parcel::{Parcel, Serialize};
use crate::proxy::SpIBinder;
@@ -321,7 +323,12 @@
/// contains a `T` pointer in its user data. fd should be a non-owned file
/// descriptor, and args must be an array of null-terminated string
/// poiinters with length num_args.
- unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t {
+ unsafe extern "C" fn on_dump(
+ binder: *mut sys::AIBinder,
+ fd: i32,
+ args: *mut *const c_char,
+ num_args: u32,
+ ) -> status_t {
if fd < 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
@@ -447,6 +454,41 @@
status_result(status)
}
+/// Register a dynamic service via the LazyServiceRegistrar.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier. The service process
+/// will be shut down once all registered services are no longer in use.
+///
+/// If any service in the process is registered as lazy, all should be, otherwise
+/// the process may be shut down while a service is in use.
+pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ let status = unsafe {
+ // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
+
+ sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
+ };
+ status_result(status)
+}
+
+/// Prevent a process which registers lazy services from being shut down even when none
+/// of the services is in use.
+///
+/// If persist is true then shut down will be blocked until this function is called again with
+/// persist false. If this is to be the initial state, call this function before calling
+/// register_lazy_service.
+pub fn force_lazy_services_persist(persist: bool) {
+ unsafe {
+ // Safety: No borrowing or transfer of ownership occurs here.
+ sys::AServiceManager_forceLazyServicesPersist(persist)
+ }
+}
+
/// Tests often create a base BBinder instance; so allowing the unit
/// type to be remotable translates nicely to Binder::new(()).
impl Remotable for () {
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index a0e991c..dad89ec 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -25,6 +25,7 @@
use std::convert::TryInto;
use std::mem::ManuallyDrop;
use std::ptr;
+use std::fmt;
mod file_descriptor;
mod parcelable;
@@ -96,6 +97,13 @@
let _ = ManuallyDrop::new(self);
ptr
}
+
+ pub(crate) fn is_owned(&self) -> bool {
+ match *self {
+ Self::Owned(_) => true,
+ Self::Borrowed(_) => false,
+ }
+ }
}
// Data serialization methods
@@ -412,6 +420,13 @@
}
}
+impl fmt::Debug for Parcel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Parcel")
+ .finish()
+ }
+}
+
#[cfg(test)]
impl Parcel {
/// Create a new parcel tied to a bogus binder. TESTING ONLY!
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index b03ed49..68fa34b 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -31,8 +31,10 @@
use std::convert::TryInto;
use std::ffi::{c_void, CString};
use std::fmt;
+use std::mem;
use std::os::unix::io::AsRawFd;
use std::ptr;
+use std::sync::Arc;
/// A strong reference to a Binder remote object.
///
@@ -233,13 +235,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- /// Perform a binder transaction
- fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
- &self,
- code: TransactionCode,
- flags: TransactionFlags,
- input_callback: F,
- ) -> Result<Parcel> {
+ fn prepare_transact(&self) -> Result<Parcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -252,15 +248,25 @@
// pointer, or null.
sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
};
+
status_result(status)?;
- let mut input = unsafe {
+
+ unsafe {
// Safety: At this point, `input` is either a valid, owned `AParcel`
// pointer, or null. `Parcel::owned` safely handles both cases,
// taking ownership of the parcel.
- Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
- };
- input_callback(&mut input)?;
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+
+ fn submit_transact(
+ &self,
+ code: TransactionCode,
+ data: Parcel,
+ flags: TransactionFlags,
+ ) -> Result<Parcel> {
let mut reply = ptr::null_mut();
+ assert!(data.is_owned());
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. Although `IBinder::transact` is
@@ -275,13 +281,13 @@
// only providing `on_transact` with an immutable reference to
// `self`.
//
- // This call takes ownership of the `input` parcel pointer, and
+ // This call takes ownership of the `data` parcel pointer, and
// passes ownership of the `reply` out parameter to its caller. It
// does not affect ownership of the `binder` parameter.
sys::AIBinder_transact(
self.as_native() as *mut sys::AIBinder,
code,
- &mut input.into_raw(),
+ &mut data.into_raw(),
&mut reply,
flags,
)
@@ -378,13 +384,17 @@
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. `recipient` can always be
// converted into a valid pointer to an
- // `AIBinder_DeathRecipient`. Any value is safe to pass as the
- // cookie, although we depend on this value being set by
- // `get_cookie` when the death recipient callback is called.
+ // `AIBinder_DeathRecipient`.
+ //
+ // The cookie is also the correct pointer, and by calling new_cookie,
+ // we have created a new ref-count to the cookie, which linkToDeath
+ // takes ownership of. Once the DeathRecipient is unlinked for any
+ // reason (including if this call fails), the onUnlinked callback
+ // will consume that ref-count.
sys::AIBinder_linkToDeath(
self.as_native_mut(),
recipient.as_native_mut(),
- recipient.get_cookie(),
+ recipient.new_cookie(),
)
})
}
@@ -552,10 +562,20 @@
}
/// Rust wrapper around DeathRecipient objects.
+///
+/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// This struct owns a ref-count of it, and so does every binder that we
+/// have been linked with.
#[repr(C)]
pub struct DeathRecipient {
recipient: *mut sys::AIBinder_DeathRecipient,
- callback: Box<dyn Fn() + Send + 'static>,
+ cookie: *mut c_void,
+ vtable: &'static DeathRecipientVtable,
+}
+
+struct DeathRecipientVtable {
+ cookie_incr_refcount: unsafe extern "C" fn(*mut c_void),
+ cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
}
impl DeathRecipient {
@@ -563,9 +583,9 @@
/// associated object dies.
pub fn new<F>(callback: F) -> DeathRecipient
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = Box::new(callback);
+ let callback: *const F = Arc::into_raw(Arc::new(callback));
let recipient = unsafe {
// Safety: The function pointer is a valid death recipient callback.
//
@@ -574,34 +594,85 @@
// no longer needed.
sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
};
+ unsafe {
+ // Safety: The function pointer is a valid onUnlinked callback.
+ //
+ // All uses of linkToDeath in this file correctly increment the
+ // ref-count that this onUnlinked callback will decrement.
+ sys::AIBinder_DeathRecipient_setOnUnlinked(recipient, Some(Self::cookie_decr_refcount::<F>));
+ }
DeathRecipient {
recipient,
- callback,
+ cookie: callback as *mut c_void,
+ vtable: &DeathRecipientVtable {
+ cookie_incr_refcount: Self::cookie_incr_refcount::<F>,
+ cookie_decr_refcount: Self::cookie_decr_refcount::<F>,
+ },
}
}
+ /// Increment the ref-count for the cookie and return it.
+ ///
+ /// # Safety
+ ///
+ /// The caller must handle the returned ref-count correctly.
+ unsafe fn new_cookie(&self) -> *mut c_void {
+ (self.vtable.cookie_incr_refcount)(self.cookie);
+
+ // Return a raw pointer with ownership of a ref-count
+ self.cookie
+ }
+
/// Get the opaque cookie that identifies this death recipient.
///
/// This cookie will be used to link and unlink this death recipient to a
/// binder object and will be passed to the `binder_died` callback as an
/// opaque userdata pointer.
fn get_cookie(&self) -> *mut c_void {
- &*self.callback as *const _ as *mut c_void
+ self.cookie
}
/// Callback invoked from C++ when the binder object dies.
///
/// # Safety
///
- /// The `cookie` parameter must have been created with the `get_cookie`
- /// method of this object.
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the caller must hold a ref-count to it.
unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = (cookie as *mut F).as_ref().unwrap();
+ let callback = (cookie as *const F).as_ref().unwrap();
callback();
}
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ drop(Arc::from_raw(cookie as *const F));
+ }
+
+ /// Callback that increments the ref-count.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must handle the created ref-count properly.
+ unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+ mem::forget(Arc::clone(&arc));
+ }
}
/// # Safety
@@ -627,6 +698,12 @@
// `AIBinder_DeathRecipient_new` when `self` was created. This
// delete method can only be called once when `self` is dropped.
sys::AIBinder_DeathRecipient_delete(self.recipient);
+
+ // Safety: We own a ref-count to the cookie, and so does every
+ // linked binder. This call gives up our ref-count. The linked
+ // binders should already have given up their ref-count, or should
+ // do so shortly.
+ (self.vtable.cookie_decr_refcount)(self.cookie)
}
}
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index da8907d..335e8d8 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -363,13 +363,58 @@
);
}
- fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+ struct Bools {
+ binder_died: Arc<AtomicBool>,
+ binder_dealloc: Arc<AtomicBool>,
+ }
+
+ impl Bools {
+ fn is_dead(&self) -> bool {
+ self.binder_died.load(Ordering::Relaxed)
+ }
+ fn assert_died(&self) {
+ assert!(
+ self.is_dead(),
+ "Did not receive death notification"
+ );
+ }
+ fn assert_dropped(&self) {
+ assert!(
+ self.binder_dealloc.load(Ordering::Relaxed),
+ "Did not dealloc death notification"
+ );
+ }
+ fn assert_not_dropped(&self) {
+ assert!(
+ !self.binder_dealloc.load(Ordering::Relaxed),
+ "Dealloc death notification too early"
+ );
+ }
+ }
+
+ fn register_death_notification(binder: &mut SpIBinder) -> (Bools, DeathRecipient) {
let binder_died = Arc::new(AtomicBool::new(false));
+ let binder_dealloc = Arc::new(AtomicBool::new(false));
+
+ struct SetOnDrop {
+ binder_dealloc: Arc<AtomicBool>,
+ }
+ impl Drop for SetOnDrop {
+ fn drop(&mut self) {
+ self.binder_dealloc.store(true, Ordering::Relaxed);
+ }
+ }
let mut death_recipient = {
let flag = binder_died.clone();
+ let set_on_drop = SetOnDrop {
+ binder_dealloc: binder_dealloc.clone(),
+ };
DeathRecipient::new(move || {
flag.store(true, Ordering::Relaxed);
+ // Force the closure to take ownership of set_on_drop. When the closure is
+ // dropped, the destructor of `set_on_drop` will run.
+ let _ = &set_on_drop;
})
};
@@ -377,7 +422,12 @@
.link_to_death(&mut death_recipient)
.expect("link_to_death failed");
- (binder_died, death_recipient)
+ let bools = Bools {
+ binder_died,
+ binder_dealloc,
+ };
+
+ (bools, death_recipient)
}
/// Killing a remote service should unregister the service and trigger
@@ -390,7 +440,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, _recipient) = register_death_notification(&mut remote);
+ let (bools, recipient) = register_death_notification(&mut remote);
drop(service_process);
remote
@@ -400,10 +450,12 @@
// Pause to ensure any death notifications get delivered
thread::sleep(Duration::from_secs(1));
- assert!(
- binder_died.load(Ordering::Relaxed),
- "Did not receive death notification"
- );
+ bools.assert_died();
+ bools.assert_not_dropped();
+
+ drop(recipient);
+
+ bools.assert_dropped();
}
/// Test unregistering death notifications.
@@ -415,7 +467,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, mut recipient) = register_death_notification(&mut remote);
+ let (bools, mut recipient) = register_death_notification(&mut remote);
remote
.unlink_to_death(&mut recipient)
@@ -430,9 +482,13 @@
thread::sleep(Duration::from_secs(1));
assert!(
- !binder_died.load(Ordering::Relaxed),
+ !bools.is_dead(),
"Received unexpected death notification after unlinking",
);
+
+ bools.assert_not_dropped();
+ drop(recipient);
+ bools.assert_dropped();
}
/// Dropping a remote handle should unregister any death notifications.
@@ -444,7 +500,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, _recipient) = register_death_notification(&mut remote);
+ let (bools, recipient) = register_death_notification(&mut remote);
// This should automatically unregister our death notification.
drop(remote);
@@ -457,9 +513,13 @@
// We dropped the remote handle, so we should not receive the death
// notification when the remote process dies here.
assert!(
- !binder_died.load(Ordering::Relaxed),
+ !bools.is_dead(),
"Received unexpected death notification after dropping remote handle"
);
+
+ bools.assert_not_dropped();
+ drop(recipient);
+ bools.assert_dropped();
}
/// Test IBinder interface methods not exercised elsewhere.
@@ -637,4 +697,27 @@
assert!(!(service1 > service1));
assert_eq!(service1 < service2, !(service2 < service1));
}
+
+ #[test]
+ fn binder_parcel_mixup() {
+ let service1 = BnTest::new_binder(
+ TestService::new("testing_service1"),
+ BinderFeatures::default(),
+ );
+ let service2 = BnTest::new_binder(
+ TestService::new("testing_service2"),
+ BinderFeatures::default(),
+ );
+
+ let service1 = service1.as_binder();
+ let service2 = service2.as_binder();
+
+ let parcel = service1.prepare_transact().unwrap();
+ let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+
+ match res {
+ Ok(_) => panic!("submit_transact should fail"),
+ Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE),
+ }
+ }
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 13ea827..1968058 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -120,9 +120,12 @@
host_supported: true,
unstable: true,
srcs: [
+ "BinderRpcTestClientInfo.aidl",
+ "BinderRpcTestServerInfo.aidl",
"IBinderRpcCallback.aidl",
"IBinderRpcSession.aidl",
"IBinderRpcTest.aidl",
+ "ParcelableCertificateData.aidl",
],
backend: {
java: {
@@ -150,6 +153,7 @@
srcs: [
"binderRpcTest.cpp",
+ "RpcCertificateVerifierSimple.cpp",
],
shared_libs: [
"libbinder",
diff --git a/libs/binder/rust/src/binder_rpc_unstable.hpp b/libs/binder/tests/BinderRpcTestClientInfo.aidl
similarity index 78%
copy from libs/binder/rust/src/binder_rpc_unstable.hpp
copy to libs/binder/tests/BinderRpcTestClientInfo.aidl
index 7932d0f..b4baebc 100644
--- a/libs/binder/rust/src/binder_rpc_unstable.hpp
+++ b/libs/binder/tests/BinderRpcTestClientInfo.aidl
@@ -14,13 +14,8 @@
* limitations under the License.
*/
-#pragma once
+import ParcelableCertificateData;
-extern "C" {
-
-struct AIBinder;
-
-bool RunRpcServer(AIBinder* service, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
-
+parcelable BinderRpcTestClientInfo {
+ ParcelableCertificateData[] certs;
}
diff --git a/libs/binder/rust/src/binder_rpc_unstable.hpp b/libs/binder/tests/BinderRpcTestServerInfo.aidl
similarity index 78%
copy from libs/binder/rust/src/binder_rpc_unstable.hpp
copy to libs/binder/tests/BinderRpcTestServerInfo.aidl
index 7932d0f..00dc0bc 100644
--- a/libs/binder/rust/src/binder_rpc_unstable.hpp
+++ b/libs/binder/tests/BinderRpcTestServerInfo.aidl
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-#pragma once
+import ParcelableCertificateData;
-extern "C" {
-
-struct AIBinder;
-
-bool RunRpcServer(AIBinder* service, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
-
+parcelable BinderRpcTestServerInfo {
+ long port;
+ ParcelableCertificateData cert;
}
diff --git a/libs/binder/rust/src/binder_rpc_unstable.hpp b/libs/binder/tests/ParcelableCertificateData.aidl
similarity index 78%
rename from libs/binder/rust/src/binder_rpc_unstable.hpp
rename to libs/binder/tests/ParcelableCertificateData.aidl
index 7932d0f..38c382e 100644
--- a/libs/binder/rust/src/binder_rpc_unstable.hpp
+++ b/libs/binder/tests/ParcelableCertificateData.aidl
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-#pragma once
-
-extern "C" {
-
-struct AIBinder;
-
-bool RunRpcServer(AIBinder* service, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
-
+parcelable ParcelableCertificateData {
+ byte[] data;
}
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.cpp b/libs/binder/tests/RpcCertificateVerifierSimple.cpp
new file mode 100644
index 0000000..4694d1b
--- /dev/null
+++ b/libs/binder/tests/RpcCertificateVerifierSimple.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "RpcCertificateVerifierSimple"
+#include <log/log.h>
+
+#include <binder/RpcCertificateUtils.h>
+
+#include "RpcCertificateVerifierSimple.h"
+
+namespace android {
+
+status_t RpcCertificateVerifierSimple::verify(const X509* peerCert, uint8_t* outAlert) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (const auto& trustedCert : mTrustedPeerCertificates) {
+ if (0 == X509_cmp(trustedCert.get(), peerCert)) {
+ return OK;
+ }
+ }
+ *outAlert = SSL_AD_CERTIFICATE_UNKNOWN;
+ return PERMISSION_DENIED;
+}
+
+status_t RpcCertificateVerifierSimple::addTrustedPeerCertificate(RpcCertificateFormat format,
+ const std::vector<uint8_t>& cert) {
+ bssl::UniquePtr<X509> x509 = deserializeCertificate(cert, format);
+ if (x509 == nullptr) {
+ ALOGE("Certificate is not in the proper format %s", PrintToString(format).c_str());
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(mMutex);
+ mTrustedPeerCertificates.push_back(std::move(x509));
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.h b/libs/binder/tests/RpcCertificateVerifierSimple.h
new file mode 100644
index 0000000..1f2e531
--- /dev/null
+++ b/libs/binder/tests/RpcCertificateVerifierSimple.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <string_view>
+#include <vector>
+
+#include <openssl/ssl.h>
+
+#include <binder/RpcCertificateFormat.h>
+#include <binder/RpcCertificateVerifier.h>
+
+namespace android {
+
+// A simple certificate verifier for testing.
+// Keep a list of leaf certificates as trusted. No certificate chain support.
+//
+// All APIs are thread-safe. However, if verify() and addTrustedPeerCertificate() are called
+// simultaneously in different threads, it is not deterministic whether verify() will use the
+// certificate being added.
+class RpcCertificateVerifierSimple : public RpcCertificateVerifier {
+public:
+ status_t verify(const X509*, uint8_t*) override;
+
+ // Add a trusted peer certificate. Peers presenting this certificate are accepted.
+ //
+ // Caller must ensure that RpcTransportCtx::newTransport() are called after all trusted peer
+ // certificates are added. Otherwise, RpcTransport-s created before may not trust peer
+ // certificates added later.
+ [[nodiscard]] status_t addTrustedPeerCertificate(RpcCertificateFormat format,
+ const std::vector<uint8_t>& cert);
+
+private:
+ std::mutex mMutex; // for below
+ std::vector<bssl::UniquePtr<X509>> mTrustedPeerCertificates;
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 3f72b8f..eec3b44 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -75,10 +75,10 @@
auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
ASSERT_THAT(debuggableResult, Ok());
ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
- auto debuggableBool = ParseBool(Trim(debuggableResult->stdout));
- ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdout);
+ auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr));
+ ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr);
if (debuggableBool == ParseBoolResult::kFalse) {
- GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdout);
+ GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdoutStr);
}
auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index eea7d8c..639876f 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -465,31 +465,30 @@
TEST_F(BinderLibTest, Freeze) {
Parcel data, reply, replypid;
- std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+ std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
- //Pass test on devices where the freezer is not supported
+ // Pass test on devices where the cgroup v2 freezer is not supported
if (freezer_file.fail()) {
GTEST_SKIP();
return;
}
- std::string freezer_enabled;
- std::getline(freezer_file, freezer_enabled);
-
- //Pass test on devices where the freezer is disabled
- if (freezer_enabled != "1") {
- GTEST_SKIP();
- return;
- }
-
EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
- EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
- EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
- EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+
+ // Pass test on devices where BINDER_FREEZE ioctl is not supported
+ int ret = IPCThreadState::self()->freeze(pid, false, 0);
+ if (ret != 0) {
+ GTEST_SKIP();
+ return;
+ }
+
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
bool sync_received, async_received;
@@ -500,6 +499,14 @@
EXPECT_EQ(sync_received, 1);
EXPECT_EQ(async_received, 0);
+ uint32_t sync_received2, async_received2;
+
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
+ &async_received2));
+
+ EXPECT_EQ(sync_received2, 1);
+ EXPECT_EQ(async_received2, 0);
+
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
}
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index e430c28..55aa57b 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -211,10 +211,9 @@
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
status = gSession->setupUnixDomainClient(addr.c_str());
- if (status == OK) goto success;
+ if (status == OK) break;
}
- LOG(FATAL) << "Could not connect: " << statusToString(status).c_str();
-success:
+ CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status).c_str();
::benchmark::RunSpecifiedBenchmarks();
return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 7c405d3..6bcf102 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <BinderRpcTestClientInfo.h>
+#include <BinderRpcTestServerInfo.h>
#include <BnBinderRpcCallback.h>
#include <BnBinderRpcSession.h>
#include <BnBinderRpcTest.h>
@@ -40,14 +42,21 @@
#include <thread>
#include <type_traits>
+#include <poll.h>
#include <sys/prctl.h>
#include <unistd.h>
+#include "../FdTrigger.h"
#include "../RpcSocketAddress.h" // for testing preconnected clients
#include "../RpcState.h" // for debugging
#include "../vm_sockets.h" // for VMADDR_*
+#include "RpcCertificateVerifierSimple.h"
using namespace std::chrono_literals;
+using namespace std::placeholders;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
namespace android {
@@ -61,12 +70,17 @@
return {RpcSecurity::RAW, RpcSecurity::TLS};
}
-static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity) {
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
+ RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr) {
switch (rpcSecurity) {
case RpcSecurity::RAW:
return RpcTransportCtxFactoryRaw::make();
- case RpcSecurity::TLS:
- return RpcTransportCtxFactoryTls::make();
+ case RpcSecurity::TLS: {
+ if (verifier == nullptr) {
+ verifier = std::make_shared<RpcCertificateVerifierSimple>();
+ }
+ return RpcTransportCtxFactoryTls::make(std::move(verifier));
+ }
default:
LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
}
@@ -304,14 +318,17 @@
class Process {
public:
Process(Process&&) = default;
- Process(const std::function<void(android::base::borrowed_fd /* writeEnd */)>& f) {
- android::base::unique_fd writeEnd;
- CHECK(android::base::Pipe(&mReadEnd, &writeEnd)) << strerror(errno);
+ Process(const std::function<void(android::base::borrowed_fd /* writeEnd */,
+ android::base::borrowed_fd /* readEnd */)>& f) {
+ android::base::unique_fd childWriteEnd;
+ android::base::unique_fd childReadEnd;
+ CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno);
+ CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno);
if (0 == (mPid = fork())) {
// racey: assume parent doesn't crash before this is set
prctl(PR_SET_PDEATHSIG, SIGHUP);
- f(writeEnd);
+ f(childWriteEnd, childReadEnd);
exit(0);
}
@@ -322,16 +339,20 @@
}
}
android::base::borrowed_fd readEnd() { return mReadEnd; }
+ android::base::borrowed_fd writeEnd() { return mWriteEnd; }
private:
pid_t mPid = 0;
android::base::unique_fd mReadEnd;
+ android::base::unique_fd mWriteEnd;
};
static std::string allocateSocketAddress() {
static size_t id = 0;
std::string temp = getenv("TMPDIR") ?: "/tmp";
- return temp + "/binderRpcTest_" + std::to_string(id++);
+ auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+ unlink(ret.c_str());
+ return ret;
};
static unsigned int allocateVsockPort() {
@@ -434,16 +455,17 @@
}
}
-static base::unique_fd connectToUds(const char* addrStr) {
- UnixSocketAddress addr(addrStr);
+static base::unique_fd connectTo(const RpcSocketAddress& addr) {
base::unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
int savedErrno = errno;
- CHECK(serverFd.ok()) << "Could not create socket " << addrStr << ": " << strerror(savedErrno);
+ CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": "
+ << strerror(savedErrno);
if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
int savedErrno = errno;
- LOG(FATAL) << "Could not connect to socket " << addrStr << ": " << strerror(savedErrno);
+ LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": "
+ << strerror(savedErrno);
}
return serverFd;
}
@@ -461,6 +483,37 @@
return PrintToString(type) + "_" + newFactory(security)->toCString();
}
+ static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
+ uint64_t length = str.length();
+ CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
+ CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+ }
+
+ static inline std::string readString(android::base::borrowed_fd fd) {
+ uint64_t length;
+ CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+ std::string ret(length, '\0');
+ CHECK(android::base::ReadFully(fd, ret.data(), length));
+ return ret;
+ }
+
+ static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
+ Parcel parcel;
+ CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+ writeString(fd,
+ std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
+ }
+
+ template <typename T>
+ static inline T readFromFd(android::base::borrowed_fd fd) {
+ std::string data = readString(fd);
+ Parcel parcel;
+ CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+ T object;
+ CHECK_EQ(OK, object.readFromParcel(&parcel));
+ return object;
+ }
+
// This creates a new process serving an interface on a certain number of
// threads.
ProcessSession createRpcTestSocketServerProcess(
@@ -472,11 +525,12 @@
unsigned int vsockPort = allocateVsockPort();
std::string addr = allocateSocketAddress();
- unlink(addr.c_str());
auto ret = ProcessSession{
- .host = Process([&](android::base::borrowed_fd writeEnd) {
- sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity));
+ .host = Process([&](android::base::borrowed_fd writeEnd,
+ android::base::borrowed_fd readEnd) {
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier));
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
server->setMaxThreads(options.numThreads);
@@ -501,7 +555,20 @@
LOG_ALWAYS_FATAL("Unknown socket type");
}
- CHECK(android::base::WriteFully(writeEnd, &outPort, sizeof(outPort)));
+ BinderRpcTestServerInfo serverInfo;
+ serverInfo.port = static_cast<int64_t>(outPort);
+ serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM);
+ writeToFd(writeEnd, serverInfo);
+ auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd);
+
+ if (rpcSecurity == RpcSecurity::TLS) {
+ for (const auto& clientCert : clientInfo.certs) {
+ CHECK_EQ(OK,
+ certVerifier
+ ->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ clientCert.data));
+ }
+ }
configure(server);
@@ -512,44 +579,56 @@
}),
};
- // always read socket, so that we have waited for the server to start
- unsigned int outPort = 0;
- CHECK(android::base::ReadFully(ret.host.readEnd(), &outPort, sizeof(outPort)));
+ std::vector<sp<RpcSession>> sessions;
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ for (size_t i = 0; i < options.numSessions; i++) {
+ sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
+ }
+
+ auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd());
+ BinderRpcTestClientInfo clientInfo;
+ for (const auto& session : sessions) {
+ auto& parcelableCert = clientInfo.certs.emplace_back();
+ parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+ }
+ writeToFd(ret.host.writeEnd(), clientInfo);
+
+ CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
if (socketType == SocketType::INET) {
- CHECK_NE(0, outPort);
+ CHECK_NE(0, serverInfo.port);
+ }
+
+ if (rpcSecurity == RpcSecurity::TLS) {
+ const auto& serverCert = serverInfo.cert.data;
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ serverCert));
}
status_t status;
- for (size_t i = 0; i < options.numSessions; i++) {
- sp<RpcSession> session =
- RpcSession::make(newFactory(rpcSecurity), std::nullopt, std::nullopt);
+ for (const auto& session : sessions) {
session->setMaxThreads(options.numIncomingConnections);
switch (socketType) {
case SocketType::PRECONNECTED:
status = session->setupPreconnectedClient({}, [=]() {
- return connectToUds(addr.c_str());
+ return connectTo(UnixSocketAddress(addr.c_str()));
});
- if (status == OK) goto success;
break;
case SocketType::UNIX:
status = session->setupUnixDomainClient(addr.c_str());
- if (status == OK) goto success;
break;
case SocketType::VSOCK:
status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
- if (status == OK) goto success;
break;
case SocketType::INET:
- status = session->setupInetClient("127.0.0.1", outPort);
- if (status == OK) goto success;
+ status = session->setupInetClient("127.0.0.1", serverInfo.port);
break;
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
- LOG_ALWAYS_FATAL("Could not connect %s", statusToString(status).c_str());
- success:
+ CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
@@ -1208,16 +1287,17 @@
}
server->start();
- sp<RpcSession> session =
- RpcSession::make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+ sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
while (!server->shutdown()) usleep(10000);
ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str());
return status == OK;
}
-static std::vector<SocketType> testSocketTypes() {
- std::vector<SocketType> ret = {SocketType::PRECONNECTED, SocketType::UNIX, SocketType::INET};
+static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
+ std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+
+ if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
static bool hasVsockLoopback = testSupportVsockLoopback();
@@ -1286,7 +1366,6 @@
TEST_P(BinderRpcSimple, Shutdown) {
auto addr = allocateSocketAddress();
- unlink(addr.c_str());
auto server = RpcServer::make(newFactory(GetParam()));
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
@@ -1351,6 +1430,415 @@
INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
BinderRpcSimple::PrintTestParam);
+class RpcTransportTest
+ : public ::testing::TestWithParam<
+ std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>> {
+public:
+ using ConnectToServer = std::function<base::unique_fd()>;
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [socketType, rpcSecurity, certificateFormat] = info.param;
+ auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+ if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
+ return ret;
+ }
+ static std::vector<ParamType> getRpcTranportTestParams() {
+ std::vector<RpcTransportTest::ParamType> ret;
+ for (auto socketType : testSocketTypes(false /* hasPreconnected */)) {
+ for (auto rpcSecurity : RpcSecurityValues()) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW: {
+ ret.emplace_back(socketType, rpcSecurity, std::nullopt);
+ } break;
+ case RpcSecurity::TLS: {
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM);
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER);
+ } break;
+ }
+ }
+ }
+ return ret;
+ }
+ void TearDown() override {
+ for (auto& server : mServers) server->shutdownAndWait();
+ }
+
+ // A server that handles client socket connections.
+ class Server {
+ public:
+ explicit Server() {}
+ Server(Server&&) = default;
+ ~Server() { shutdownAndWait(); }
+ [[nodiscard]] AssertionResult setUp() {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
+ rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ switch (socketType) {
+ case SocketType::PRECONNECTED: {
+ return AssertionFailure() << "Not supported by this test";
+ } break;
+ case SocketType::UNIX: {
+ auto addr = allocateSocketAddress();
+ auto status = rpcServer->setupUnixDomainServer(addr.c_str());
+ if (status != OK) {
+ return AssertionFailure()
+ << "setupUnixDomainServer: " << statusToString(status);
+ }
+ mConnectToServer = [addr] {
+ return connectTo(UnixSocketAddress(addr.c_str()));
+ };
+ } break;
+ case SocketType::VSOCK: {
+ auto port = allocateVsockPort();
+ auto status = rpcServer->setupVsockServer(port);
+ if (status != OK) {
+ return AssertionFailure() << "setupVsockServer: " << statusToString(status);
+ }
+ mConnectToServer = [port] {
+ return connectTo(VsockSocketAddress(VMADDR_CID_LOCAL, port));
+ };
+ } break;
+ case SocketType::INET: {
+ unsigned int port;
+ auto status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port);
+ if (status != OK) {
+ return AssertionFailure() << "setupInetServer: " << statusToString(status);
+ }
+ mConnectToServer = [port] {
+ const char* addr = kLocalInetAddress;
+ auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+ if (aiStart == nullptr) return base::unique_fd{};
+ for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+ auto fd = connectTo(
+ InetSocketAddress(ai->ai_addr, ai->ai_addrlen, addr, port));
+ if (fd.ok()) return fd;
+ }
+ ALOGE("None of the socket address resolved for %s:%u can be connected",
+ addr, port);
+ return base::unique_fd{};
+ };
+ }
+ }
+ mFd = rpcServer->releaseServer();
+ if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
+ mCtx = newFactory(rpcSecurity, mCertVerifier)->newServerCtx();
+ if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
+ mSetup = true;
+ return AssertionSuccess();
+ }
+ RpcTransportCtx* getCtx() const { return mCtx.get(); }
+ std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const {
+ return mCertVerifier;
+ }
+ ConnectToServer getConnectToServerFn() { return mConnectToServer; }
+ void start() {
+ LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
+ mThread = std::make_unique<std::thread>(&Server::run, this);
+ }
+ void run() {
+ LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
+
+ std::vector<std::thread> threads;
+ while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
+ base::unique_fd acceptedFd(
+ TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/,
+ SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
+ }
+
+ for (auto& thread : threads) thread.join();
+ }
+ void handleOne(android::base::unique_fd acceptedFd) {
+ ASSERT_TRUE(acceptedFd.ok());
+ auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get());
+ if (serverTransport == nullptr) return; // handshake failed
+ ASSERT_TRUE(mPostConnect(serverTransport.get(), mFdTrigger.get()));
+ }
+ void shutdownAndWait() {
+ shutdown();
+ join();
+ }
+ void shutdown() { mFdTrigger->trigger(); }
+
+ void setPostConnect(
+ std::function<AssertionResult(RpcTransport*, FdTrigger* fdTrigger)> fn) {
+ mPostConnect = std::move(fn);
+ }
+
+ private:
+ std::unique_ptr<std::thread> mThread;
+ ConnectToServer mConnectToServer;
+ std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
+ base::unique_fd mFd;
+ std::unique_ptr<RpcTransportCtx> mCtx;
+ std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
+ std::make_shared<RpcCertificateVerifierSimple>();
+ bool mSetup = false;
+ // The function invoked after connection and handshake. By default, it is
+ // |defaultPostConnect| that sends |kMessage| to the client.
+ std::function<AssertionResult(RpcTransport*, FdTrigger* fdTrigger)> mPostConnect =
+ Server::defaultPostConnect;
+
+ void join() {
+ if (mThread != nullptr) {
+ mThread->join();
+ mThread = nullptr;
+ }
+ }
+
+ static AssertionResult defaultPostConnect(RpcTransport* serverTransport,
+ FdTrigger* fdTrigger) {
+ std::string message(kMessage);
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
+ message.size());
+ if (status != OK) return AssertionFailure() << statusToString(status);
+ return AssertionSuccess();
+ }
+ };
+
+ class Client {
+ public:
+ explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {}
+ Client(Client&&) = default;
+ [[nodiscard]] AssertionResult setUp() {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ mFdTrigger = FdTrigger::make();
+ mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
+ if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
+ return AssertionSuccess();
+ }
+ RpcTransportCtx* getCtx() const { return mCtx.get(); }
+ std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const {
+ return mCertVerifier;
+ }
+ // connect() and do handshake
+ bool setUpTransport() {
+ mFd = mConnectToServer();
+ if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server";
+ mClientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get());
+ return mClientTransport != nullptr;
+ }
+ AssertionResult readMessage(const std::string& expectedMessage = kMessage) {
+ LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed");
+ std::string readMessage(expectedMessage.size(), '\0');
+ status_t readStatus =
+ mClientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
+ readMessage.size());
+ if (readStatus != OK) {
+ return AssertionFailure() << statusToString(readStatus);
+ }
+ if (readMessage != expectedMessage) {
+ return AssertionFailure()
+ << "Expected " << expectedMessage << ", actual " << readMessage;
+ }
+ return AssertionSuccess();
+ }
+ void run(bool handshakeOk = true, bool readOk = true) {
+ if (!setUpTransport()) {
+ ASSERT_FALSE(handshakeOk) << "newTransport returns nullptr, but it shouldn't";
+ return;
+ }
+ ASSERT_TRUE(handshakeOk) << "newTransport does not return nullptr, but it should";
+ ASSERT_EQ(readOk, readMessage());
+ }
+
+ private:
+ ConnectToServer mConnectToServer;
+ base::unique_fd mFd;
+ std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
+ std::unique_ptr<RpcTransportCtx> mCtx;
+ std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
+ std::make_shared<RpcCertificateVerifierSimple>();
+ std::unique_ptr<RpcTransport> mClientTransport;
+ };
+
+ // Make A trust B.
+ template <typename A, typename B>
+ status_t trust(A* a, B* b) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ if (rpcSecurity != RpcSecurity::TLS) return OK;
+ LOG_ALWAYS_FATAL_IF(!certificateFormat.has_value());
+ auto bCert = b->getCtx()->getCertificate(*certificateFormat);
+ return a->getCertVerifier()->addTrustedPeerCertificate(*certificateFormat, bCert);
+ }
+
+ static constexpr const char* kMessage = "hello";
+ std::vector<std::unique_ptr<Server>> mServers;
+};
+
+TEST_P(RpcTransportTest, GoodCertificate) {
+ auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(server->setUp());
+
+ Client client(server->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+
+ ASSERT_EQ(OK, trust(&client, server));
+ ASSERT_EQ(OK, trust(server, &client));
+
+ server->start();
+ client.run();
+}
+
+TEST_P(RpcTransportTest, MultipleClients) {
+ auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(server->setUp());
+
+ std::vector<Client> clients;
+ for (int i = 0; i < 2; i++) {
+ auto& client = clients.emplace_back(server->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+ ASSERT_EQ(OK, trust(&client, server));
+ ASSERT_EQ(OK, trust(server, &client));
+ }
+
+ server->start();
+ for (auto& client : clients) client.run();
+}
+
+TEST_P(RpcTransportTest, UntrustedServer) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+
+ auto untrustedServer = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(untrustedServer->setUp());
+
+ Client client(untrustedServer->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+
+ ASSERT_EQ(OK, trust(untrustedServer, &client));
+
+ untrustedServer->start();
+
+ // For TLS, this should reject the certificate. For RAW sockets, it should pass because
+ // the client can't verify the server's identity.
+ bool handshakeOk = rpcSecurity != RpcSecurity::TLS;
+ client.run(handshakeOk);
+}
+TEST_P(RpcTransportTest, MaliciousServer) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto validServer = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(validServer->setUp());
+
+ auto maliciousServer = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(maliciousServer->setUp());
+
+ Client client(maliciousServer->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+
+ ASSERT_EQ(OK, trust(&client, validServer));
+ ASSERT_EQ(OK, trust(validServer, &client));
+ ASSERT_EQ(OK, trust(maliciousServer, &client));
+
+ maliciousServer->start();
+
+ // For TLS, this should reject the certificate. For RAW sockets, it should pass because
+ // the client can't verify the server's identity.
+ bool handshakeOk = rpcSecurity != RpcSecurity::TLS;
+ client.run(handshakeOk);
+}
+
+TEST_P(RpcTransportTest, UntrustedClient) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(server->setUp());
+
+ Client client(server->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+
+ ASSERT_EQ(OK, trust(&client, server));
+
+ server->start();
+
+ // For TLS, Client should be able to verify server's identity, so client should see
+ // do_handshake() successfully executed. However, server shouldn't be able to verify client's
+ // identity and should drop the connection, so client shouldn't be able to read anything.
+ bool readOk = rpcSecurity != RpcSecurity::TLS;
+ client.run(true, readOk);
+}
+
+TEST_P(RpcTransportTest, MaliciousClient) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(server->setUp());
+
+ Client validClient(server->getConnectToServerFn());
+ ASSERT_TRUE(validClient.setUp());
+ Client maliciousClient(server->getConnectToServerFn());
+ ASSERT_TRUE(maliciousClient.setUp());
+
+ ASSERT_EQ(OK, trust(&validClient, server));
+ ASSERT_EQ(OK, trust(&maliciousClient, server));
+
+ server->start();
+
+ // See UntrustedClient.
+ bool readOk = rpcSecurity != RpcSecurity::TLS;
+ maliciousClient.run(true, readOk);
+}
+
+TEST_P(RpcTransportTest, Trigger) {
+ std::string msg2 = ", world!";
+ std::mutex writeMutex;
+ std::condition_variable writeCv;
+ bool shouldContinueWriting = false;
+ auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
+ std::string message(kMessage);
+ auto status =
+ serverTransport->interruptableWriteFully(fdTrigger, message.data(), message.size());
+ if (status != OK) return AssertionFailure() << statusToString(status);
+
+ {
+ std::unique_lock<std::mutex> lock(writeMutex);
+ if (!writeCv.wait_for(lock, 3s, [&] { return shouldContinueWriting; })) {
+ return AssertionFailure() << "write barrier not cleared in time!";
+ }
+ }
+
+ status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size());
+ if (status != -ECANCELED)
+ return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
+ "should return -ECANCELLED, but it is "
+ << statusToString(status);
+ return AssertionSuccess();
+ };
+
+ auto server = mServers.emplace_back(std::make_unique<Server>()).get();
+ ASSERT_TRUE(server->setUp());
+
+ // Set up client
+ Client client(server->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp());
+
+ // Exchange keys
+ ASSERT_EQ(OK, trust(&client, server));
+ ASSERT_EQ(OK, trust(server, &client));
+
+ server->setPostConnect(serverPostConnect);
+
+ server->start();
+ // connect() to server and do handshake
+ ASSERT_TRUE(client.setUpTransport());
+ // read the first message. This ensures that server has finished handshake and start handling
+ // client fd. Server thread should pause at writeCv.wait_for().
+ ASSERT_TRUE(client.readMessage(kMessage));
+ // Trigger server shutdown after server starts handling client FD. This ensures that the second
+ // write is on an FdTrigger that has been shut down.
+ server->shutdown();
+ // Continues server thread to write the second message.
+ {
+ std::lock_guard<std::mutex> lock(writeMutex);
+ shouldContinueWriting = true;
+ }
+ writeCv.notify_all();
+ // After this line, server thread unblocks and attempts to write the second message, but
+ // shutdown is triggered, so write should failed with -ECANCELLED. See |serverPostConnect|.
+ // On the client side, second read fails with DEAD_OBJECT
+ ASSERT_FALSE(client.readMessage(msg2));
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
+ ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()),
+ RpcTransportTest::PrintParamInfo);
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 46e9630..a807afa 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -21,6 +21,7 @@
#include <android-base/strings.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
+#include <binder/Status.h>
#include <gtest/gtest.h>
#include "../Debug.h"
@@ -154,6 +155,9 @@
[](Parcel* p) { ASSERT_EQ(OK, p->writeVectorSize(std::optional<std::vector<int32_t>>(std::nullopt))); },
[](Parcel* p) { ASSERT_EQ(OK, p->writeVectorSize(std::optional<std::vector<int32_t>>({0, 1, 17}))); },
[](Parcel* p) { ASSERT_EQ(OK, p->writeNoException()); },
+ [](Parcel* p) { ASSERT_EQ(OK, binder::Status::ok().writeToParcel(p)); },
+ [](Parcel* p) { ASSERT_EQ(OK, binder::Status::fromExceptionCode(7, ":D").writeToParcel(p)); },
+ [](Parcel* p) { ASSERT_EQ(OK, binder::Status::fromServiceSpecificError(8, ":/").writeToParcel(p)); },
};
// clang-format on
@@ -226,7 +230,8 @@
"020000000000000001000000|020000000000000001000000|ffffffff|"
"0200000000000000000000000100000000000000|0200000000000000000000000100000000000000|"
"ffffffff|010000000100000025000000|010000000100000025000000|00000000|0100000025000000|"
- "0100000025000000|03000000|00000000|ffffffff|03000000|00000000";
+ "0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|"
+ "07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000";
TEST(RpcWire, CurrentVersion) {
checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index fb24836..4330e3e 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -34,7 +34,7 @@
auto result = execute({"echo", "foo"}, nullptr);
ASSERT_THAT(result, Ok());
EXPECT_THAT(result->exitCode, Optional(EX_OK));
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
TEST(UtilsHost, ExecuteLongRunning) {
@@ -44,7 +44,7 @@
std::vector<std::string> args{"sh", "-c",
"sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"};
auto result = execute(std::move(args), [](const CommandResult& commandResult) {
- return android::base::EndsWith(commandResult.stdout, "\n");
+ return android::base::EndsWith(commandResult.stdoutStr, "\n");
});
auto elapsed = std::chrono::system_clock::now() - now;
auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
@@ -53,7 +53,7 @@
ASSERT_THAT(result, Ok());
EXPECT_EQ(std::nullopt, result->exitCode);
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
// ~CommandResult() called, child process is killed.
@@ -70,7 +70,7 @@
std::vector<std::string> args{"sh", "-c",
"sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"};
auto result = execute(std::move(args), [](const CommandResult& commandResult) {
- return android::base::EndsWith(commandResult.stdout, "\n");
+ return android::base::EndsWith(commandResult.stdoutStr, "\n");
});
auto elapsed = std::chrono::system_clock::now() - now;
auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
@@ -79,7 +79,7 @@
ASSERT_THAT(result, Ok());
EXPECT_EQ(std::nullopt, result->exitCode);
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
// ~CommandResult() called, child process is killed.
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 7fd9f6b..8bf04cc 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -36,8 +36,7 @@
void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
if (provider.ConsumeBool()) {
- auto session =
- RpcSession::make(RpcTransportCtxFactoryRaw::make(), std::nullopt, std::nullopt);
+ auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
CHECK_EQ(OK, session->addNullDebuggingClient());
p->markForRpc(session);
fillRandomParcelData(p, std::move(provider));
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index b1263e8..6f054d2 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -51,7 +51,6 @@
cc_fuzz {
name: "binder_bpBinderFuzz",
defaults: ["binder_fuzz_defaults"],
- host_supported: false,
srcs: ["BpBinderFuzz.cpp"],
}
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index c50279b..20c5569 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -19,8 +19,15 @@
#include <commonFuzzHelpers.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include <android-base/logging.h>
#include <binder/BpBinder.h>
#include <binder/IServiceManager.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <signal.h>
+#include <sys/prctl.h>
+#include <thread>
namespace android {
@@ -28,13 +35,30 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size);
- // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
- // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
- // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
- // crashes that are not easy to diagnose.
- int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
- sp<BpBinder> bpbinder = BpBinder::create(handle);
- if (bpbinder == nullptr) return 0;
+ std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
+ (void)unlink(addr.c_str());
+
+ sp<RpcServer> server = RpcServer::make();
+
+ // use RPC binder because fuzzer can't get coverage from another process.
+ auto thread = std::thread([&]() {
+ prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+ server->setRootObject(sp<BBinder>::make());
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+ server->join();
+ });
+
+ sp<RpcSession> session = RpcSession::make();
+ status_t status;
+ for (size_t tries = 0; tries < 5; tries++) {
+ usleep(10000);
+ status = session->setupUnixDomainClient(addr.c_str());
+ if (status == OK) break;
+ }
+ CHECK_EQ(status, OK) << "Unable to connect";
+
+ sp<BpBinder> bpBinder = session->getRootObject()->remoteBinder();
// To prevent memory from running out from calling too many add item operations.
const uint32_t MAX_RUNS = 2048;
@@ -43,12 +67,16 @@
while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
if (fdp.ConsumeBool()) {
- callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+ callArbitraryFunction(&fdp, gBPBinderOperations, bpBinder, s_recipient);
} else {
- callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+ callArbitraryFunction(&fdp, gIBinderOperations, bpBinder.get());
}
}
+ CHECK(session->shutdownAndWait(true)) << "couldn't shutdown session";
+ CHECK(server->shutdown()) << "couldn't shutdown server";
+ thread.join();
+
return 0;
}
} // namespace android
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
index 6ca0e2f..741987f 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
@@ -52,7 +52,7 @@
const sp<IBinder::DeathRecipient>& s_recipient) -> void {
// Clean up possible leftover memory.
wp<IBinder::DeathRecipient> outRecipient(nullptr);
- bpbinder->sendObituary();
+ if (!bpbinder->isRpcBinder()) bpbinder->sendObituary();
bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
&outRecipient);
@@ -72,7 +72,9 @@
[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
- const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+ const sp<IBinder::DeathRecipient>&) -> void {
+ if (!bpbinder->isRpcBinder()) bpbinder->sendObituary();
+ },
[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>&) -> void {
uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 3026921..5a80ad0 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -14,10 +14,10 @@
address: true,
},
srcs: [
- "cast_test.cpp",
"Flags_test.cpp",
+ "cast_test.cpp",
+ "enum_test.cpp",
"future_test.cpp",
- "NamedEnum_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
"static_vector_test.cpp",
diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp
index 8c00b52..d241fa2 100644
--- a/libs/ftl/Flags_test.cpp
+++ b/libs/ftl/Flags_test.cpp
@@ -23,7 +23,7 @@
using namespace android::flag_operators;
-enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
TEST(Flags, Test) {
Flags<TestFlags> flags = TestFlags::ONE;
@@ -165,7 +165,7 @@
TEST(Flags, String_UnknownValues) {
auto flags = Flags<TestFlags>(0b1011);
- ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000");
}
TEST(FlagsIterator, IteratesOverAllFlags) {
@@ -210,18 +210,4 @@
ASSERT_EQ(++iter, flags.end());
}
-TEST(FlagNames, RuntimeFlagName) {
- TestFlags f = TestFlags::ONE;
- ASSERT_EQ(flag_name(f), "ONE");
-}
-
-TEST(FlagNames, RuntimeUnknownFlagName) {
- TestFlags f = static_cast<TestFlags>(0x8);
- ASSERT_EQ(flag_name(f), std::nullopt);
-}
-
-TEST(FlagNames, CompileTimeFlagName) {
- static_assert(flag_name<TestFlags::TWO>() == "TWO");
-}
-
-} // namespace android::test
\ No newline at end of file
+} // namespace android::test
diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp
deleted file mode 100644
index dff2b8a..0000000
--- a/libs/ftl/NamedEnum_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <ftl/NamedEnum.h>
-
-namespace android {
-
-// Test enum class maximum enum value smaller than default maximum of 8.
-enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
-// Big enum contains enum values greater than default maximum of 8.
-enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
-
-// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
-template <>
-constexpr size_t NamedEnum::max<TestBigEnums> = 16;
-
-namespace test {
-using android::TestBigEnums;
-using android::TestEnums;
-
-TEST(NamedEnum, RuntimeNamedEnum) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
-}
-
-// Test big enum
-TEST(NamedEnum, RuntimeBigNamedEnum) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeNamedEnumAsString) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::string(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::string(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::string(e), "SEVEN");
-}
-
-TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnum) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::string(e), "05");
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
-}
-
-TEST(NamedEnum, CompileTimeFlagName) {
- static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
- static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
-}
-
-} // namespace test
-
-} // namespace android
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
new file mode 100644
index 0000000..1fd43ab
--- /dev/null
+++ b/libs/ftl/enum_test.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+namespace {
+
+enum class E { A, B, C, F = 5, ftl_last = F };
+
+static_assert(ftl::enum_begin_v<E> == E::A);
+static_assert(ftl::enum_last_v<E> == E::F);
+static_assert(ftl::enum_size_v<E> == 6);
+
+static_assert(ftl::enum_name<E::B>() == "B");
+static_assert(ftl::enum_name<E::ftl_last>() == "F");
+static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+
+enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+
+static_assert(ftl::enum_begin_v<F> == F{0});
+static_assert(ftl::enum_last_v<F> == F{15});
+static_assert(ftl::enum_size_v<F> == 16);
+
+static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+
+// If a scoped enum is unsigned, its implicit range corresponds to its bit indices.
+enum class Flags : std::uint8_t {
+ kNone = 0,
+ kFlag1 = 0b0000'0010,
+ kFlag4 = 0b0001'0000,
+ kFlag7 = 0b1000'0000,
+ kMask = kFlag1 | kFlag4 | kFlag7,
+ kAll = 0b1111'1111
+};
+
+static_assert(ftl::enum_begin_v<Flags> == Flags{0});
+static_assert(ftl::enum_last_v<Flags> == Flags{7});
+static_assert(ftl::enum_size_v<Flags> == 8);
+
+static_assert(ftl::enum_name<Flags::kNone>() == "kNone");
+static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4");
+static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7");
+
+// Though not flags, the enumerators are within the implicit range of bit indices.
+enum class Planet : std::uint8_t {
+ kMercury,
+ kVenus,
+ kEarth,
+ kMars,
+ kJupiter,
+ kSaturn,
+ kUranus,
+ kNeptune
+};
+
+constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1}; // Honorable mention.
+
+static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury);
+static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune);
+static_assert(ftl::enum_size_v<Planet> == 8);
+
+static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury");
+static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn");
+
+// Unscoped enum must define explicit range, even if the underlying type is fixed.
+enum Temperature : int {
+ kRoom = 20,
+ kFridge = 4,
+ kFreezer = -18,
+
+ ftl_first = kFreezer,
+ ftl_last = kRoom
+};
+
+static_assert(ftl::enum_begin_v<Temperature> == kFreezer);
+static_assert(ftl::enum_last_v<Temperature> == kRoom);
+static_assert(ftl::enum_size_v<Temperature> == 39);
+
+static_assert(ftl::enum_name<kFreezer>() == "kFreezer");
+static_assert(ftl::enum_name<kFridge>() == "kFridge");
+static_assert(ftl::enum_name<kRoom>() == "kRoom");
+
+} // namespace
+
+TEST(Enum, Range) {
+ std::string string;
+ for (E v : ftl::enum_range<E>()) {
+ string += ftl::enum_name(v).value_or("?");
+ }
+ EXPECT_EQ(string, "ABC??F");
+}
+
+TEST(Enum, Name) {
+ {
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_name(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt);
+ }
+}
+
+TEST(Enum, String) {
+ {
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0");
+ EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010");
+ EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_string(kPluto), "8");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_string(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100");
+ }
+}
+
+} // namespace android::test
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e5a2151..379b090 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -508,21 +508,7 @@
}
}
- auto mergeTransaction =
- [&t, currentFrameNumber = bufferItem.mFrameNumber](
- std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
- auto& [targetFrameNumber, transaction] = pendingTransaction;
- if (currentFrameNumber < targetFrameNumber) {
- return false;
- }
- t->merge(std::move(transaction));
- return true;
- };
-
- mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
- mPendingTransactions.end(), mergeTransaction),
- mPendingTransactions.end());
-
+ mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
t->setApplyToken(mApplyToken).apply();
}
@@ -630,7 +616,10 @@
class BBQSurface : public Surface {
private:
+ std::mutex mMutex;
sp<BLASTBufferQueue> mBbq;
+ bool mDestroyed = false;
+
public:
BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
@@ -650,6 +639,10 @@
status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override {
+ std::unique_lock _lock{mMutex};
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
"BBQSurface::setFrameRate")) {
return BAD_VALUE;
@@ -658,8 +651,20 @@
}
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ std::unique_lock _lock{mMutex};
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
return mBbq->setFrameTimelineInfo(frameTimelineInfo);
}
+
+ void destroy() override {
+ Surface::destroy();
+
+ std::unique_lock _lock{mMutex};
+ mDestroyed = true;
+ mBbq = nullptr;
+ }
};
// TODO: Can we coalesce this with frame updates? Need to confirm
@@ -707,6 +712,32 @@
}
}
+void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {
+ std::lock_guard _lock{mMutex};
+
+ SurfaceComposerClient::Transaction t;
+ mergePendingTransactions(&t, frameNumber);
+ t.setApplyToken(mApplyToken).apply();
+}
+
+void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
+ uint64_t frameNumber) {
+ auto mergeTransaction =
+ [&t, currentFrameNumber = frameNumber](
+ std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ auto& [targetFrameNumber, transaction] = pendingTransaction;
+ if (currentFrameNumber < targetFrameNumber) {
+ return false;
+ }
+ t->merge(std::move(transaction));
+ return true;
+ };
+
+ mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+ mPendingTransactions.end(), mergeTransaction),
+ mPendingTransactions.end());
+}
+
// Maintains a single worker thread per process that services a list of runnables.
class AsyncWorker : public Singleton<AsyncWorker> {
private:
@@ -837,4 +868,9 @@
}
}
+uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() {
+ std::unique_lock _lock{mMutex};
+ return mLastAcquiredFrameNumber;
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index df308d8..5fe5e71 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -645,7 +645,10 @@
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+ // TODO(http://b/140581935): This message is BQ_LOGW because it
+ // often logs when no actionable errors are present. Return to
+ // using BQ_LOGE after ensuring this only logs during errors.
+ BQ_LOGW("detachBuffer: slot %d is not owned by the producer "
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2edb4e4..353a91d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2622,4 +2622,14 @@
return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
}
+sp<IBinder> Surface::getSurfaceControlHandle() const {
+ Mutex::Autolock lock(mMutex);
+ return mSurfaceControlHandle;
+}
+
+void Surface::destroy() {
+ Mutex::Autolock lock(mMutex);
+ mSurfaceControlHandle = nullptr;
+}
+
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 05554ca..1bca6f9 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -194,7 +194,7 @@
void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
sp<SurfaceControl> surfaceControl) {
std::lock_guard<std::mutex> lock(mMutex);
- mJankListeners.insert({surfaceControl->getHandle(), listener});
+ mJankListeners.insert({surfaceControl->getLayerId(), listener});
}
void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
@@ -223,8 +223,8 @@
void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
- mSurfaceStatsListeners.insert({surfaceControl->getHandle(),
- SurfaceStatsCallbackEntry(context, cookie, listener)});
+ mSurfaceStatsListeners.insert(
+ {surfaceControl->getLayerId(), SurfaceStatsCallbackEntry(context, cookie, listener)});
}
void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) {
@@ -254,7 +254,7 @@
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
- std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
+ std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -352,13 +352,26 @@
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
}
+
for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ // The callbackMap contains the SurfaceControl object, which we need to look up the
+ // layerId. Since we don't know which callback contains the SurfaceControl, iterate
+ // through all until the SC is found.
+ int32_t layerId = -1;
+ for (auto callbackId : transactionStats.callbackIds) {
+ sp<SurfaceControl> sc =
+ callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
+ if (sc != nullptr) {
+ layerId = sc->getLayerId();
+ break;
+ }
+ }
+
{
// Acquire surface stats listener lock such that we guarantee that after calling
// unregister, there won't be any further callback.
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
- auto listenerRange = mSurfaceStatsListeners.equal_range(
- surfaceStats.surfaceControl);
+ auto listenerRange = mSurfaceStatsListeners.equal_range(layerId);
for (auto it = listenerRange.first; it != listenerRange.second; it++) {
auto entry = it->second;
entry.callback(entry.context, transactionStats.latchTime,
@@ -367,7 +380,7 @@
}
if (surfaceStats.jankData.empty()) continue;
- auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
+ auto jankRange = jankListenersMap.equal_range(layerId);
for (auto it = jankRange.first; it != jankRange.second; it++) {
it->second->onJankDataAvailable(surfaceStats.jankData);
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 6c5b2aa..1f517f6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -94,12 +94,12 @@
uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
+ void applyPendingTransactions(uint64_t frameNumber);
void setTransactionCompleteCallback(uint64_t frameNumber,
std::function<void(int64_t)>&& transactionCompleteCallback);
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
SurfaceComposerClient::Transaction* outTransaction = nullptr);
- void flushShadowQueue() {}
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -107,6 +107,7 @@
void setSidebandStream(const sp<NativeHandle>& stream);
uint32_t getLastTransformHint() const;
+ uint64_t getLastAcquiredFrameNum();
virtual ~BLASTBufferQueue();
@@ -125,6 +126,8 @@
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
+ void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
+ REQUIRES(mMutex);
std::string mName;
// Represents the queued buffer count from buffer queue,
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 7e4143b..e540351 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -99,7 +99,7 @@
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
- sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+ sp<IBinder> getSurfaceControlHandle() const;
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
@@ -333,6 +333,7 @@
virtual int connect(
int api, bool reportBufferRemoval,
const sp<SurfaceListener>& sListener);
+ virtual void destroy();
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
// from this Surface will be collected and returned here. Once this method returns, these
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index a980ce2..baa6878 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -685,13 +685,13 @@
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
GUARDED_BY(mMutex);
- std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+ std::multimap<int32_t, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash>
mReleaseBufferCallbacks GUARDED_BY(mMutex);
// This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
// std::recursive_mutex
- std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
+ std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
public:
static sp<TransactionCompletedListener> getInstance();
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 7b7c923..54a372c 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -71,8 +71,9 @@
SLIPPERY = 0x20000000,
LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
- }; // Window types from WindowManager.LayoutParams
+ };
+ // Window types from WindowManager.LayoutParams
enum class Type : int32_t {
UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
@@ -87,43 +88,55 @@
APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- STATUS_BAR = FIRST_SYSTEM_WINDOW,
- SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
- PHONE = FIRST_SYSTEM_WINDOW + 2,
- SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
- KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
- TOAST = FIRST_SYSTEM_WINDOW + 5,
- SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
- PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
- SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
- KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
- SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
- INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
- INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
- WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
- STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
- SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
- DRAG = FIRST_SYSTEM_WINDOW + 16,
- STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
- POINTER = FIRST_SYSTEM_WINDOW + 18,
- NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
- VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
- BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
- INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
- NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
- MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
- ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
- DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
- ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
- NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+
+#define FIRST_SYSTEM_WINDOW_ 2000
+
+ STATUS_BAR = FIRST_SYSTEM_WINDOW_,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1,
+ PHONE = FIRST_SYSTEM_WINDOW_ + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4,
+ TOAST = FIRST_SYSTEM_WINDOW_ + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15,
+ DRAG = FIRST_SYSTEM_WINDOW_ + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17,
+ POINTER = FIRST_SYSTEM_WINDOW_ + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40,
+
+ FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_,
LAST_SYSTEM_WINDOW = 2999,
+
+#undef FIRST_SYSTEM_WINDOW_
+
+ // Small range to limit LUT size.
+ ftl_first = FIRST_SYSTEM_WINDOW,
+ ftl_last = FIRST_SYSTEM_WINDOW + 15
};
- enum class Feature {
- DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- NO_INPUT_CHANNEL = 0x00000002,
- DISABLE_USER_ACTIVITY = 0x00000004,
+ enum class Feature : uint32_t {
+ DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
+ NO_INPUT_CHANNEL = 1u << 1,
+ DISABLE_USER_ACTIVITY = 1u << 2,
+ DROP_INPUT = 1u << 3,
+ DROP_INPUT_IF_OBSCURED = 1u << 4,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -258,4 +271,4 @@
WindowInfo mInfo;
};
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 1e8ff94..037849e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -834,9 +834,9 @@
case AMOTION_EVENT_ACTION_OUTSIDE:
return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
+ return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
+ return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_HOVER_MOVE:
return "HOVER_MOVE";
case AMOTION_EVENT_ACTION_SCROLL:
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 220c8e1..69ae9a0 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,7 +21,7 @@
#include <ctype.h>
#include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -228,7 +228,7 @@
void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
if (mSensors.find(info.type) != mSensors.end()) {
ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
- NamedEnum::string(info.type).c_str());
+ ftl::enum_string(info.type).c_str());
}
mSensors.insert_or_assign(info.type, info);
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 1e93dfb..91ab008 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,10 +30,10 @@
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <log/log.h>
#include <utils/Trace.h>
-#include <ftl/NamedEnum.h>
#include <input/InputTransport.h>
using android::base::StringPrintf;
@@ -714,7 +714,7 @@
}
ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
- mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str());
return android::base::Error(UNKNOWN_ERROR);
}
@@ -856,7 +856,7 @@
case InputMessage::Type::TIMELINE: {
LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
"InputConsumer!",
- NamedEnum::string(mMsg.header.type).c_str());
+ ftl::enum_string(mMsg.header.type).c_str());
break;
}
@@ -1449,14 +1449,14 @@
out = out + "mChannel = " + mChannel->getName() + "\n";
out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
if (mMsgDeferred) {
- out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n";
+ out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
}
out += "Batches:\n";
for (const Batch& batch : mBatches) {
out += " Batch:\n";
for (const InputMessage& msg : batch.samples) {
out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
- NamedEnum::string(msg.header.type).c_str());
+ ftl::enum_string(msg.header.type).c_str());
switch (msg.header.type) {
case InputMessage::Type::KEY: {
out += android::base::StringPrintf("action=%s keycode=%" PRId32,
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index c365ab0..7c25cda 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,10 +16,8 @@
#define LOG_TAG "KeyLayoutMap"
-#include <stdlib.h>
-
#include <android/keycodes.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputEventLabels.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
@@ -28,6 +26,10 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
+#include <cstdlib>
+#include <string_view>
+#include <unordered_map>
+
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -39,36 +41,38 @@
namespace android {
+namespace {
-static const char* WHITESPACE = " \t\r";
+constexpr const char* WHITESPACE = " \t\r";
-#define SENSOR_ENTRY(type) NamedEnum::string(type), type
-static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
- {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
- {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
- {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
- {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
-
-// --- KeyLayoutMap ---
-
-KeyLayoutMap::KeyLayoutMap() {
+template <InputDeviceSensorType S>
+constexpr auto sensorPair() {
+ return std::make_pair(ftl::enum_name<S>(), S);
}
-KeyLayoutMap::~KeyLayoutMap() {
-}
+static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_LIST =
+ {sensorPair<InputDeviceSensorType::ACCELEROMETER>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD>(),
+ sensorPair<InputDeviceSensorType::ORIENTATION>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE>(),
+ sensorPair<InputDeviceSensorType::LIGHT>(),
+ sensorPair<InputDeviceSensorType::PRESSURE>(),
+ sensorPair<InputDeviceSensorType::TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::PROXIMITY>(),
+ sensorPair<InputDeviceSensorType::GRAVITY>(),
+ sensorPair<InputDeviceSensorType::LINEAR_ACCELERATION>(),
+ sensorPair<InputDeviceSensorType::ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::RELATIVE_HUMIDITY>(),
+ sensorPair<InputDeviceSensorType::AMBIENT_TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::GAME_ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+
+} // namespace
+
+KeyLayoutMap::KeyLayoutMap() = default;
+KeyLayoutMap::~KeyLayoutMap() = default;
base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
const char* contents) {
@@ -160,8 +164,8 @@
const Sensor& sensor = it->second;
#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
- NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+ ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+ ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
#endif
return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
}
@@ -513,7 +517,7 @@
}
static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
- auto it = SENSOR_LIST.find(std::string(token));
+ auto it = SENSOR_LIST.find(token);
if (it == SENSOR_LIST.end()) {
return std::nullopt;
}
@@ -581,8 +585,8 @@
int32_t sensorDataIndex = indexOpt.value();
#if DEBUG_PARSER
- ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
- NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+ ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ftl::enum_string(sensorType).c_str(), sensorDataIndex);
#endif
Sensor sensor;
@@ -591,4 +595,5 @@
map.emplace(code, sensor);
return NO_ERROR;
}
-};
+
+} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 6a14d4f..ba71960 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -192,6 +192,7 @@
// Only poke DisplayManagerGlobal to unregister if we previously registered
// callbacks.
if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ gChoreographers.registeredToDisplayManager = false;
JNIEnv* env = getJniEnv();
if (env == nullptr) {
ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7f01135..0bc2b5d 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1089,10 +1089,13 @@
/**
* Retrieves an identifier for the next frame to be queued by this window.
*
- * \return the next frame id.
+ * Frame ids start at 1 and are incremented on each new frame until the underlying surface changes,
+ * in which case the frame id is reset to 1.
+ *
+ * \return the next frame id (0 being uninitialized).
*/
-static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
- int64_t value;
+static inline uint64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+ uint64_t value;
window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
return value;
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 7c5bee9..6af6195 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -39,15 +39,15 @@
uniform float2 in_maxSizeXY;
half4 main(float2 xy) {
- half4 c = sample(input, xy);
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ half4 c = input.eval(xy);
+ c += input.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += input.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += input.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
+ c += input.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
+ clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
return half4(c.rgb * 0.2, 1.0);
}
@@ -65,7 +65,7 @@
uniform float mixFactor;
half4 main(float2 xy) {
- return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
+ return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor));
}
)");
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index fc45af9..dc5fe17 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -391,7 +391,7 @@
shader.append(R"(
uniform shader input;
half4 main(float2 xy) {
- float4 c = float4(sample(input, xy));
+ float4 c = float4(input.eval(xy));
)");
if (undoPremultipliedAlpha) {
shader.append(R"(
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 4ac5c40..c262e35 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -184,7 +184,7 @@
);
coord.x = (outU - uScrollX) * viewportWidth;
coord.y = (outV - uScrollY) * viewportHeight;
- return sample(uContentTexture, coord);
+ return uContentTexture.eval(coord);
})");
const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index b3b726d..694bda6 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -424,13 +424,11 @@
ASSERT_TRUE(result.valid());
auto [status, fence] = result.get();
- int fd = fence.release();
- if (fd >= 0) {
- sync_wait(fd, -1);
- close(fd);
+ ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
}
- ASSERT_EQ(NO_ERROR, status);
if (layers.size() > 0 && mGLESRE != nullptr) {
ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
}
@@ -1317,31 +1315,9 @@
mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
ASSERT_TRUE(result.valid());
- auto [status, _] = result.get();
+ auto [status, fence] = result.get();
ASSERT_EQ(BAD_VALUE, status);
-}
-
-TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
- initializeRenderEngine();
-
- renderengine::DisplaySettings settings;
- settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
-
- std::vector<const renderengine::LayerSettings*> layers;
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layer.alpha = 1.0;
- layers.push_back(&layer);
-
- std::future<renderengine::RenderEngineResult> result =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
- ASSERT_TRUE(result.valid());
- auto [status, _] = result.get();
- ASSERT_EQ(NO_ERROR, status);
- expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+ ASSERT_FALSE(fence.ok());
}
TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
@@ -1369,8 +1345,13 @@
std::future<renderengine::RenderEngineResult> result =
mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
ASSERT_TRUE(result.valid());
- auto [status, _] = result.get();
+ auto [status, fence] = result.get();
+
ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
+ }
+
ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
@@ -1780,11 +1761,10 @@
ASSERT_TRUE(resultTwo.valid());
auto [statusTwo, fenceTwo] = resultTwo.get();
ASSERT_EQ(NO_ERROR, statusTwo);
-
- const int fd = fenceTwo.get();
- if (fd >= 0) {
- sync_wait(fd, -1);
+ if (fenceTwo.ok()) {
+ sync_wait(fenceTwo.get(), -1);
}
+
// Only cleanup the first time.
EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
mRE->cleanupPostRender();
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 33b3e1e..71b0f5f 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -287,16 +287,16 @@
// --- NotifyPointerCaptureChangedArgs ---
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
- bool enabled)
- : NotifyArgs(id, eventTime), enabled(enabled) {}
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+ int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+ : NotifyArgs(id, eventTime), request(request) {}
NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
const NotifyPointerCaptureChangedArgs& other)
- : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+ : NotifyArgs(other.id, other.eventTime), request(other.request) {}
bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
- return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+ return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
}
void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index f26a9a9..a864cf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,12 +19,12 @@
//#define LOG_NDEBUG 0
#include "InputReaderBase.h"
-#include <ftl/NamedEnum.h>
#include "input/DisplayViewport.h"
#include "input/Input.h"
-#include <android/log.h>
#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <ftl/enum.h>
#define INDENT " "
#define INDENT2 " "
@@ -67,6 +67,9 @@
if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
result += "EXTERNAL_STYLUS_PRESENCE | ";
}
+ if (changes & CHANGE_POINTER_CAPTURE) {
+ result += "POINTER_CAPTURE | ";
+ }
if (changes & CHANGE_ENABLED_STATE) {
result += "ENABLED_STATE | ";
}
@@ -114,7 +117,7 @@
}
if (count > 1) {
ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
- NamedEnum::string(type).c_str());
+ ftl::enum_string(type).c_str());
}
return result;
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5e9facf..6ce0313 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -116,7 +116,7 @@
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
- void setPointerCapture(bool enabled) override {}
+ void setPointerCapture(const PointerCaptureRequest&) override {}
void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 288068f..571c126 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -119,15 +119,15 @@
// PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
// for all entries.
PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
- bool hasPointerCapture)
+ const PointerCaptureRequest& request)
: EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
- pointerCaptureEnabled(hasPointerCapture) {}
+ pointerCaptureRequest(request) {}
PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
std::string PointerCaptureChangedEntry::getDescription() const {
return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
- pointerCaptureEnabled ? "true" : "false");
+ pointerCaptureRequest.enable ? "true" : "false");
}
// --- DragEntry ---
@@ -192,6 +192,18 @@
interceptKeyWakeupTime = 0;
}
+// --- TouchModeEntry ---
+
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode)
+ : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
+ inTouchMode(inTouchMode) {}
+
+TouchModeEntry::~TouchModeEntry() {}
+
+std::string TouchModeEntry::getDescription() const {
+ return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
+}
+
// --- MotionEntry ---
MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 8feb982..5365a78 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -39,6 +39,9 @@
SENSOR,
POINTER_CAPTURE_CHANGED,
DRAG,
+ TOUCH_MODE_CHANGED,
+
+ ftl_last = TOUCH_MODE_CHANGED
};
int32_t id;
@@ -104,9 +107,9 @@
};
struct PointerCaptureChangedEntry : EventEntry {
- bool pointerCaptureEnabled;
+ const PointerCaptureRequest pointerCaptureRequest;
- PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+ PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
std::string getDescription() const override;
~PointerCaptureChangedEntry() override;
@@ -185,7 +188,7 @@
float xOffset, float yOffset);
std::string getDescription() const override;
- virtual ~MotionEntry();
+ ~MotionEntry() override;
};
struct SensorEntry : EventEntry {
@@ -207,6 +210,15 @@
~SensorEntry() override;
};
+struct TouchModeEntry : EventEntry {
+ bool inTouchMode;
+
+ TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode);
+ std::string getDescription() const override;
+
+ ~TouchModeEntry() override;
+};
+
// Tracks the progress of dispatching a particular event to a particular connection.
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 4a75773..600f02b 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -27,7 +27,7 @@
#include <android-base/stringprintf.h>
#include <binder/Binder.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <gui/WindowInfo.h>
#include <log/log.h>
@@ -65,7 +65,7 @@
if (result == Focusability::OK) {
return std::nullopt;
}
- removeFocusReason = NamedEnum::string(result);
+ removeFocusReason = ftl::enum_string(result);
}
// We don't have a focused window or the currently focused window is no longer focusable. Check
@@ -79,7 +79,7 @@
if (result == Focusability::OK) {
return updateFocusedWindow(displayId,
"Window became focusable. Previous reason: " +
- NamedEnum::string(previousResult),
+ ftl::enum_string(previousResult),
requestedFocus, request->windowName);
}
}
@@ -116,7 +116,7 @@
request.token, request.windowName);
}
ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
- request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ request.windowName.c_str(), displayId, ftl::enum_string(result).c_str());
return std::nullopt;
}
@@ -134,7 +134,7 @@
// The requested window is not currently focusable. Wait for the window to become focusable
// but remove focus from the current window so that input events can go into a pending queue
// and be sent to the window when it becomes focused.
- return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
nullptr);
}
@@ -212,7 +212,7 @@
for (const auto& [displayId, request] : mFocusRequestByDisplay) {
auto it = mLastFocusResultByDisplay.find(displayId);
std::string result =
- it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
displayId, request.windowName.c_str(), result.c_str());
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index 1d6cd9a..6d11a77 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -77,6 +77,8 @@
NO_WINDOW,
NOT_FOCUSABLE,
NOT_VISIBLE,
+
+ ftl_last = NOT_VISIBLE
};
// Checks if the window token can be focused on a display. The token can be focused if there is
@@ -113,4 +115,4 @@
std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId);
};
-} // namespace android::inputdispatcher
\ No newline at end of file
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 84f5a18..da3e237 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -27,6 +27,7 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <ftl/enum.h>
#include <gui/SurfaceComposerClient.h>
#include <input/InputDevice.h>
#include <log/log.h>
@@ -560,7 +561,6 @@
mInTouchMode(true),
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
- mFocusedWindowRequestedPointerCapture(false),
mWindowTokenWithPointerCapture(nullptr),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator),
@@ -827,6 +827,14 @@
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED: {
+ const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
+ dispatchTouchModeChangeLocked(currentTime, typedEntry);
+ done = true;
+ dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
+ break;
+ }
+
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto typedEntry =
std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
@@ -1007,6 +1015,7 @@
LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
@@ -1141,9 +1150,10 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
- LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str());
+ LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str());
break;
}
}
@@ -1332,36 +1342,51 @@
void InputDispatcher::dispatchPointerCaptureChangedLocked(
nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
DropReason& dropReason) {
+ dropReason = DropReason::NOT_DROPPED;
+
const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
- if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
- LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
- }
- if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
- // Pointer capture was already forcefully disabled because of focus change.
- dropReason = DropReason::NOT_DROPPED;
- return;
- }
-
- // Set drop reason for early returns
- dropReason = DropReason::NO_POINTER_CAPTURE;
-
sp<IBinder> token;
- if (entry->pointerCaptureEnabled) {
- // Enable Pointer Capture
- if (!mFocusedWindowRequestedPointerCapture) {
+
+ if (entry->pointerCaptureRequest.enable) {
+ // Enable Pointer Capture.
+ if (haveWindowWithPointerCapture &&
+ (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
+ LOG_ALWAYS_FATAL("This request to enable Pointer Capture has already been dispatched "
+ "to the window.");
+ }
+ if (!mCurrentPointerCaptureRequest.enable) {
// This can happen if a window requests capture and immediately releases capture.
ALOGW("No window requested Pointer Capture.");
+ dropReason = DropReason::NO_POINTER_CAPTURE;
return;
}
+ if (entry->pointerCaptureRequest.seq != mCurrentPointerCaptureRequest.seq) {
+ ALOGI("Skipping dispatch of Pointer Capture being enabled: sequence number mismatch.");
+ return;
+ }
+
token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
mWindowTokenWithPointerCapture = token;
} else {
- // Disable Pointer Capture
+ // Disable Pointer Capture.
+ // We do not check if the sequence number matches for requests to disable Pointer Capture
+ // for two reasons:
+ // 1. Pointer Capture can be disabled by a focus change, which means we can get two entries
+ // to disable capture with the same sequence number: one generated by
+ // disablePointerCaptureForcedLocked() and another as an acknowledgement of Pointer
+ // Capture being disabled in InputReader.
+ // 2. We respect any request to disable Pointer Capture generated by InputReader, since the
+ // actual Pointer Capture state that affects events being generated by input devices is
+ // in InputReader.
+ if (!haveWindowWithPointerCapture) {
+ // Pointer capture was already forcefully disabled because of focus change.
+ dropReason = DropReason::NOT_DROPPED;
+ return;
+ }
token = mWindowTokenWithPointerCapture;
mWindowTokenWithPointerCapture = nullptr;
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
}
@@ -1370,8 +1395,7 @@
if (channel == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
return;
@@ -1385,6 +1409,43 @@
dropReason = DropReason::NOT_DROPPED;
}
+void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
+ const std::shared_ptr<TouchModeEntry>& entry) {
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ getWindowHandlesLocked(mFocusedDisplayId);
+ if (windowHandles.empty()) {
+ return;
+ }
+ const std::vector<InputTarget> inputTargets =
+ getInputTargetsFromWindowHandlesLocked(windowHandles);
+ if (inputTargets.empty()) {
+ return;
+ }
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, inputTargets);
+}
+
+std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked(
+ const std::vector<sp<WindowInfoHandle>>& windowHandles) const {
+ std::vector<InputTarget> inputTargets;
+ for (const sp<WindowInfoHandle>& handle : windowHandles) {
+ // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only.
+ const sp<IBinder>& token = handle->getToken();
+ if (token == nullptr) {
+ continue;
+ }
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(token);
+ if (channel == nullptr) {
+ continue; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ inputTargets.push_back(target);
+ }
+ return inputTargets;
+}
+
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
@@ -1514,7 +1575,7 @@
ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
"source=0x%x, sensorType=%s",
entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- NamedEnum::string(entry->sensorType).c_str());
+ ftl::enum_string(entry->sensorType).c_str());
}
auto command = [this, entry]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -1531,7 +1592,7 @@
bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
- NamedEnum::string(sensorType).c_str());
+ ftl::enum_string(sensorType).c_str());
}
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -1646,13 +1707,13 @@
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, "
+ "action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, entry.action, entry.actionButton, entry.flags, entry.metaState,
- entry.buttonState, entry.edgeFlags, entry.xPrecision, entry.yPrecision,
- entry.downTime);
+ entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
+ entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
+ entry.xPrecision, entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
@@ -1747,11 +1808,12 @@
}
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
- ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
+ ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
}
@@ -1803,7 +1865,12 @@
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
- NamedEnum::string(entry.type).c_str(), displayId);
+ ftl::enum_string(entry.type).c_str(), displayId);
+ return InputEventInjectionResult::FAILED;
+ }
+
+ // Drop key events if requested by input feature
+ if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
return InputEventInjectionResult::FAILED;
}
@@ -1828,7 +1895,7 @@
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
- NamedEnum::string(entry.type).c_str());
+ ftl::enum_string(entry.type).c_str());
return InputEventInjectionResult::FAILED;
} else {
// Still waiting for the focused window
@@ -2052,6 +2119,11 @@
}
}
+ // Drop touch events if requested by input feature
+ if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
+ newTouchedWindowHandle = nullptr;
+ }
+
// Also don't send the new touch event to unresponsive gesture monitors
newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
@@ -2117,6 +2189,13 @@
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+
+ // Drop touch events if requested by input feature
+ if (newTouchedWindowHandle != nullptr &&
+ shouldDropInput(entry, newTouchedWindowHandle)) {
+ newTouchedWindowHandle = nullptr;
+ }
+
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -2622,8 +2701,7 @@
"frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
"], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
"hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
- (isTouchedWindow) ? "[TOUCHED] " : "",
- NamedEnum::string(info->type, "%" PRId32).c_str(),
+ isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(),
info->packageName.c_str(), info->ownerUid, info->id,
toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
info->frameTop, info->frameRight, info->frameBottom,
@@ -2739,6 +2817,10 @@
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED: {
+ break;
+ }
+
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
@@ -2746,7 +2828,7 @@
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
- NamedEnum::string(eventEntry.type).c_str());
+ ftl::enum_string(eventEntry.type).c_str());
break;
}
}
@@ -2791,7 +2873,7 @@
if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
"Entry type %s should not have FLAG_SPLIT",
- NamedEnum::string(eventEntry->type).c_str());
+ ftl::enum_string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2967,6 +3049,7 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
break;
@@ -2978,7 +3061,7 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- NamedEnum::string(newEntry.type).c_str());
+ ftl::enum_string(newEntry.type).c_str());
break;
}
}
@@ -3185,12 +3268,22 @@
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED: {
+ const TouchModeEntry& touchModeEntry =
+ static_cast<const TouchModeEntry&>(eventEntry);
+ status = connection->inputPublisher
+ .publishTouchModeEvent(dispatchEntry->seq, touchModeEntry.id,
+ touchModeEntry.inTouchMode);
+
+ break;
+ }
+
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto& captureEntry =
static_cast<const PointerCaptureChangedEntry&>(eventEntry);
status = connection->inputPublisher
.publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
- captureEntry.pointerCaptureEnabled);
+ captureEntry.pointerCaptureRequest.enable);
break;
}
@@ -3207,7 +3300,7 @@
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
- NamedEnum::string(eventEntry.type).c_str());
+ ftl::enum_string(eventEntry.type).c_str());
return;
}
}
@@ -3519,17 +3612,18 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("Canceling %s events is not supported",
- NamedEnum::string(cancelationEventEntry->type).c_str());
+ ftl::enum_string(cancelationEventEntry->type).c_str());
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- NamedEnum::string(cancelationEventEntry->type).c_str());
+ ftl::enum_string(cancelationEventEntry->type).c_str());
break;
}
}
@@ -3582,13 +3676,14 @@
case EventEntry::Type::KEY:
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- NamedEnum::string(downEventEntry->type).c_str());
+ ftl::enum_string(downEventEntry->type).c_str());
break;
}
}
@@ -3936,7 +4031,7 @@
ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
" sensorType=%s",
args->id, args->eventTime, args->deviceId, args->source,
- NamedEnum::string(args->sensorType).c_str());
+ ftl::enum_string(args->sensorType).c_str());
}
bool needWake;
@@ -4006,14 +4101,14 @@
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
if (DEBUG_INBOUND_EVENT_DETAILS) {
ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
- args->enabled ? "true" : "false");
+ args->request.enable ? "true" : "false");
}
bool needWake;
{ // acquire lock
std::scoped_lock _l(mLock);
auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
- args->enabled);
+ args->request);
needWake = enqueueInboundEventLocked(std::move(entry));
} // release lock
@@ -4572,6 +4667,18 @@
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
+ // Since we are about to drop the touch, cancel the events for the wallpaper as
+ // well.
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
+ touchedWindow.windowHandle->getInfo()->hasWallpaper) {
+ sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
+ if (wallpaper != nullptr) {
+ sp<Connection> wallpaperConnection =
+ getConnectionLocked(wallpaper->getToken());
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+ options);
+ }
+ }
}
state.windows.erase(state.windows.begin() + i);
} else {
@@ -4782,6 +4889,7 @@
void InputDispatcher::setInTouchMode(bool inTouchMode) {
std::scoped_lock lock(mLock);
mInTouchMode = inTouchMode;
+ // TODO(b/193718270): Fire TouchModeEvent here.
}
void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
@@ -4799,6 +4907,18 @@
mBlockUntrustedTouchesMode = mode;
}
+std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
+ const sp<IBinder>& token) {
+ for (auto& [displayId, state] : mTouchStatesByDisplay) {
+ for (TouchedWindow& w : state.windows) {
+ if (w.windowHandle->getToken() == token) {
+ return std::make_pair(&state, &w);
+ }
+ }
+ }
+ return std::make_pair(nullptr, nullptr);
+}
+
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) {
if (fromToken == toToken) {
@@ -4811,58 +4931,43 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
- if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
- ALOGW("Cannot transfer focus because from or to window not found.");
+ // Find the target touch state and touched window by fromToken.
+ auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+ if (state == nullptr || touchedWindow == nullptr) {
+ ALOGD("Focus transfer failed because from window is not being touched.");
return false;
}
+
+ const int32_t displayId = state->displayId;
+ sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+ if (toWindowHandle == nullptr) {
+ ALOGW("Cannot transfer focus because to window not found.");
+ return false;
+ }
+
if (DEBUG_FOCUS) {
ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
- fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
- }
- if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
- if (DEBUG_FOCUS) {
- ALOGD("Cannot transfer focus because windows are on different displays.");
- }
- return false;
+ touchedWindow->windowHandle->getName().c_str(),
+ toWindowHandle->getName().c_str());
}
- bool found = false;
- for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
- TouchState& state = pair.second;
- for (size_t i = 0; i < state.windows.size(); i++) {
- const TouchedWindow& touchedWindow = state.windows[i];
- if (touchedWindow.windowHandle == fromWindowHandle) {
- int32_t oldTargetFlags = touchedWindow.targetFlags;
- BitSet32 pointerIds = touchedWindow.pointerIds;
+ // Erase old window.
+ int32_t oldTargetFlags = touchedWindow->targetFlags;
+ BitSet32 pointerIds = touchedWindow->pointerIds;
+ state->removeWindowByToken(fromToken);
- state.windows.erase(state.windows.begin() + i);
+ // Add new window.
+ int32_t newTargetFlags = oldTargetFlags &
+ (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+ InputTarget::FLAG_DISPATCH_AS_IS);
+ state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
- int32_t newTargetFlags = oldTargetFlags &
- (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
- InputTarget::FLAG_DISPATCH_AS_IS);
- state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
- // Store the dragging window.
- if (isDragDrop) {
- mDragState = std::make_unique<DragState>(toWindowHandle);
- }
-
- found = true;
- goto Found;
- }
- }
- }
- Found:
-
- if (!found) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus transfer failed because from window did not have focus.");
- }
- return false;
+ // Store the dragging window.
+ if (isDragDrop) {
+ mDragState = std::make_unique<DragState>(toWindowHandle);
}
+ // Synthesize cancel for old window and down for new window.
sp<Connection> fromConnection = getConnectionLocked(fromToken);
sp<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4890,27 +4995,20 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+ auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
+ [](const auto& pair) { return pair.second.windows.size() == 1; });
+ if (it == mTouchStatesByDisplay.end()) {
+ ALOGW("Cannot transfer touch state because there is no exact window being touched");
+ return false;
+ }
+ const int32_t displayId = it->first;
+ sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
if (toWindowHandle == nullptr) {
ALOGW("Could not find window associated with token=%p", destChannelToken.get());
return false;
}
- const int32_t displayId = toWindowHandle->getInfo()->displayId;
-
- auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
- displayId);
- return false;
- }
-
- TouchState& state = touchStateIt->second;
- if (state.windows.size() != 1) {
- ALOGW("Cannot transfer touch state because there are %zu windows being touched",
- state.windows.size());
- return false;
- }
+ TouchState& state = it->second;
const TouchedWindow& touchedWindow = state.windows[0];
fromToken = touchedWindow.windowHandle->getToken();
} // release lock
@@ -4952,8 +5050,8 @@
std::string InputDispatcher::dumpPointerCaptureStateLocked() {
std::string dump;
- dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
- toString(mFocusedWindowRequestedPointerCapture));
+ dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
+ toString(mCurrentPointerCaptureRequest.enable));
std::string windowName = "None";
if (mWindowTokenWithPointerCapture) {
@@ -4962,7 +5060,7 @@
windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
: "token has capture without window";
}
- dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+ dump += StringPrintf(INDENT "Current Window with Pointer Capture: %s\n", windowName.c_str());
return dump;
}
@@ -5052,7 +5150,7 @@
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible), windowInfo->alpha,
windowInfo->flags.string().c_str(),
- NamedEnum::string(windowInfo->type).c_str(),
+ ftl::enum_string(windowInfo->type).c_str(),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
@@ -5435,14 +5533,13 @@
return;
}
- if (enabled == mFocusedWindowRequestedPointerCapture) {
+ if (enabled == mCurrentPointerCaptureRequest.enable) {
ALOGW("Ignoring request to %s Pointer Capture: "
"window has %s requested pointer capture.",
enabled ? "enable" : "disable", enabled ? "already" : "not");
return;
}
- mFocusedWindowRequestedPointerCapture = enabled;
setPointerCaptureLocked(enabled);
} // release lock
@@ -6063,14 +6160,13 @@
}
void InputDispatcher::disablePointerCaptureForcedLocked() {
- if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+ if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
return;
}
ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
- if (mFocusedWindowRequestedPointerCapture) {
- mFocusedWindowRequestedPointerCapture = false;
+ if (mCurrentPointerCaptureRequest.enable) {
setPointerCaptureLocked(false);
}
@@ -6087,14 +6183,16 @@
}
auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
- false /* hasCapture */);
+ mCurrentPointerCaptureRequest);
mInboundQueue.push_front(std::move(entry));
}
-void InputDispatcher::setPointerCaptureLocked(bool enabled) {
- auto command = [this, enabled]() REQUIRES(mLock) {
+void InputDispatcher::setPointerCaptureLocked(bool enable) {
+ mCurrentPointerCaptureRequest.enable = enable;
+ mCurrentPointerCaptureRequest.seq++;
+ auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->setPointerCapture(enabled);
+ mPolicy->setPointerCapture(request);
};
postCommandLocked(std::move(command));
}
@@ -6139,4 +6237,19 @@
mLooper->wake();
}
+bool InputDispatcher::shouldDropInput(
+ const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
+ if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
+ (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
+ isWindowObscuredLocked(windowHandle))) {
+ ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
+ "%" PRId32 ".",
+ ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
+ windowHandle->getInfo()->inputFeatures.string().c_str(),
+ windowHandle->getInfo()->displayId);
+ return true;
+ }
+ return false;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index afe4366..4f6d0d2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -364,6 +364,12 @@
bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
REQUIRES(mLock);
+ // Gets all the input targets (with their respective input channels) from the window handles
+ // passed as argument.
+ std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked(
+ const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const
+ REQUIRES(mLock);
+
/*
* Validate and update InputWindowHandles for a given display.
*/
@@ -386,10 +392,12 @@
// Keeps track of the focused window per display and determines focus changes.
FocusResolver mFocusResolver GUARDED_BY(mLock);
- // Whether the focused window on the focused display has requested Pointer Capture.
- // The state of this variable should always be in sync with the state of Pointer Capture in the
- // policy, which is updated through setPointerCaptureLocked(enabled).
- bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+ // The enabled state of this request is true iff the focused window on the focused display has
+ // requested Pointer Capture. This request also contains the sequence number associated with the
+ // current request. The state of this variable should always be in sync with the state of
+ // Pointer Capture in the policy, and is only updated through setPointerCaptureLocked(request).
+ PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
// The window token that has Pointer Capture.
// This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
@@ -399,7 +407,7 @@
void disablePointerCaptureForcedLocked() REQUIRES(mLock);
// Set the Pointer Capture state in the Policy.
- void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+ void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
@@ -424,6 +432,9 @@
void dispatchPointerCaptureChangedLocked(
nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
DropReason& dropReason) REQUIRES(mLock);
+ void dispatchTouchModeChangeLocked(nsecs_t currentTime,
+ const std::shared_ptr<TouchModeEntry>& entry)
+ REQUIRES(mLock);
void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
@@ -546,6 +557,10 @@
std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
const sp<android::gui::WindowInfoHandle>& windowHandle);
+ bool shouldDropInput(const EventEntry& entry,
+ const sp<android::gui::WindowInfoHandle>& windowHandle) const
+ REQUIRES(mLock);
+
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -636,6 +651,10 @@
DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
+ // Find touched state and touched window by token.
+ std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
+ REQUIRES(mLock);
+
// Statistics gathering.
LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
LatencyTracker mLatencyTracker GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index b7ed658..4a50a68 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -134,4 +134,14 @@
return haveSlipperyForegroundWindow;
}
+sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& window = windows[i];
+ if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+ return window.windowHandle;
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 579b868..7dcf55d 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -51,6 +51,7 @@
void filterNonMonitors();
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
+ sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index a7dccf0..b74f304 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -202,6 +202,7 @@
* InputDispatcher is the source of truth of Pointer Capture.
*/
virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+
/* Flush input device motion sensor.
*
* Returns true on success.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index ebfcbe1..3c1e637 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -156,7 +156,7 @@
*
* InputDispatcher is solely responsible for updating the Pointer Capture state.
*/
- virtual void setPointerCapture(bool enabled) = 0;
+ virtual void setPointerCapture(const PointerCaptureRequest&) = 0;
/* Notifies the policy that the drag window has moved over to another window */
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
index 8da699d..0b44187 100644
--- a/services/inputflinger/docs/pointer_capture.md
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -17,6 +17,8 @@
`InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
+We use a sequence number to synchronize different requests to enable Pointer Capture between InputReader and InputDispatcher.
+
### Enabling Pointer Capture
There are four key steps that take place when Pointer Capture is enabled:
@@ -40,5 +42,5 @@
`InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
-- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mCurrentPointerCaptureRequest`: The sequence number of the current Pointer Capture request. This request is enabled iff the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
- `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 4b7d26d..fe74214 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -211,11 +211,12 @@
/* Describes a change in the state of Pointer Capture. */
struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
- bool enabled;
+ // The sequence number of the Pointer Capture request, if enabled.
+ PointerCaptureRequest request;
inline NotifyPointerCaptureChangedArgs() {}
- NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+ NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 7fdbbfd..3c8ac1c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -279,29 +279,30 @@
// True to show the location of touches on the touch screen as spots.
bool showTouches;
- // True if pointer capture is enabled.
- bool pointerCapture;
+ // The latest request to enable or disable Pointer Capture.
+ PointerCaptureRequest pointerCaptureRequest;
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
- InputReaderConfiguration() :
- virtualKeyQuietTime(0),
+ InputReaderConfiguration()
+ : virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
pointerGesturesEnabled(true),
- pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
- pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
- pointerGestureTapInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapSlop(10.0f), // 10 pixels
+ pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
+ pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
+ pointerGestureTapInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapSlop(10.0f), // 10 pixels
pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinDistance(15), // 15 pixels
- pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+ pointerGestureMultitouchMinDistance(15), // 15 pixels
+ pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
pointerGestureSwipeMaxWidthRatio(0.25f),
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false), pointerCapture(false) { }
+ showTouches(false),
+ pointerCaptureRequest() {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0f0ad0a..d10f8b6 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -40,6 +40,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
@@ -263,7 +264,7 @@
*/
static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot,
SysfsClass clazz) {
- std::string nodeStr = NamedEnum::string(clazz);
+ std::string nodeStr = ftl::enum_string(clazz);
std::for_each(nodeStr.begin(), nodeStr.end(),
[](char& c) { c = std::tolower(static_cast<unsigned char>(c)); });
std::vector<std::filesystem::path> nodes;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e71a9e9..86d4c26 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -367,9 +367,15 @@
}
if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
- const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
- mConfig.pointerCapture);
- mQueuedListener->notifyPointerCaptureChanged(&args);
+ if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
+ ALOGV("Skipping notifying pointer capture changes: "
+ "There was no change in the pointer capture state.");
+ } else {
+ mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
+ const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+ mCurrentPointerCaptureRequest);
+ mQueuedListener->notifyPointerCaptureChanged(&args);
+ }
}
}
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 9c8a29a..a693496 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -17,9 +17,9 @@
#include <locale>
#include <regex>
-#include "../Macros.h"
+#include <ftl/enum.h>
-#include <ftl/NamedEnum.h>
+#include "../Macros.h"
#include "PeripheralController.h"
// Log detailed debug messages about input device lights.
@@ -286,7 +286,7 @@
for (const auto& [lightId, light] : mLights) {
dump += StringPrintf(INDENT4 "Id: %d", lightId);
dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
- dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+ dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
light->dump(dump);
}
}
@@ -487,7 +487,7 @@
auto& light = it->second;
if (DEBUG_LIGHT_DETAILS) {
ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color);
+ ftl::enum_string(light->type).c_str(), color);
}
return light->setLightColor(color);
}
@@ -501,7 +501,7 @@
std::optional<int32_t> color = light->getLightColor();
if (DEBUG_LIGHT_DETAILS) {
ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color.value_or(0));
+ ftl::enum_string(light->type).c_str(), color.value_or(0));
}
return color;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 7a00bac..1f96294 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -146,6 +146,8 @@
enum class SysfsClass : uint32_t {
POWER_SUPPLY = 0,
LEDS = 1,
+
+ ftl_last = LEDS
};
enum class LightColor : uint32_t {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index a00c5af..e44aa0f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -230,6 +230,8 @@
uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
+ PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
+
// state queries
typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 437902a..2ac41b1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -154,9 +154,9 @@
mHWheelScale = 1.0f;
}
- if ((!changes && config->pointerCapture) ||
+ if ((!changes && config->pointerCaptureRequest.enable) ||
(changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
- if (config->pointerCapture) {
+ if (config->pointerCaptureRequest.enable) {
if (mParameters.mode == Parameters::MODE_POINTER) {
mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index a507632..a1bd548 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -16,8 +16,9 @@
#include <locale>
-#include "../Macros.h"
+#include <ftl/enum.h>
+#include "../Macros.h"
#include "SensorInputMapper.h"
// Log detailed debug messages about each sensor event notification to the dispatcher.
@@ -93,7 +94,7 @@
dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp);
dump += INDENT3 "Sensors:\n";
for (const auto& [sensorType, sensor] : mSensors) {
- dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str());
+ dump += StringPrintf(INDENT4 "%s\n", ftl::enum_string(sensorType).c_str());
dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled);
dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count());
dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n",
@@ -208,10 +209,10 @@
axis.max /* maxRange */, axis.scale /* resolution */,
0.0f /* power */, 0 /* minDelay */,
0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */,
- NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */,
+ ftl::enum_string(sensorType), 0 /* maxDelay */, 0 /* flags */,
getDeviceId());
- std::string prefix = "sensor." + NamedEnum::string(sensorType);
+ std::string prefix = "sensor." + ftl::enum_string(sensorType);
transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
int32_t reportingMode = 0;
@@ -335,7 +336,7 @@
std::chrono::microseconds maxBatchReportLatency) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
- NamedEnum::string(sensorType).c_str(), samplingPeriod.count(),
+ ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
maxBatchReportLatency.count());
}
@@ -359,7 +360,7 @@
void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str());
+ ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
}
if (!setSensorEnabled(sensorType, false /* enabled */)) {
@@ -393,13 +394,12 @@
nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
if (DEBUG_SENSOR_EVENT_DETAILS) {
ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
- NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1],
- values[2]);
+ ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
}
if (sensor.lastSampleTimeNs.has_value() &&
timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str());
+ ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
}
} else {
// Convert to Android unit
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index b63aed7..419b0d0 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,9 +18,10 @@
#include "../Macros.h"
// clang-format on
-#include <ftl/NamedEnum.h>
#include "TouchInputMapper.h"
+#include <ftl/enum.h>
+
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "TouchButtonAccumulator.h"
@@ -259,7 +260,7 @@
void TouchInputMapper::dump(std::string& dump) {
dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
- NamedEnum::string(mDeviceMode).c_str());
+ ftl::enum_string(mDeviceMode).c_str());
dumpParameters(dump);
dumpVirtualKeys(dump);
dumpRawPointerAxes(dump);
@@ -515,9 +516,9 @@
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
- dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
+ dump += INDENT4 "GestureMode: " + ftl::enum_string(mParameters.gestureMode) + "\n";
- dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
+ dump += INDENT4 "DeviceType: " + ftl::enum_string(mParameters.deviceType) + "\n";
dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
"displayId='%s'\n",
@@ -525,7 +526,7 @@
toString(mParameters.associatedDisplayIsExternal),
mParameters.uniqueDisplayId.c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
- dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
+ dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n";
}
void TouchInputMapper::configureRawPointerAxes() {
@@ -621,7 +622,7 @@
// Determine device mode.
if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
+ mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
mSource = AINPUT_SOURCE_MOUSE;
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
@@ -804,11 +805,12 @@
// preserve the cursor position.
if (mDeviceMode == DeviceMode::POINTER ||
(mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
- (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
+ (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+ mConfig.pointerCaptureRequest.enable)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
- if (mConfig.pointerCapture) {
+ if (mConfig.pointerCaptureRequest.enable) {
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e104220..a56468f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -185,6 +185,8 @@
UNSCALED, // unscaled mapping (touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
POINTER, // pointer mapping (pointer)
+
+ ftl_last = POINTER
};
DeviceMode mDeviceMode;
@@ -198,6 +200,8 @@
TOUCH_PAD,
TOUCH_NAVIGATION,
POINTER,
+
+ ftl_last = POINTER
};
DeviceType deviceType;
@@ -210,6 +214,8 @@
ORIENTATION_90 = DISPLAY_ORIENTATION_90,
ORIENTATION_180 = DISPLAY_ORIENTATION_180,
ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+
+ ftl_last = ORIENTATION_270
};
Orientation orientation;
@@ -219,6 +225,8 @@
enum class GestureMode {
SINGLE_TOUCH,
MULTI_TOUCH,
+
+ ftl_last = MULTI_TOUCH
};
GestureMode gestureMode;
@@ -818,4 +826,4 @@
} // namespace android
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0fc4708..903f337 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -47,7 +47,8 @@
static const int32_t DEVICE_ID = 1;
// An arbitrary display id.
-static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t SECOND_DISPLAY_ID = 1;
// An arbitrary injector pid / uid pair that has permission to inject events.
static const int32_t INJECTOR_PID = 999;
@@ -73,6 +74,12 @@
return event;
}
+static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
+ ASSERT_EQ(expectedAction, receivedAction)
+ << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
+ << MotionEvent::actionToString(receivedAction);
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -243,19 +250,22 @@
mConfig.keyRepeatDelay = delay;
}
- void waitForSetPointerCapture(bool enabled) {
+ PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
[this, enabled]() REQUIRES(mLock) {
- return mPointerCaptureEnabled &&
- *mPointerCaptureEnabled ==
+ return mPointerCaptureRequest->enable ==
enabled;
})) {
- FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+ ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled
+ << ") to be called.";
+ return {};
}
- mPointerCaptureEnabled.reset();
+ auto request = *mPointerCaptureRequest;
+ mPointerCaptureRequest.reset();
+ return request;
}
void assertSetPointerCaptureNotCalled() {
@@ -263,11 +273,11 @@
base::ScopedLockAssertion assumeLocked(mLock);
if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
- FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+ FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
"enabled = "
- << *mPointerCaptureEnabled;
+ << std::to_string(mPointerCaptureRequest->enable);
}
- mPointerCaptureEnabled.reset();
+ mPointerCaptureRequest.reset();
}
void assertDropTargetEquals(const sp<IBinder>& targetToken) {
@@ -285,7 +295,8 @@
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
std::condition_variable mPointerCaptureChangedCondition;
- std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
+
+ std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
// ANR handling
std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
@@ -402,9 +413,9 @@
mOnPointerDownToken = newToken;
}
- void setPointerCapture(bool enabled) override {
+ void setPointerCapture(const PointerCaptureRequest& request) override {
std::scoped_lock lock(mLock);
- mPointerCaptureEnabled = {enabled};
+ mPointerCaptureRequest = {request};
mPointerCaptureChangedCondition.notify_all();
}
@@ -796,7 +807,8 @@
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
+
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
}
@@ -942,6 +954,15 @@
mInfo.displayId = displayId;
}
+ sp<FakeWindowHandle> clone(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+ sp<FakeWindowHandle> handle =
+ new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
+ displayId, mInfo.token);
+ return handle;
+ }
+
void setFocusable(bool focusable) { mInfo.focusable = focusable; }
void setVisible(bool visible) { mInfo.visible = visible; }
@@ -968,6 +989,10 @@
mInfo.addTouchableRegion(frame);
}
+ void setType(WindowInfo::Type type) { mInfo.type = type; }
+
+ void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
+
void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
@@ -1385,8 +1410,9 @@
return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
}
-static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
- return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
+ const PointerCaptureRequest& request) {
+ return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
}
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
@@ -1470,6 +1496,197 @@
windowSecond->assertNoEvents();
}
+/**
+ * Two windows: A top window, and a wallpaper behind the window.
+ * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window
+ * gets ACTION_CANCEL.
+ * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true)
+ * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setHasWallpaper(true);
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ foregroundWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ foregroundWindow->consumeMotionMove();
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Now the foreground window goes away, but the wallpaper stays
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ foregroundWindow->consumeMotionCancel();
+ // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
+ * A single window that receives touch (on top), and a wallpaper window underneath it.
+ * The top window gets a multitouch gesture.
+ * Ensure that wallpaper gets the same gesture.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ window->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+ // Touch down on top window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both top window and its wallpaper should receive the touch down
+ window->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the top window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(150)
+ .y(150))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionPointerDown(1 /* pointerIndex */);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+ window->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
+/**
+ * Two windows: a window on the left and window on the right.
+ * A third window, wallpaper, is behind both windows, and spans both top windows.
+ * The first touch down goes to the left window. A second pointer touches down on the right window.
+ * The touch is split, so both left and right windows should receive ACTION_DOWN.
+ * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by
+ * ACTION_POINTER_DOWN(1).
+ */
+TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+ leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ leftWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+ rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ rightWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+ // Touch down on left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ leftWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the right window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(300)
+ .y(100))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ leftWindow->consumeMotionMove();
+ // Since the touch is split, right window gets ACTION_DOWN
+ rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+
+ // Now, leftWindow, which received the first finger, disappears.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+ leftWindow->consumeMotionCancel();
+ // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // The pointer that's still down on the right window moves, and goes to the right window only.
+ // As far as the dispatcher's concerned though, both pointers are still present.
+ const MotionEvent secondFingerMoveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(310)
+ .y(110))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT));
+ rightWindow->consumeMotionMove();
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
@@ -2001,6 +2218,134 @@
secondWindow->assertNoEvents();
}
+// This case will create two windows and one mirrored window on the default display and mirror
+// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
+// the windows info of second display before default display.
+TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> firstWindowInPrimary =
+ new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+ firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+ firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> secondWindowInPrimary =
+ new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+ secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> mirrorWindowInPrimary =
+ firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+ mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+ mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> firstWindowInSecondary =
+ firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+ firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> secondWindowInSecondary =
+ secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+ secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ // Update window info, let it find window handle of second display first.
+ mDispatcher->setInputWindows(
+ {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+ {ADISPLAY_ID_DEFAULT,
+ {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // Transfer touch focus
+ ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
+ secondWindowInPrimary->getToken()));
+ // The first window gets cancel.
+ firstWindowInPrimary->consumeMotionCancel();
+ secondWindowInPrimary->consumeMotionDown();
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ firstWindowInPrimary->assertNoEvents();
+ secondWindowInPrimary->consumeMotionMove();
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ firstWindowInPrimary->assertNoEvents();
+ secondWindowInPrimary->consumeMotionUp();
+}
+
+// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
+// 'transferTouch' api.
+TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> firstWindowInPrimary =
+ new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+ firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+ firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> secondWindowInPrimary =
+ new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+ secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> mirrorWindowInPrimary =
+ firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+ mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+ mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> firstWindowInSecondary =
+ firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+ firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ sp<FakeWindowHandle> secondWindowInSecondary =
+ secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+ secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+ secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ // Update window info, let it find window handle of second display first.
+ mDispatcher->setInputWindows(
+ {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+ {ADISPLAY_ID_DEFAULT,
+ {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+ // Touch on second display.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+ // Transfer touch focus
+ ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
+
+ // The first window gets cancel.
+ firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
+ secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ firstWindowInPrimary->assertNoEvents();
+ secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ firstWindowInPrimary->assertNoEvents();
+ secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -2158,11 +2503,21 @@
expectedDisplayId, expectedFlags);
}
+ void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
expectedDisplayId, expectedFlags);
}
+ void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ expectedDisplayId, expectedFlags);
+ }
+
MotionEvent* consumeMotion() {
InputEvent* event = mInputReceiver->consume();
if (!event) {
@@ -2182,6 +2537,57 @@
std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
+/**
+ * Two entities that receive touch: A window, and a global monitor.
+ * The touch goes to the window, and then the window disappears.
+ * The monitor does not get cancel right away. But if more events come in, the touch gets canceled
+ * for the monitor, as well.
+ * 1. foregroundWindow
+ * 2. monitor <-- global monitor (doesn't observe z order, receives all events)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT,
+ false /*isGestureMonitor*/);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both the foreground window and the global monitor should receive the touch down
+ window->consumeMotionDown();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionMove();
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+ // Now the foreground window goes away
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ window->consumeMotionCancel();
+ monitor.assertNoEvents(); // Global monitor does not get a cancel yet
+
+ // If more events come in, there will be no more foreground window to send them to. This will
+ // cause a cancel for the monitor, as well.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {120, 200}))
+ << "Injection should fail because the window was removed";
+ window->assertNoEvents();
+ // Global monitor now gets the cancel
+ monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+}
+
// Tests for gesture monitors
TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -2921,7 +3327,6 @@
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
- static constexpr int32_t SECOND_DISPLAY_ID = 1;
virtual void SetUp() override {
InputDispatcherTest::SetUp();
@@ -3084,8 +3489,6 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
NotifyMotionArgs motionArgs;
@@ -3427,7 +3830,7 @@
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
@@ -4596,16 +4999,18 @@
mWindow->consumeFocusEvent(true);
}
- void notifyPointerCaptureChanged(bool enabled) {
- const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+ void notifyPointerCaptureChanged(const PointerCaptureRequest& request) {
+ const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request);
mDispatcher->notifyPointerCaptureChanged(&args);
}
- void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+ PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
+ bool enabled) {
mDispatcher->requestPointerCapture(window->getToken(), enabled);
- mFakePolicy->waitForSetPointerCapture(enabled);
- notifyPointerCaptureChanged(enabled);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+ notifyPointerCaptureChanged(request);
window->consumeCaptureEvent(enabled);
+ return request;
}
};
@@ -4627,7 +5032,7 @@
}
TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
- requestAndVerifyPointerCapture(mWindow, true);
+ auto request = requestAndVerifyPointerCapture(mWindow, true);
setFocusedWindow(mSecondWindow);
@@ -4635,26 +5040,26 @@
mWindow->consumeCaptureEvent(false);
mWindow->consumeFocusEvent(false);
mSecondWindow->consumeFocusEvent(true);
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
// Ensure that additional state changes from InputReader are not sent to the window.
- notifyPointerCaptureChanged(false);
- notifyPointerCaptureChanged(true);
- notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged({});
+ notifyPointerCaptureChanged(request);
+ notifyPointerCaptureChanged({});
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
mFakePolicy->assertSetPointerCaptureNotCalled();
}
TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
- requestAndVerifyPointerCapture(mWindow, true);
+ auto request = requestAndVerifyPointerCapture(mWindow, true);
// InputReader unexpectedly disables and enables pointer capture.
- notifyPointerCaptureChanged(false);
- notifyPointerCaptureChanged(true);
+ notifyPointerCaptureChanged({});
+ notifyPointerCaptureChanged(request);
// Ensure that Pointer Capture is disabled.
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
mWindow->consumeCaptureEvent(false);
mWindow->assertNoEvents();
}
@@ -4664,24 +5069,43 @@
// The first window loses focus.
setFocusedWindow(mSecondWindow);
- mFakePolicy->waitForSetPointerCapture(false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
mWindow->consumeCaptureEvent(false);
// Request Pointer Capture from the second window before the notification from InputReader
// arrives.
mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
- mFakePolicy->waitForSetPointerCapture(true);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
// InputReader notifies Pointer Capture was disabled (because of the focus change).
- notifyPointerCaptureChanged(false);
+ notifyPointerCaptureChanged({});
// InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
- notifyPointerCaptureChanged(true);
+ notifyPointerCaptureChanged(request);
mSecondWindow->consumeFocusEvent(true);
mSecondWindow->consumeCaptureEvent(true);
}
+TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
+ // App repeatedly enables and disables capture.
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+ mDispatcher->requestPointerCapture(mWindow->getToken(), false);
+ mFakePolicy->assertSetPointerCaptureCalled(false);
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+
+ // InputReader notifies that PointerCapture has been enabled for the first request. Since the
+ // first request is now stale, this should do nothing.
+ notifyPointerCaptureChanged(firstRequest);
+ mWindow->assertNoEvents();
+
+ // InputReader notifies that the second request was enabled.
+ notifyPointerCaptureChanged(secondRequest);
+ mWindow->consumeCaptureEvent(true);
+}
+
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
@@ -5310,4 +5734,134 @@
mSecondWindow->assertNoEvents();
}
+class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
+
+TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // With the flag cleared, the window should get input
+ window->setInputFeatures({});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) {
+ std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> obscuringWindow =
+ new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+ ADISPLAY_ID_DEFAULT);
+ obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+ obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setOwnerInfo(222, 222);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // With the flag cleared, the window should get input
+ window->setInputFeatures({});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) {
+ std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> obscuringWindow =
+ new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+ ADISPLAY_ID_DEFAULT);
+ obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+ obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setOwnerInfo(222, 222);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // When the window is no longer obscured because it went on top, it should get input
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index f06c044..5c430f5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -312,8 +312,9 @@
transform = t;
}
- void setPointerCapture(bool enabled) {
- mConfig.pointerCapture = enabled;
+ PointerCaptureRequest setPointerCapture(bool enabled) {
+ mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+ return mConfig.pointerCaptureRequest;
}
void setShowTouches(bool enabled) {
@@ -327,6 +328,8 @@
float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
private:
+ uint32_t mNextPointerCaptureSequenceNumber = 0;
+
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, bool isActive,
const std::string& uniqueId,
@@ -1993,24 +1996,24 @@
TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
NotifyPointerCaptureChangedArgs args;
- mFakePolicy->setPointerCapture(true);
+ auto request = mFakePolicy->setPointerCapture(true);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+ ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+ ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
mFakePolicy->setPointerCapture(false);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+ ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
- // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+ // Verify that the Pointer Capture state is not updated when the configuration value
// does not change.
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
mReader->loopOnce();
- mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+ mFakeListener->assertNotifyCaptureWasNotCalled();
}
class FakeVibratorInputMapper : public FakeInputMapper {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fb7de97..6a26c63 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -100,6 +100,11 @@
"to have been called."));
}
+void TestInputListener::assertNotifyCaptureWasNotCalled() {
+ ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyPointerCaptureChangedArgs>(
+ "notifyPointerCaptureChanged() should not be called."));
+}
+
template <class NotifyArgsType>
void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
std::unique_lock<std::mutex> lock(mLock);
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0ffcaaa..0a1dc4b 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -55,6 +55,7 @@
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+ void assertNotifyCaptureWasNotCalled();
void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr);
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index f310738..1ef8f78 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -21,12 +21,12 @@
#include <cinttypes>
+#include <android-base/stringprintf.h>
+
#include "ClientCache.h"
namespace android {
-using base::StringAppendF;
-
ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
@@ -212,16 +212,15 @@
void ClientCache::dump(std::string& result) {
std::lock_guard lock(mMutex);
- for (auto i : mBuffers) {
- const sp<IBinder>& cacheOwner = i.second.first;
- StringAppendF(&result," Cache owner: %p\n", cacheOwner.get());
- auto &buffers = i.second.second;
- for (auto& [id, clientCacheBuffer] : buffers) {
- StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
- (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
- (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
+ for (const auto& [_, cache] : mBuffers) {
+ base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get());
+
+ for (const auto& [id, entry] : cache.second) {
+ const auto& buffer = entry.buffer->getBuffer();
+ base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(),
+ buffer->getHeight());
}
}
}
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 58bb41a..a63145a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -28,51 +28,36 @@
// Geometrical space to which content is projected.
// For example, this can be the layer space or the physical display space.
-struct ProjectionSpace {
+class ProjectionSpace {
+public:
ProjectionSpace() = default;
- ProjectionSpace(ui::Size size, Rect content)
- : bounds(std::move(size)), content(std::move(content)) {}
-
- // Bounds of this space. Always starts at (0,0).
- Rect bounds;
-
- // Rect onto which content is projected.
- Rect content;
-
- // The orientation of this space. This value is meaningful only in relation to the rotation
- // of another projection space and it's used to determine the rotating transformation when
- // mapping between the two.
- // As a convention when using this struct orientation = 0 for the "oriented*" projection
- // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
- // of the display space will become 90, while the orientation of the layer stack space will
- // remain the same.
- ui::Rotation orientation = ui::ROTATION_0;
+ ProjectionSpace(ui::Size size, Rect content) : mBounds(size), mContent(std::move(content)) {}
// Returns a transform which maps this.content into destination.content
// and also rotates according to this.orientation and destination.orientation
ui::Transform getTransform(const ProjectionSpace& destination) const {
- ui::Rotation rotation = destination.orientation - orientation;
+ ui::Rotation rotation = destination.getOrientation() - mOrientation;
// Compute a transformation which rotates the destination in a way it has the same
// orientation as us.
const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
ui::Transform inverseRotatingTransform;
- inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
- destination.bounds.height());
+ inverseRotatingTransform.set(inverseRotationFlags, destination.getBounds().width,
+ destination.getBounds().height);
// The destination content rotated so it has the same orientation as us.
- Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+ Rect orientedDestContent = inverseRotatingTransform.transform(destination.getContent());
// Compute translation from the source content to (0, 0).
- const float sourceX = content.left;
- const float sourceY = content.top;
+ const float sourceX = mContent.left;
+ const float sourceY = mContent.top;
ui::Transform sourceTranslation;
sourceTranslation.set(-sourceX, -sourceY);
// Compute scaling transform which maps source content to destination content, assuming
// they are both at (0, 0).
ui::Transform scale;
- const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
- const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+ const float scaleX = static_cast<float>(orientedDestContent.width()) / mContent.width();
+ const float scaleY = static_cast<float>(orientedDestContent.height()) / mContent.height();
scale.set(scaleX, 0, 0, scaleY);
// Compute translation from (0, 0) to the orientated destination content.
@@ -83,8 +68,8 @@
// Compute rotation transform.
const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
- auto orientedDestWidth = destination.bounds.width();
- auto orientedDestHeight = destination.bounds.height();
+ auto orientedDestWidth = destination.getBounds().width;
+ auto orientedDestHeight = destination.getBounds().height;
if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
std::swap(orientedDestWidth, orientedDestHeight);
}
@@ -98,9 +83,39 @@
}
bool operator==(const ProjectionSpace& other) const {
- return bounds == other.bounds && content == other.content &&
- orientation == other.orientation;
+ return mBounds == other.mBounds && mContent == other.mContent &&
+ mOrientation == other.mOrientation;
}
+
+ void setBounds(ui::Size newBounds) { mBounds = std::move(newBounds); }
+
+ void setContent(Rect newContent) { mContent = std::move(newContent); }
+
+ void setOrientation(ui::Rotation newOrientation) { mOrientation = newOrientation; }
+
+ Rect getBoundsAsRect() const { return Rect(mBounds.getWidth(), mBounds.getHeight()); }
+
+ const ui::Size& getBounds() const { return mBounds; }
+
+ const Rect& getContent() const { return mContent; }
+
+ ui::Rotation getOrientation() const { return mOrientation; }
+
+private:
+ // Bounds of this space. Always starts at (0,0).
+ ui::Size mBounds = ui::Size();
+
+ // Rect onto which content is projected.
+ Rect mContent = Rect();
+
+ // The orientation of this space. This value is meaningful only in relation to the rotation
+ // of another projection space and it's used to determine the rotating transformation when
+ // mapping between the two.
+ // As a convention when using this struct orientation = 0 for the "oriented*" projection
+ // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+ // of the display space will become 90, while the orientation of the layer stack space will
+ // remain the same.
+ ui::Rotation mOrientation = ui::ROTATION_0;
};
} // namespace compositionengine
@@ -108,8 +123,8 @@
inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
return android::base::
StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
- to_string(space.bounds).c_str(), to_string(space.content).c_str(),
- toCString(space.orientation));
+ to_string(space.getBoundsAsRect()).c_str(),
+ to_string(space.getContent()).c_str(), toCString(space.getOrientation()));
}
// Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6b9ea87..02fa49f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -53,7 +53,7 @@
mId = args.id;
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
- editState().displaySpace.bounds = Rect(args.pixels);
+ editState().displaySpace.setBounds(args.pixels);
setName(args.name);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 31b699e..eb3f3b1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -156,38 +156,40 @@
const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
- outputState.displaySpace.orientation = orientation;
- LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ outputState.displaySpace.setOrientation(orientation);
+ LOG_FATAL_IF(outputState.displaySpace.getBoundsAsRect() == Rect::INVALID_RECT,
"The display bounds are unknown.");
// Compute orientedDisplaySpace
- ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+ ui::Size orientedSize = outputState.displaySpace.getBounds();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
}
- outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
- outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+ outputState.orientedDisplaySpace.setBounds(orientedSize);
+ outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);
// Compute displaySpace.content
const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
ui::Transform rotation;
if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
- const auto displaySize = outputState.displaySpace.bounds;
+ const auto displaySize = outputState.displaySpace.getBoundsAsRect();
rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
}
- outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+ outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));
// Compute framebufferSpace
- outputState.framebufferSpace.orientation = orientation;
- LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ outputState.framebufferSpace.setOrientation(orientation);
+ LOG_FATAL_IF(outputState.framebufferSpace.getBoundsAsRect() == Rect::INVALID_RECT,
"The framebuffer bounds are unknown.");
- const auto scale =
- getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
- outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+ const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
+ outputState.framebufferSpace.getBoundsAsRect());
+ outputState.framebufferSpace.setContent(
+ outputState.displaySpace.getContent().scale(scale.x, scale.y));
// Compute layerStackSpace
- outputState.layerStackSpace.content = layerStackSpaceRect;
- outputState.layerStackSpace.bounds = layerStackSpaceRect;
+ outputState.layerStackSpace.setContent(layerStackSpaceRect);
+ outputState.layerStackSpace.setBounds(
+ ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));
outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
@@ -200,21 +202,21 @@
auto& state = editState();
// Update framebuffer space
- const Rect newBounds(size);
- state.framebufferSpace.bounds = newBounds;
+ const ui::Size newBounds(size);
+ state.framebufferSpace.setBounds(newBounds);
// Update display space
- state.displaySpace.bounds = newBounds;
+ state.displaySpace.setBounds(newBounds);
state.transform = state.layerStackSpace.getTransform(state.displaySpace);
// Update oriented display space
- const auto orientation = state.displaySpace.orientation;
+ const auto orientation = state.displaySpace.getOrientation();
ui::Size orientedSize = size;
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
}
- const Rect newOrientedBounds(orientedSize);
- state.orientedDisplaySpace.bounds = newOrientedBounds;
+ const ui::Size newOrientedBounds(orientedSize);
+ state.orientedDisplaySpace.setBounds(newOrientedBounds);
if (mPlanner) {
mPlanner->setDisplaySize(size);
@@ -349,7 +351,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
const auto size = mRenderSurface->getSize();
- editState().framebufferSpace.bounds = Rect(size);
+ editState().framebufferSpace.setBounds(size);
if (mPlanner) {
mPlanner->setDisplaySize(size);
}
@@ -370,7 +372,7 @@
Region Output::getDirtyRegion() const {
const auto& outputState = getState();
- return outputState.dirtyRegion.intersect(outputState.layerStackSpace.content);
+ return outputState.dirtyRegion.intersect(outputState.layerStackSpace.getContent());
}
bool Output::includesLayer(ui::LayerFilter filter) const {
@@ -450,7 +452,7 @@
// Compute the resulting coverage for this output, and store it for later
const ui::Transform& tr = outputState.transform;
- Region undefinedRegion{outputState.displaySpace.bounds};
+ Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
outputState.undefinedRegion = undefinedRegion;
@@ -647,7 +649,7 @@
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(outputState.displaySpace.bounds);
+ drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect());
if (drawRegion.isEmpty()) {
return;
}
@@ -665,7 +667,7 @@
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
- visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
+ visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
outputLayerState.shadowRegion = shadowRegion;
}
@@ -1041,10 +1043,10 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
- clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+ clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
clientCompositionDisplay.orientation =
- ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+ ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
: ui::Dataspace::UNKNOWN;
@@ -1138,7 +1140,7 @@
ALOGV("Rendering client layers");
const auto& outputState = getState();
- const Region viewportRegion(outputState.layerStackSpace.content);
+ const Region viewportRegion(outputState.layerStackSpace.getContent());
bool firstLayer = true;
bool disableBlurs = false;
@@ -1201,7 +1203,7 @@
outputState.needsFiltering,
.isSecure = outputState.isSecure,
.supportsProtectedContent = supportsProtectedContent,
- .viewport = outputState.layerStackSpace.content,
+ .viewport = outputState.layerStackSpace.getContent(),
.dataspace = outputDataspace,
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
@@ -1308,7 +1310,7 @@
void Output::dirtyEntireOutput() {
auto& outputState = editState();
- outputState.dirtyRegion.set(outputState.displaySpace.bounds);
+ outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
}
void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 56e9d27..ecfd901 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -79,7 +79,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = getOutput().getState().layerStackSpace.content;
+ const Rect& viewport = getOutput().getState().layerStackSpace.getContent();
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -136,7 +136,7 @@
* buffer
*/
uint32_t invTransformOrient =
- ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+ ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
// calculate the inverse transform
if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -192,7 +192,7 @@
Rect activeCrop = layerState.geomCrop;
if (!activeCrop.isEmpty() && bufferSize.isValid()) {
activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
+ if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
activeCrop.clear();
}
activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -228,7 +228,7 @@
geomLayerBounds.bottom += outset;
}
Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
+ if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
frame.clear();
}
const ui::Transform displayTransform{outputState.transform};
@@ -628,7 +628,7 @@
const auto& outputState = getOutput().getState();
Rect frame = layerFEState->cursorFrame;
- frame.intersect(outputState.layerStackSpace.content, &frame);
+ frame.intersect(outputState.layerStackSpace.getContent(), &frame);
Rect position = outputState.transform.transform(frame);
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 5bbd58e..e6b716e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,13 +159,13 @@
void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
const OutputCompositionState& outputState) {
ATRACE_CALL();
- const Rect& viewport = outputState.layerStackSpace.content;
+ const Rect& viewport = outputState.layerStackSpace.getContent();
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
- ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+ ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = outputState.framebufferSpace.content,
+ .physicalDisplay = outputState.framebufferSpace.getContent(),
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
@@ -282,7 +282,7 @@
mOutputSpace = outputState.framebufferSpace;
mTexture = texture;
mTexture->setReadyFence(mDrawFence);
- mOutputSpace.orientation = outputState.framebufferSpace.orientation;
+ mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
mOutputDataspace = outputDataspace;
mOrientation = orientation;
mSkipCount = 0;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 936dba3..2532e3d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -93,11 +93,7 @@
void LayerState::dump(std::string& result) const {
for (const StateInterface* field : getNonUniqueFields()) {
- if (auto viewOpt = flag_name(field->getField()); viewOpt) {
- base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
- } else {
- result.append("<UNKNOWN FIELD>:\n");
- }
+ base::StringAppendF(&result, " %16s: ", ftl::flag_string(field->getField()).c_str());
bool first = true;
for (const std::string& line : field->toStrings()) {
@@ -126,11 +122,7 @@
continue;
}
- if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
- base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
- } else {
- result.append("<UNKNOWN FIELD>:\n");
- }
+ base::StringAppendF(&result, " %16s: ", ftl::flag_string(thisField->getField()).c_str());
const auto& thisStrings = thisField->toStrings();
const auto& otherStrings = otherField->toStrings();
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index f2978f9..ed235b8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -876,7 +876,7 @@
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
- mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
mDisplay->finishFrame({});
@@ -894,7 +894,7 @@
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
- gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
gpuDisplay->finishFrame({});
@@ -912,9 +912,8 @@
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
- gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
-
gpuDisplay->finishFrame({});
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c8c6012..53b71b7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -158,7 +158,7 @@
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferTransform = TR_IDENT;
- mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
}
FloatRect calculateOutputSourceCrop() {
@@ -229,7 +229,7 @@
mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
@@ -243,7 +243,7 @@
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
- mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -265,7 +265,7 @@
mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
- mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
mOutputState.transform = ui::Transform{TR_IDENT};
}
@@ -313,7 +313,7 @@
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
- mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
const Rect expected{0, 0, 960, 540};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -399,7 +399,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -511,7 +511,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -942,7 +942,7 @@
// This test simulates a scenario where displayInstallOrientation is set to
// ROT_90. This only has an effect on the transform; orientation stays 0 (see
// DisplayDevice::setProjection).
- mOutputState.displaySpace.orientation = ui::ROTATION_0;
+ mOutputState.displaySpace.setOrientation(ui::ROTATION_0);
mOutputState.transform = ui::Transform{TR_ROT_90};
// Buffers are pre-rotated based on the transform hint (ROT_90); their
// geomBufferTransform is set to the inverse transform.
@@ -1237,7 +1237,7 @@
mLayerFEState.cursorFrame = kDefaultCursorFrame;
- mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
+ mOutputState.layerStackSpace.setContent(kDefaultDisplayViewport);
mOutputState.transform = ui::Transform{kDefaultTransform};
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index e1c9aaa..bec7479 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -143,7 +143,8 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
}
@@ -289,8 +290,10 @@
TEST_F(OutputTest, setProjectionWorks) {
const Rect displayRect{0, 0, 1000, 2000};
- mOutput->editState().displaySpace.bounds = displayRect;
- mOutput->editState().framebufferSpace.bounds = displayRect;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+ mOutput->editState().framebufferSpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
const ui::Rotation orientation = ui::ROTATION_90;
const Rect frame{50, 60, 100, 100};
@@ -298,28 +301,29 @@
mOutput->setProjection(orientation, viewport, frame);
- EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
- EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
- EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
const auto state = mOutput->getState();
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(viewport, state.layerStackSpace.content);
- EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(frame, state.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
- EXPECT_EQ(orientation, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+ EXPECT_EQ(orientation, state.displaySpace.getOrientation());
- EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
- EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+ EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.getContent());
+ EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
}
@@ -327,8 +331,10 @@
TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
const Rect displayRect{0, 0, 1000, 2000};
const Rect framebufferRect{0, 0, 500, 1000};
- mOutput->editState().displaySpace.bounds = displayRect;
- mOutput->editState().framebufferSpace.bounds = framebufferRect;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+ mOutput->editState().framebufferSpace.setBounds(
+ ui::Size(framebufferRect.getWidth(), framebufferRect.getHeight()));
const ui::Rotation orientation = ui::ROTATION_90;
const Rect frame{50, 60, 100, 100};
@@ -336,28 +342,29 @@
mOutput->setProjection(orientation, viewport, frame);
- EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
- EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
- EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
const auto state = mOutput->getState();
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(viewport, state.layerStackSpace.content);
- EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(frame, state.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
- EXPECT_EQ(orientation, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+ EXPECT_EQ(orientation, state.displaySpace.getOrientation());
- EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
- EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
- EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+ EXPECT_EQ(framebufferRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.getContent());
+ EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
}
/*
@@ -365,16 +372,16 @@
*/
TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
- mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
- mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
- mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
- mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
- mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
- mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
- mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
- mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
- mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
- mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
+ mOutput->editState().layerStackSpace.setContent(Rect(0, 0, 2000, 1000));
+ mOutput->editState().layerStackSpace.setBounds(ui::Size(2000, 1000));
+ mOutput->editState().orientedDisplaySpace.setContent(Rect(0, 0, 1800, 900));
+ mOutput->editState().orientedDisplaySpace.setBounds(ui::Size(2000, 1000));
+ mOutput->editState().framebufferSpace.setContent(Rect(0, 0, 900, 1800));
+ mOutput->editState().framebufferSpace.setBounds(ui::Size(1000, 2000));
+ mOutput->editState().framebufferSpace.setOrientation(ui::ROTATION_90);
+ mOutput->editState().displaySpace.setContent(Rect(0, 0, 900, 1800));
+ mOutput->editState().displaySpace.setBounds(ui::Size(1000, 2000));
+ mOutput->editState().displaySpace.setOrientation(ui::ROTATION_90);
const ui::Size newDisplaySize{500, 1000};
@@ -385,20 +392,21 @@
const auto state = mOutput->getState();
const Rect displayRect(newDisplaySize);
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.getOrientation());
- EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
- EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+ EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
@@ -562,7 +570,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect());
}
/*
@@ -571,7 +579,7 @@
TEST_F(OutputTest, getDirtyRegion) {
const Rect viewport{100, 200};
- mOutput->editState().layerStackSpace.content = viewport;
+ mOutput->editState().layerStackSpace.setContent(viewport);
mOutput->editState().dirtyRegion.set(50, 300);
// The dirty region should be clipped to the display bounds.
@@ -1055,7 +1063,8 @@
OutputRebuildLayerStacksTest() {
mOutput.mState.isEnabled = true;
mOutput.mState.transform = kIdentityTransform;
- mOutput.mState.displaySpace.bounds = kOutputBounds;
+ mOutput.mState.displaySpace.setBounds(
+ ui::Size(kOutputBounds.getWidth(), kOutputBounds.getHeight()));
mRefreshArgs.updatingOutputGeometryThisFrame = true;
@@ -1258,8 +1267,8 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
- mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
+ mOutput.mState.displaySpace.setBounds(ui::Size(200, 300));
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 200, 300));
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
mLayer.layerFEState.isVisible = true;
@@ -1339,7 +1348,7 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
- mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
+ mOutput.mState.displaySpace.setBounds(ui::Size(0, 0));
ensureOutputLayerIfVisible();
}
@@ -1536,7 +1545,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1562,7 +1571,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2977,11 +2986,11 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
- mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
- mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
- mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.orientedDisplaySpace.setContent(kDefaultOutputFrame);
+ mOutput.mState.layerStackSpace.setContent(kDefaultOutputViewport);
+ mOutput.mState.framebufferSpace.setContent(kDefaultOutputDestinationClip);
+ mOutput.mState.displaySpace.setContent(kDefaultOutputDestinationClip);
+ mOutput.mState.displaySpace.setOrientation(kDefaultOutputOrientation);
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
@@ -3704,12 +3713,12 @@
struct GenerateClientCompositionRequestsTest_ThreeLayers
: public GenerateClientCompositionRequestsTest {
GenerateClientCompositionRequestsTest_ThreeLayers() {
- mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
- mOutput.mState.layerStackSpace.content = kDisplayViewport;
- mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+ mOutput.mState.orientedDisplaySpace.setContent(kDisplayFrame);
+ mOutput.mState.layerStackSpace.setContent(kDisplayViewport);
+ mOutput.mState.displaySpace.setContent(kDisplayDestinationClip);
mOutput.mState.transform =
ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
- mOutput.mState.displaySpace.orientation = kDisplayOrientation;
+ mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
@@ -4309,11 +4318,11 @@
const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
- mOutput.mState.layerStackSpace.content = kPortraitViewport;
- mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+ mOutput.mState.orientedDisplaySpace.setContent(kPortraitFrame);
+ mOutput.mState.layerStackSpace.setContent(kPortraitViewport);
+ mOutput.mState.displaySpace.setContent(kPortraitDestinationClip);
mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
- mOutput.mState.displaySpace.orientation = kPortraitOrientation;
+ mOutput.mState.displaySpace.setOrientation(kPortraitOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = true;
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
index 704f5a8..19b3928 100644
--- a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -53,40 +53,40 @@
TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
ProjectionSpace space;
- space.content = Rect(100, 200);
- space.bounds = Rect(100, 200);
+ space.setContent(Rect(100, 200));
+ space.setBounds(ui::Size(100, 200));
const ui::Transform identity;
for (int rotation = 0; rotation <= 3; rotation++) {
- space.orientation = ui::Rotation(rotation);
+ space.setOrientation(ui::Rotation(rotation));
EXPECT_EQ(space.getTransform(space), identity);
}
}
TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
ProjectionSpace source;
- source.content = Rect(10, 10, 20, 20);
- source.bounds = Rect(100, 200);
+ source.setContent(Rect(10, 10, 20, 20));
+ source.setBounds(ui::Size(100, 200));
ProjectionSpace dest;
- dest.content = Rect(10, 20, 30, 20);
- dest.bounds = source.bounds;
+ dest.setContent(Rect(10, 20, 30, 20));
+ dest.setBounds(source.getBounds());
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content);
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
}
TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
ProjectionSpace source;
- source.content = Rect(0, 0, 20, 20);
- source.bounds = Rect(100, 200);
+ source.setContent(Rect(0, 0, 20, 20));
+ source.setBounds(ui::Size(100, 200));
ProjectionSpace dest;
- dest.content = Rect(0, 0, 40, 30);
- dest.bounds = source.bounds;
+ dest.setContent(Rect(0, 0, 40, 30));
+ dest.setBounds(source.getBounds());
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content);
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
}
TEST(ProjectionSpaceTest, getSideStripTest) {
@@ -99,7 +99,7 @@
void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content)
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent())
<< "Source content doesn't map to dest content when projecting " << to_string(source)
<< " onto " << to_string(dest);
@@ -113,8 +113,8 @@
// | | | *
// +-----+ +-------*
// source(ROTATION_0) dest (ROTATION_90)
- const auto sourceStrip = getSideStrip(source.content, source.orientation);
- const auto destStrip = getSideStrip(dest.content, dest.orientation);
+ const auto sourceStrip = getSideStrip(source.getContent(), source.getOrientation());
+ const auto destStrip = getSideStrip(dest.getContent(), dest.getOrientation());
ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
ASSERT_NE(destStrip, Rect::INVALID_RECT);
const auto mappedStrip = transform.transform(sourceStrip);
@@ -126,16 +126,16 @@
TEST(ProjectionSpaceTest, getTransformWithOrienations) {
ProjectionSpace source;
- source.bounds = Rect(12, 13, 678, 789);
- source.content = Rect(40, 50, 234, 343);
+ source.setBounds(ui::Size(666, 776));
+ source.setContent(Rect(40, 50, 234, 343));
ProjectionSpace dest;
- dest.bounds = Rect(17, 18, 879, 564);
- dest.content = Rect(43, 52, 432, 213);
+ dest.setBounds(ui::Size(862, 546));
+ dest.setContent(Rect(43, 52, 432, 213));
for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
- source.orientation = ui::Rotation(sourceRot);
+ source.setOrientation(ui::Rotation(sourceRot));
for (int destRot = 0; destRot <= 3; destRot++) {
- dest.orientation = ui::Rotation(destRot);
+ dest.setOrientation(ui::Rotation(destRot));
testTransform(source, dest);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 818334d..ecb05f8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -123,7 +123,7 @@
// set up minimium params needed for rendering
mOutputState.dataspace = ui::Dataspace::SRGB;
mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
}
}
@@ -349,9 +349,9 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
EXPECT_EQ(0.5f, layers[0]->alpha);
EXPECT_EQ(0.75f, layers[1]->alpha);
@@ -401,9 +401,9 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
EXPECT_EQ(0.5f, layers[0]->alpha);
EXPECT_EQ(0.75f, layers[1]->alpha);
@@ -456,9 +456,9 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
EXPECT_EQ(0.5f, layers[0]->alpha);
EXPECT_EQ(0.75f, layers[1]->alpha);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 0d5e362..9b0a75f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -143,7 +143,7 @@
// set up minimium params needed for rendering
mOutputState.dataspace = ui::Dataspace::SRGB;
mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
}
}
@@ -503,7 +503,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
@@ -516,7 +516,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
@@ -544,7 +544,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index fd93b2d..3397118 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,11 +116,11 @@
}
int DisplayDevice::getWidth() const {
- return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
+ return mCompositionDisplay->getState().displaySpace.getBounds().width;
}
int DisplayDevice::getHeight() const {
- return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
+ return mCompositionDisplay->getState().displaySpace.getBounds().height;
}
void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -290,13 +290,15 @@
if (!orientedDisplaySpaceRect.isValid()) {
// The destination frame can be invalid if it has never been set,
// in that case we assume the whole display size.
- orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+ orientedDisplaySpaceRect =
+ getCompositionDisplay()->getState().displaySpace.getBoundsAsRect();
}
if (layerStackSpaceRect.isEmpty()) {
// The layerStackSpaceRect can be invalid if it has never been set, in that case
// we assume the whole framebuffer size.
- layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+ layerStackSpaceRect =
+ getCompositionDisplay()->getState().framebufferSpace.getBoundsAsRect();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
@@ -364,8 +366,8 @@
return mCompositionDisplay->isSecure();
}
-const Rect& DisplayDevice::getBounds() const {
- return mCompositionDisplay->getState().displaySpace.bounds;
+const Rect DisplayDevice::getBounds() const {
+ return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
const Region& DisplayDevice::getUndefinedRegion() const {
@@ -389,11 +391,11 @@
}
const Rect& DisplayDevice::getLayerStackSpaceRect() const {
- return mCompositionDisplay->getState().layerStackSpace.content;
+ return mCompositionDisplay->getState().layerStackSpace.getContent();
}
const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
- return mCompositionDisplay->getState().orientedDisplaySpace.content;
+ return mCompositionDisplay->getState().orientedDisplaySpace.getContent();
}
bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7762054..4a731bd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -156,8 +156,8 @@
// Return true if intent is supported by the display.
bool hasRenderIntent(ui::RenderIntent intent) const;
- const Rect& getBounds() const;
- const Rect& bounds() const { return getBounds(); }
+ const Rect getBounds() const;
+ const Rect bounds() const { return getBounds(); }
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2376b83..ef6f115 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -134,6 +134,7 @@
mDrawingState.frameTimelineInfo = {};
mDrawingState.postTime = -1;
mDrawingState.destinationFrame.makeInvalid();
+ mDrawingState.isTrustedOverlay = false;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index e922d46..b00423e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -330,18 +330,28 @@
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+ const Policy* policy = getCurrentPolicyLocked();
+ const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+ // If the default mode group is different from the group of current mode,
+ // this means a layer requesting a seamed mode switch just disappeared and
+ // we should switch back to the default group.
+ // However if a seamed layer is still present we anchor around the group
+ // of the current mode, in order to prevent unnecessary seamed mode switches
+ // (e.g. when pausing a video playback).
+ const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
+ : defaultMode->getModeGroup();
+
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
if (globalSignals.touch && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
setTouchConsidered();
- return getMaxRefreshRateByPolicyLocked();
+ return getMaxRefreshRateByPolicyLocked(anchorGroup);
}
// If the primary range consists of a single refresh rate then we can only
// move out the of range if layers explicitly request a different refresh
// rate.
- const Policy* policy = getCurrentPolicyLocked();
const bool primaryRangeIsSingleRate =
policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
@@ -353,7 +363,9 @@
}
if (layers.empty() || noVoteLayers == layers.size()) {
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
}
// Only if all layers want Min we should return Min
@@ -370,8 +382,6 @@
scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
- const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
-
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), layer.weight,
@@ -409,10 +419,7 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = seamedFocusedLayers > 0
- ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
- : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
-
+ const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->toString().c_str(),
@@ -451,9 +458,9 @@
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
[](RefreshRateScore score) { return score.score == 0; })) {
- ALOGV("layers not scored - choose %s",
- getMaxRefreshRateByPolicyLocked().getName().c_str());
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
} else {
return *bestRefreshRate;
}
@@ -463,7 +470,7 @@
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
- const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+ const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
const bool touchBoostForExplicitExact = [&] {
if (mSupportsFrameRateOverride) {
@@ -646,10 +653,10 @@
return getMaxRefreshRateByPolicyLocked();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
const auto& refreshRate = (**it);
- if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
+ if (anchorGroup == refreshRate.getModeGroup()) {
return refreshRate;
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0d75689..3713587 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -388,7 +388,11 @@
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
+ const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
+ return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup());
+ }
+
+ const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 12389d1..5a881a3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -620,12 +620,18 @@
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
std::vector<PhysicalDisplayId> displayIds;
displayIds.reserve(mPhysicalDisplayTokens.size());
+ const auto defaultDisplayId = [this]() REQUIRES(mStateLock) {
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ return display->getPhysicalId();
+ }
- const auto internalDisplayId = getInternalDisplayIdLocked();
- displayIds.push_back(internalDisplayId);
+ // fallback to the internal display id if the active display is unknown
+ return getInternalDisplayIdLocked();
+ }();
+ displayIds.push_back(defaultDisplayId);
for (const auto& [id, token] : mPhysicalDisplayTokens) {
- if (id != internalDisplayId) {
+ if (id != defaultDisplayId) {
displayIds.push_back(id);
}
}
@@ -2870,8 +2876,8 @@
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
currentState.orientedDisplaySpaceRect);
- if (display->isPrimary()) {
- mDefaultDisplayTransformHint = display->getTransformHint();
+ if (isDisplayActiveLocked(display)) {
+ mActiveDisplayTransformHint = display->getTransformHint();
}
}
if (currentState.width != drawingState.width ||
@@ -3362,9 +3368,9 @@
composerState.state.surface = handle;
states.add(composerState);
- lbc->updateTransformHint(mDefaultDisplayTransformHint);
+ lbc->updateTransformHint(mActiveDisplayTransformHint);
if (outTransformHint) {
- *outTransformHint = mDefaultDisplayTransformHint;
+ *outTransformHint = mActiveDisplayTransformHint;
}
// attach this layer to the client
client->attachLayer(handle, lbc);
@@ -4475,7 +4481,7 @@
const nsecs_t vsyncPeriod =
display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
- mDefaultDisplayTransformHint = display->getTransformHint();
+ mActiveDisplayTransformHint = display->getTransformHint();
// Use phase of 0 since phase is not known.
// Use latency of 0, which will snap to the ideal latency.
DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
@@ -4667,7 +4673,7 @@
result.append(traceFileProto.SerializeAsString());
} else {
// Dump info that we need to access from the main thread
- const auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+ const auto layerTree = LayerProtoParser::generateLayerTree(layersTrace->layers());
result.append(LayerProtoParser::layerTreeToString(layerTree));
result.append("\n");
dumpOffscreenLayers(result);
@@ -6895,7 +6901,7 @@
parent->addChild(layer);
}
- layer->updateTransformHint(mDefaultDisplayTransformHint);
+ layer->updateTransformHint(mActiveDisplayTransformHint);
if (state->initialProducer != nullptr) {
mGraphicBufferProducerList.insert(state->initialProducer);
@@ -6942,12 +6948,12 @@
return;
}
mActiveDisplayToken = activeDisplay->getDisplayToken();
-
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
updateInternalDisplayVsyncLocked(activeDisplay);
mScheduler->setModeChangePending(false);
mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
onActiveDisplaySizeChanged(activeDisplay);
+ mActiveDisplayTransformHint = activeDisplay->getTransformHint();
}
status_t SurfaceFlinger::addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 407907d..9dedfa7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1500,7 +1500,7 @@
auto getLayerCreatedState(const sp<IBinder>& handle);
sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
- std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+ std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
void scheduleRegionSamplingThread();
void notifyRegionSamplingThread();
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
index 9fe30f8..3d24ecb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -84,10 +84,10 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -96,13 +96,14 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
// For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -111,9 +112,9 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -122,13 +123,14 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
// For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index a6bfde7..618c10d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -349,10 +349,19 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ NO_ERROR);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ // We select max even when this will cause a non-seamless switch.
+ refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {Fps(0), Fps(90)}}),
+ NO_ERROR);
+ EXPECT_EQ(mExpected90DifferentGroupConfig,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {