diff options
36 files changed, 876 insertions, 79 deletions
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index 0ee6c3adac..f2678ebaa4 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -137,6 +137,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str()); dprintf(fd, "version: %s\n", ds_.version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str()); + dprintf(fd, "bugreport_internal_dir_: %s\n", ds_.bugreport_internal_dir_.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str()); dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str()); dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str()); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index df80651dc2..be10232d09 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -82,6 +82,7 @@ using android::String8; using android::TIMED_OUT; using android::UNKNOWN_ERROR; using android::Vector; +using android::base::StringPrintf; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::DumpstateSectionReporter; @@ -121,6 +122,69 @@ static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); + +namespace android { +namespace os { +namespace { + +static int Open(std::string path, int flags, mode_t mode = 0) { + int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); + if (fd == -1) { + MYLOGE("open(%s, %s)\n", path.c_str(), strerror(errno)); + } + return fd; +} + +static int OpenForWrite(std::string path) { + return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +} + +static int OpenForRead(std::string path) { + return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); +} + +bool CopyFile(int in_fd, int out_fd) { + char buf[4096]; + ssize_t byte_count; + while ((byte_count = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf)))) > 0) { + if (!android::base::WriteFully(out_fd, buf, byte_count)) { + return false; + } + } + return (byte_count != -1); +} + +static bool CopyFileToFd(const std::string& input_file, int out_fd) { + MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd); + + // Obtain a handle to the source file. + android::base::unique_fd in_fd(OpenForRead(input_file)); + if (out_fd != -1 && in_fd.get() != -1) { + if (CopyFile(in_fd.get(), out_fd)) { + return true; + } + MYLOGE("Failed to copy zip file: %s\n", strerror(errno)); + } + return false; +} + +static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) { + if (input_file == output_file) { + MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n", + output_file.c_str()); + return false; + } + + MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str()); + android::base::unique_fd out_fd(OpenForWrite(output_file)); + return CopyFileToFd(input_file, out_fd.get()); +} + +} // namespace +} // namespace os +} // namespace android + static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, fullCommand, options); @@ -137,8 +201,6 @@ static int DumpFile(const std::string& title, const std::string& path) { // Relative directory (inside the zip) for all files copied as-is into the bugreport. static const std::string ZIP_ROOT_DIR = "FS"; -// Must be hardcoded because dumpstate HAL implementation need SELinux access to it -static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kProtoPath = "proto/"; static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { @@ -1145,7 +1207,7 @@ static void DumpHals() { return !isalnum(c) && std::string("@-_:.").find(c) == std::string::npos; }, '_'); - const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName; + const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; { auto fd = android::base::unique_fd( @@ -1529,7 +1591,8 @@ void Dumpstate::DumpstateBoard() { std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { - paths.emplace_back(kDumpstateBoardPath + kDumpstateBoardFiles[i]); + paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), + kDumpstateBoardFiles[i].c_str())); remover.emplace_back(android::base::make_scope_guard(std::bind( [](std::string path) { if (remove(path.c_str()) != 0 && errno != ENOENT) { @@ -1687,7 +1750,8 @@ bool Dumpstate::FinishZipFile() { MYLOGE("Failed to add dumpstate log to .zip file\n"); return false; } - // ... and re-opens it for further logging. + // TODO: Should truncate the existing file. + // ... and re-open it for further logging. redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str())); fprintf(stderr, "\n"); @@ -1770,17 +1834,30 @@ static void Vibrate(int duration_ms) { // clang-format on } +static void MaybeResolveSymlink(std::string* path) { + std::string resolved_path; + if (android::base::Readlink(*path, &resolved_path)) { + *path = resolved_path; + } +} + /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. */ static void PrepareToWriteToFile() { - ds.bugreport_dir_ = dirname(ds.options_->use_outfile.c_str()); + MaybeResolveSymlink(&ds.bugreport_internal_dir_); + + std::string base_name_part1 = "bugreport"; + if (!ds.options_->use_outfile.empty()) { + ds.bugreport_dir_ = dirname(ds.options_->use_outfile.c_str()); + base_name_part1 = basename(ds.options_->use_outfile.c_str()); + } + std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); ds.base_name_ = - android::base::StringPrintf("%s-%s-%s", basename(ds.options_->use_outfile.c_str()), - device_name.c_str(), build_id.c_str()); + StringPrintf("%s-%s-%s", base_name_part1.c_str(), device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); @@ -1801,15 +1878,18 @@ static void PrepareToWriteToFile() { ds.tmp_path_ = ds.GetPath(".tmp"); ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); + std::string destination = ds.options_->fd != -1 ? StringPrintf("[fd:%d]", ds.options_->fd) + : ds.bugreport_dir_.c_str(); MYLOGD( "Bugreport dir: %s\n" + "Internal Bugreport dir: %s\n" "Base name: %s\n" "Suffix: %s\n" "Log path: %s\n" "Temporary path: %s\n" "Screenshot path: %s\n", - ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), - ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); + destination.c_str(), ds.bugreport_internal_dir_.c_str(), ds.base_name_.c_str(), + ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); if (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(".zip"); @@ -1875,6 +1955,19 @@ static void FinalizeFile() { ds.path_ = new_path; } } + // The zip file lives in an internal directory. Copy it over to output. + bool copy_succeeded = false; + if (ds.options_->fd != -1) { + copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->fd); + } else { + ds.final_path_ = ds.GetPath(ds.bugreport_dir_, ".zip"); + copy_succeeded = android::os::CopyFileToFile(ds.path_, ds.final_path_); + } + if (copy_succeeded) { + if (remove(ds.path_.c_str())) { + MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno)); + } + } } } if (do_text_file) { @@ -1899,8 +1992,9 @@ static void FinalizeFile() { /* Broadcasts that we are done with the bugreport */ static void SendBugreportFinishedBroadcast() { - if (!ds.path_.empty()) { - MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); + // TODO(b/111441001): use callback instead of broadcast. + if (!ds.final_path_.empty()) { + MYLOGI("Final bugreport path: %s\n", ds.final_path_.c_str()); // clang-format off std::vector<std::string> am_args = { @@ -1908,7 +2002,7 @@ static void SendBugreportFinishedBroadcast() { "--ei", "android.intent.extra.ID", std::to_string(ds.id_), "--ei", "android.intent.extra.PID", std::to_string(ds.pid_), "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()), - "--es", "android.intent.extra.BUGREPORT", ds.path_, + "--es", "android.intent.extra.BUGREPORT", ds.final_path_, "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ }; // clang-format on @@ -1930,7 +2024,7 @@ static void SendBugreportFinishedBroadcast() { if (ds.options_->is_remote_mode) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH"); - am_args.push_back(SHA256_file_hash(ds.path_)); + am_args.push_back(SHA256_file_hash(ds.final_path_)); SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); } else { SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); @@ -2060,6 +2154,7 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI("telephony_only: %d\n", options.telephony_only); MYLOGI("wifi_only: %d\n", options.wifi_only); MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); + MYLOGI("fd: %d\n", options.fd); MYLOGI("use_outfile: %s\n", options.use_outfile.c_str()); MYLOGI("extra_options: %s\n", options.extra_options.c_str()); MYLOGI("args: %s\n", options.args.c_str()); @@ -2125,8 +2220,13 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { - if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) - && use_outfile.empty()) { + if (fd != -1 && !do_zip_file) { + return false; + } + + bool has_out_file_options = !use_outfile.empty() || fd != -1; + if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && + !has_out_file_options) { return false; } @@ -2148,10 +2248,31 @@ void Dumpstate::SetOptions(std::unique_ptr<DumpOptions> options) { options_ = std::move(options); } +/* + * Dumps relevant information to a bugreport based on the given options. + * + * The bugreport can be dumped to a file or streamed to a socket. + * + * How dumping to file works: + * stdout is redirected to a temporary file. This will later become the main bugreport entry. + * stderr is redirected a log file. + * + * The temporary bugreport is then populated via printfs, dumping contents of files and + * output of commands to stdout. + * + * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final + * text file. + * + * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also + * gets added to the archive. + * + * Bugreports are first generated in a local directory and later copied to the caller's fd or + * directory. + */ Dumpstate::RunStatus Dumpstate::Run() { + LogDumpOptions(*options_); if (!options_->ValidateOptions()) { MYLOGE("Invalid options specified\n"); - LogDumpOptions(*options_); return RunStatus::INVALID_INPUT; } /* set as high priority, and protect from OOM killer */ @@ -2191,9 +2312,9 @@ Dumpstate::RunStatus Dumpstate::Run() { // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = - is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", - dirname(options_->use_outfile.c_str())) - : ""; + is_redirecting + ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) + : ""; progress_.reset(new Progress(stats_path)); /* gets the sequential id */ @@ -2289,13 +2410,18 @@ Dumpstate::RunStatus Dumpstate::Run() { int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { + // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); redirect_to_file(stderr, const_cast<char*>(log_path_.c_str())); if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), strerror(errno)); } + + // Redirect stdout to tmp_path_. This is the main bugreport entry and will be + // moved into zip file later, if zipping. TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); + // TODO: why not write to a file instead of stdout to overcome this problem? /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ @@ -2370,7 +2496,7 @@ Dumpstate::RunStatus Dumpstate::Run() { return RunStatus::OK; } -/* Main entry point for dumpstate. */ +/* Main entry point for dumpstate binary. */ int run_main(int argc, char* argv[]) { std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); Dumpstate::RunStatus status = options->Initialize(argc, argv); @@ -2382,7 +2508,7 @@ int run_main(int argc, char* argv[]) { switch (status) { case Dumpstate::RunStatus::OK: return 0; - break; + // TODO(b/111441001): Exit directly in the following cases. case Dumpstate::RunStatus::HELP: ShowUsageAndExit(0 /* exit code */); break; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 35cbdb1096..94e3191087 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -142,7 +142,7 @@ class Progress { float growth_factor_; int32_t n_runs_; int32_t average_max_; - const std::string& path_; + std::string path_; }; /* @@ -164,6 +164,11 @@ static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; static std::string VERSION_DEFAULT = "default"; /* + * Directory used by Dumpstate binary to keep its local files. + */ +static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; + +/* * Structure that contains the information of an open dump file. */ struct DumpData { @@ -300,7 +305,11 @@ class Dumpstate { */ bool FinishZipFile(); - /* Gets the path of a bugreport file with the given suffix. */ + /* Constructs a full path inside directory with file name formatted using the given suffix. */ + std::string GetPath(const std::string& directory, const std::string& suffix) const; + + /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the + * given suffix. */ std::string GetPath(const std::string& suffix) const; /* Returns true if the current version supports priority dump feature. */ @@ -314,7 +323,6 @@ class Dumpstate { /* Sets runtime options. */ void SetOptions(std::unique_ptr<DumpOptions> options); - // TODO: add other options from DumpState. /* * Structure to hold options that determine the behavior of dumpstate. */ @@ -333,6 +341,10 @@ class Dumpstate { bool wifi_only = false; // Whether progress updates should be published. bool do_progress_updates = false; + // File descriptor to output zip file. -1 indicates not set. Takes precedence over + // use_outfile. + int fd = -1; + // Partial path to output file. std::string use_outfile; // TODO: rename to MODE. // Extra options passed as system property. @@ -381,12 +393,6 @@ class Dumpstate { // Bugreport format version; std::string version_ = VERSION_CURRENT; - // Full path of the directory where the bugreport files will be written. - std::string bugreport_dir_; - - // Full path of the temporary file containing the screenshot (when requested). - std::string screenshot_path_; - time_t now_; // Base name (without suffix or extensions) of the bugreport files, typically @@ -397,15 +403,30 @@ class Dumpstate { // `-d`), but it could be changed by the user.. std::string name_; - // Full path of the temporary file containing the bugreport. + std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; + + // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. + // At the very end this file is pulled into the zip file. std::string tmp_path_; - // Full path of the file containing the dumpstate logs. + // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. + // This is useful for debugging. std::string log_path_; - // Pointer to the actual path, be it zip or text. + // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; + // TODO: If temporary this should be removed at the end. + // Full path of the temporary file containing the screenshot (when requested). + std::string screenshot_path_; + + // TODO(b/111441001): remove when obsolete. + // Full path of the final zip file inside the caller-specified directory, if available. + std::string final_path_; + + // The caller-specified directory, if available. + std::string bugreport_dir_; + // Pointer to the zipped file. std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose}; diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 6cbb691676..d97ffbf2ed 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -229,7 +229,11 @@ bool Dumpstate::IsZipping() const { } std::string Dumpstate::GetPath(const std::string& suffix) const { - return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(), + return GetPath(bugreport_internal_dir_, suffix); +} + +std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { + return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), name_.c_str(), suffix.c_str()); } diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h index dc25deddbb..fe2e2acaca 100644 --- a/headers/media_plugin/media/openmax/OMX_AsString.h +++ b/headers/media_plugin/media/openmax/OMX_AsString.h @@ -189,6 +189,7 @@ inline static const char *asString(OMX_AUDIO_CODINGEXTTYPE i, const char *def = switch (i) { case OMX_AUDIO_CodingAndroidAC3: return "AndroidAC3"; case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS"; + case OMX_AUDIO_CodingAndroidAC4: return "AndroidAC4"; default: return asString((OMX_AUDIO_CODINGTYPE)i, def); } } @@ -536,6 +537,7 @@ inline static const char *asString(OMX_INDEXEXTTYPE i, const char *def = "??") { case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus"; case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation"; case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3"; + case OMX_IndexParamAudioAndroidAc4: return "ParamAudioAndroidAc4"; case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported"; // case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported"; // case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat"; diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h index 8409553697..1ea740fbdf 100644 --- a/headers/media_plugin/media/openmax/OMX_AudioExt.h +++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h @@ -48,6 +48,7 @@ typedef enum OMX_AUDIO_CODINGEXTTYPE { OMX_AUDIO_CodingAndroidAC3, /**< AC3 encoded data */ OMX_AUDIO_CodingAndroidOPUS, /**< OPUS encoded data */ OMX_AUDIO_CodingAndroidEAC3, /**< EAC3 encoded data */ + OMX_AUDIO_CodingAndroidAC4, /**< AC4 encoded data */ } OMX_AUDIO_CODINGEXTTYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE { @@ -68,6 +69,15 @@ typedef struct OMX_AUDIO_PARAM_ANDROID_EAC3TYPE { variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ANDROID_EAC3TYPE; +typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ +} OMX_AUDIO_PARAM_ANDROID_AC4TYPE; + typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h index 716d959979..9fc3ef344e 100644 --- a/headers/media_plugin/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h @@ -64,6 +64,7 @@ typedef enum OMX_INDEXEXTTYPE { OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */ OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */ OMX_IndexParamAudioAndroidAacDrcPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */ + OMX_IndexParamAudioAndroidAc4, /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */ OMX_IndexExtAudioEndUnused, /* Image parameters and configurations */ diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index c2a6764338..dfdda0c2f4 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -135,8 +135,8 @@ void GraphicsEnv::setDriverPath(const std::string path) { } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, - bool developerOptIn, const int rulesFd, const long rulesOffset, - const long rulesLength) { + const std::string developerOptIn, const int rulesFd, + const long rulesOffset, const long rulesLength) { if (!mAnglePath.empty()) { ALOGV("ignoring attempt to change ANGLE path from '%s' to '%s'", mAnglePath.c_str(), path.c_str()); @@ -153,7 +153,13 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName mAngleAppName = appName; } - mAngleDeveloperOptIn = developerOptIn; + if (!mAngleDeveloperOptIn.empty()) { + ALOGV("ignoring attempt to change ANGLE application opt-in from '%s' to '%s'", + mAngleDeveloperOptIn.c_str(), developerOptIn.c_str()); + } else { + ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); + mAngleDeveloperOptIn = developerOptIn; + } ALOGV("setting ANGLE rules file descriptor to '%i'", rulesFd); mAngleRulesFd = rulesFd; @@ -182,8 +188,8 @@ const char* GraphicsEnv::getAngleAppName() { return mAngleAppName.c_str(); } -bool GraphicsEnv::getAngleDeveloperOptIn() { - return mAngleDeveloperOptIn; +const char* GraphicsEnv::getAngleDeveloperOptIn() { + return mAngleDeveloperOptIn.c_str(); } int GraphicsEnv::getAngleRulesFd() { diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 20e4d66b4a..4ec53f1cac 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -44,12 +44,12 @@ public: // (libraries must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a - void setAngleInfo(const std::string path, const std::string appName, bool devOptIn, + void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, const int rulesFd, const long rulesOffset, const long rulesLength); android_namespace_t* getAngleNamespace(); const char* getAngleAppName(); const char* getAngleAppPref(); - bool getAngleDeveloperOptIn(); + const char* getAngleDeveloperOptIn(); int getAngleRulesFd(); long getAngleRulesOffset(); long getAngleRulesLength(); @@ -69,7 +69,7 @@ private: std::string mDriverPath; std::string mAnglePath; std::string mAngleAppName; - bool mAngleDeveloperOptIn; + std::string mAngleDeveloperOptIn; int mAngleRulesFd; long mAngleRulesOffset; long mAngleRulesLength; diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp index e747ee11be..3816c1bc4f 100644 --- a/libs/ui/BufferHubBuffer.cpp +++ b/libs/ui/BufferHubBuffer.cpp @@ -61,6 +61,16 @@ namespace { // to use Binder. static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client"; +using dvr::BufferHubDefs::AnyClientAcquired; +using dvr::BufferHubDefs::AnyClientGained; +using dvr::BufferHubDefs::AnyClientPosted; +using dvr::BufferHubDefs::IsClientAcquired; +using dvr::BufferHubDefs::IsClientGained; +using dvr::BufferHubDefs::IsClientPosted; +using dvr::BufferHubDefs::IsClientReleased; +using dvr::BufferHubDefs::kHighBitsMask; +using dvr::BufferHubDefs::kMetadataHeaderSize; + } // namespace BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {} @@ -71,7 +81,7 @@ BufferHubClient::BufferHubClient(LocalChannelHandle mChannelHandle) BufferHubClient::~BufferHubClient() {} bool BufferHubClient::IsValid() const { - return IsConnected() && GetChannelHandle().valid(); + return IsConnected() && GetChannelHandle().valid(); } LocalChannelHandle BufferHubClient::TakeChannelHandle() { @@ -151,11 +161,17 @@ int BufferHubBuffer::ImportGraphicBuffer() { } size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size()); - if (metadataSize < dvr::BufferHubDefs::kMetadataHeaderSize) { + if (metadataSize < kMetadataHeaderSize) { ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata too small: %zu", metadataSize); return -EINVAL; } + // Populate shortcuts to the atomics in metadata. + auto metadata_header = mMetadata.metadata_header(); + buffer_state_ = &metadata_header->buffer_state; + fence_state_ = &metadata_header->fence_state; + active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask; + // Import the buffer: We only need to hold on the native_handle_t here so that // GraphicBuffer instance can be created in future. mBufferHandle = bufferTraits.take_buffer_handle(); @@ -176,7 +192,93 @@ int BufferHubBuffer::ImportGraphicBuffer() { // TODO(b/112012161) Set up shared fences. ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(), - mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire)); + buffer_state_->load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::Gain() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already gained by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + do { + if (AnyClientGained(current_buffer_state & (~mClientStateMask)) || + AnyClientAcquired(current_buffer_state)) { + ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state to gained state, whose value happens to be the same as + // mClientStateMask. + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Post() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + uint64_t current_active_clients_bit_mask = 0ULL; + uint64_t updated_buffer_state = 0ULL; + do { + if (!IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " + "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Set the producer client buffer state to released, other clients' buffer state to posted. + current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); + updated_buffer_state = + current_active_clients_bit_mask & (~mClientStateMask) & kHighBitsMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +int BufferHubBuffer::Acquire() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientAcquired(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already acquired by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint64_t updated_buffer_state = 0ULL; + do { + if (!IsClientPosted(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " + "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state for this consumer from posted to acquired. + updated_buffer_state = current_buffer_state ^ mClientStateMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Release() { + uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientReleased(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already released by this client %" PRIx64 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint64_t updated_buffer_state = 0ULL; + do { + updated_buffer_state = current_buffer_state & (~mClientStateMask); + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. return 0; } diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index 37cf617374..20f27c53e3 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -42,9 +42,6 @@ uint64_t getValid10UsageBits() { for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) { bits = bits | bit; } - // TODO(b/72323293, b/72703005): Remove these additional bits - bits = bits | (1 << 10) | (1 << 13); - return bits; }(); return valid10UsageBits; diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index a2f1783e58..f56e6b9e86 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -101,6 +101,9 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (layerCount < 1) layerCount = 1; + // TODO(b/72323293, b/72703005): Remove these invalid bits from callers + usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); + Gralloc2::IMapper::BufferDescriptorInfo info = {}; info.width = width; info.height = height; diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h index 6850b43f8a..03d10e7a9a 100644 --- a/libs/ui/include/ui/BufferHubBuffer.h +++ b/libs/ui/include/ui/BufferHubBuffer.h @@ -103,6 +103,27 @@ public: // to read from and/or write into. bool IsValid() const { return mBufferHandle.IsValid(); } + // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is + // gained. + // The buffer can be gained as long as there is no other client in acquired or gained state. + int Gain(); + + // Posts the gained buffer for other buffer clients to use the buffer. + // The buffer can be posted iff the buffer state for this client is gained. + // After posting the buffer, this client is put to released state and does not have access to + // the buffer for this cycle of the usage of the buffer. + int Post(); + + // Acquires the buffer for shared read permission. + // The buffer can be acquired iff the buffer state for this client is posted. + int Acquire(); + + // Releases the buffer. + // The buffer can be released from any buffer state. + // After releasing the buffer, this client no longer have any permissions to the buffer for the + // current cycle of the usage of the buffer. + int Release(); + // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for // all possible bits). pdx::Status<int> GetEventMask(int events) { @@ -130,6 +151,9 @@ private: // Global id for the buffer that is consistent across processes. int mId = -1; + + // Client state mask of this BufferHubBuffer object. It is unique amoung all + // clients/users of the buffer. uint64_t mClientStateMask = 0; // Stores ground truth of the buffer. @@ -141,6 +165,10 @@ private: // An ashmem-based metadata object. The same shared memory are mapped to the // bufferhubd daemon and all buffer clients. BufferHubMetadata mMetadata; + // Shortcuts to the atomics inside the header of mMetadata. + std::atomic<uint64_t>* buffer_state_{nullptr}; + std::atomic<uint64_t>* fence_state_{nullptr}; + std::atomic<uint64_t>* active_clients_bit_mask_{nullptr}; // PDX backend. BufferHubClient mClient; diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index fcc6d371d9..18bbb3e876 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -74,7 +74,12 @@ cc_test { cc_test { name: "BufferHubMetadata_test", header_libs: ["libbufferhub_headers", "libdvr_headers"], - shared_libs: ["libpdx_default_transport", "libui", "libutils"], + shared_libs: [ + "libbase", + "libpdx_default_transport", + "libui", + "libutils", + ], srcs: ["BufferHubMetadata_test.cpp"], cflags: ["-Wall", "-Werror"], } diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp index 6c7d06bd0d..e33acf6d11 100644 --- a/libs/ui/tests/BufferHubBuffer_test.cpp +++ b/libs/ui/tests/BufferHubBuffer_test.cpp @@ -36,7 +36,14 @@ const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const size_t kUserMetadataSize = 0; +using dvr::BufferHubDefs::AnyClientAcquired; +using dvr::BufferHubDefs::AnyClientGained; +using dvr::BufferHubDefs::AnyClientPosted; using dvr::BufferHubDefs::IsBufferReleased; +using dvr::BufferHubDefs::IsClientAcquired; +using dvr::BufferHubDefs::IsClientGained; +using dvr::BufferHubDefs::IsClientPosted; +using dvr::BufferHubDefs::IsClientReleased; using dvr::BufferHubDefs::kFirstClientBitMask; using dvr::BufferHubDefs::kMetadataHeaderSize; using frameworks::bufferhub::V1_0::BufferHubStatus; @@ -48,9 +55,40 @@ using hidl::base::V1_0::IBase; using pdx::LocalChannelHandle; class BufferHubBufferTest : public ::testing::Test { +protected: void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); } }; +class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { +protected: + void SetUp() override { + BufferHubBufferTest::SetUp(); + CreateTwoClientsOfABuffer(); + } + + std::unique_ptr<BufferHubBuffer> b1; + uint64_t b1ClientMask = 0ULL; + std::unique_ptr<BufferHubBuffer> b2; + uint64_t b2ClientMask = 0ULL; + +private: + // Creates b1 and b2 as the clients of the same buffer for testing. + void CreateTwoClientsOfABuffer(); +}; + +void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() { + b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + b1ClientMask = b1->client_state_mask(); + ASSERT_NE(b1ClientMask, 0ULL); + auto statusOrHandle = b1->Duplicate(); + ASSERT_TRUE(statusOrHandle); + LocalChannelHandle h2 = statusOrHandle.take(); + b2 = BufferHubBuffer::Import(std::move(h2)); + b2ClientMask = b2->client_state_mask(); + ASSERT_NE(b2ClientMask, 0ULL); + ASSERT_NE(b2ClientMask, b1ClientMask); +} + TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) { // Buffer Creation will fail: BLOB format requires height to be 1. auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount, @@ -315,5 +353,180 @@ TEST_F(BufferHubBufferTest, ImportFreedBuffer) { EXPECT_EQ(nullptr, client2.get()); } +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Successful gaining the buffer should change the buffer state bit of b1 to + // gained state, other client state bits to released state. + EXPECT_EQ(b1->Gain(), 0); + EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask)); + + // Gaining from gained state by the same client should not return error. + EXPECT_EQ(b1->Gain(), 0); + + // Gaining from gained state by another client should return error. + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Gaining from acquired state should fail. + EXPECT_EQ(b1->Gain(), -EBUSY); + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Gaining a buffer who has other posted client should succeed. + EXPECT_EQ(b1->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // A posted client should be able to gain the buffer when there is no other clients in + // acquired state. + EXPECT_EQ(b2->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b1->Post(), 0); + auto current_buffer_state = b1->buffer_state(); + EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask)); + EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Post from posted state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Posting from acquired state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Posting from released state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from posted state should pass. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from released state should fail, although there are other clients + // in posted state. + EXPECT_EQ(b1->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask)); + + // Acquiring from acquired state by the same client should not error out. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Acquiring form released state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + // Acquiring from gained state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + } // namespace } // namespace android diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp index de03fad01f..cd92b625d5 100644 --- a/libs/vr/libbufferhub/producer_buffer.cpp +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -224,15 +224,17 @@ int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, uint64_t current_fence_state = fence_state_->load(std::memory_order_acquire); uint64_t current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); - // If there is an release fence from consumer, we need to return it. + // If there are release fence(s) from consumer(s), we need to return it to the + // consumer(s). // TODO(b/112007999) add an atomic variable in metadata header in shared // memory to indicate which client is the last producer of the buffer. // Currently, assume the first client is the only producer to the buffer. if (current_fence_state & current_active_clients_bit_mask & (~BufferHubDefs::kFirstClientBitMask)) { *out_fence = shared_release_fence_.Duplicate(); - out_meta->release_fence_mask = - current_fence_state & current_active_clients_bit_mask; + out_meta->release_fence_mask = current_fence_state & + current_active_clients_bit_mask & + (~BufferHubDefs::kFirstClientBitMask); } return 0; diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 6edadcd6ed..50e30c28d7 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -41,6 +41,12 @@ extern "C" { android_namespace_t* android_get_exported_namespace(const char*); + typedef enum ANGLEPreference { + ANGLE_PREFER_DEFAULT = 0, + ANGLE_PREFER_NATIVE = 1, + ANGLE_PREFER_ANGLE = 2, + } ANGLEPreference; + // TODO(ianelliott@): Get the following from an ANGLE header: // Version-1 API: typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse); @@ -524,6 +530,17 @@ static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns return nullptr; } +static ANGLEPreference getAngleDevOption(const char* devOption) { + if (devOption == nullptr) + return ANGLE_PREFER_DEFAULT; + + if (strcmp(devOption, "angle") == 0) { + return ANGLE_PREFER_ANGLE; + } else if (strcmp(devOption, "native") == 0) { + return ANGLE_PREFER_NATIVE; + } + return ANGLE_PREFER_DEFAULT; +} static bool check_angle_rules(void* so, const char* app_name) { bool use_angle = false; @@ -646,15 +663,19 @@ static void* load_angle(const char* kind, android_namespace_t* ns, egl_connectio char prop[PROPERTY_VALUE_MAX]; const char* app_name = android::GraphicsEnv::getInstance().getAngleAppName(); - bool developer_opt_in = android::GraphicsEnv::getInstance().getAngleDeveloperOptIn(); + const char* developer_opt_in = android::GraphicsEnv::getInstance().getAngleDeveloperOptIn(); // Determine whether or not to use ANGLE: - bool use_angle = developer_opt_in; + ANGLEPreference developer_option = getAngleDevOption(developer_opt_in); + bool use_angle = (developer_option == ANGLE_PREFER_ANGLE); if (use_angle) { ALOGV("User set \"Developer Options\" to force the use of ANGLE"); } else if (cnx->angleDecided) { use_angle = cnx->useAngle; + } else if (developer_option == ANGLE_PREFER_NATIVE) { + ALOGV("User set \"Developer Options\" to force the use of Native"); + use_angle = false; } else { // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily // load ANGLE and call the updatable opt-in/out logic: diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 4433af0826..c100db7c2a 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -378,7 +378,8 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (wideColorBoardConfig && hasColorSpaceSupport) { mExtensionString.append( "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear " - "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "); + "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 " + "EGL_EXT_gl_colorspace_display_p3_passthrough "); } bool hasHdrBoardConfig = diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 31e4e2d2b4..79166a75c3 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -466,6 +466,8 @@ static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { return HAL_DATASPACE_DISPLAY_P3; } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { return HAL_DATASPACE_DISPLAY_P3_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) { + return HAL_DATASPACE_DISPLAY_P3; } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { return HAL_DATASPACE_V0_SCRGB; } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { @@ -510,6 +512,9 @@ static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) { colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT); + } return colorSpaces; } @@ -527,6 +532,7 @@ static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window, case EGL_GL_COLORSPACE_LINEAR_KHR: case EGL_GL_COLORSPACE_SRGB_KHR: case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: case EGL_GL_COLORSPACE_SCRGB_EXT: case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp index 459b1356b8..cca91c3a6f 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -217,6 +217,7 @@ TEST_F(EGLTest, EGLDisplayP3) { // Test that display-p3 extensions exist ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3")); ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear")); + ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough")); // Use 8-bit to keep forcus on Display-P3 aspect EGLint attrs[] = { @@ -289,6 +290,59 @@ TEST_F(EGLTest, EGLDisplayP3) { EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface)); } +TEST_F(EGLTest, EGLDisplayP3Passthrough) { + EGLConfig config; + EGLBoolean success; + + if (!hasWideColorDisplay) { + // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; + return; + } + + // Test that display-p3 extensions exist + ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3")); + ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear")); + ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough")); + + get8BitConfig(config); + + struct DummyConsumer : public BnConsumerListener { + void onFrameAvailable(const BufferItem& /* item */) override {} + void onBuffersReleased() override {} + void onSidebandStreamChanged() override {} + }; + + // Create a EGLSurface + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + consumer->consumerConnect(new DummyConsumer, false); + sp<Surface> mSTC = new Surface(producer); + sp<ANativeWindow> mANW = mSTC; + EGLint winAttrs[] = { + // clang-format off + EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, + EGL_NONE, EGL_NONE + // clang-format on + }; + + EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, eglSurface); + + android_dataspace dataspace = + static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(mANW.get())); + ASSERT_EQ(dataspace, HAL_DATASPACE_DISPLAY_P3); + + EGLint value; + success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value); + ASSERT_EQ(EGL_UNSIGNED_TRUE, success); + ASSERT_EQ(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, value); + + EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface)); +} + TEST_F(EGLTest, EGLDisplayP31010102) { EGLint numConfigs; EGLConfig config; @@ -303,6 +357,7 @@ TEST_F(EGLTest, EGLDisplayP31010102) { // Test that display-p3 extensions exist ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3")); ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear")); + ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough")); // Use 8-bit to keep forcus on Display-P3 aspect EGLint attrs[] = { diff --git a/opengl/tests/lib/include/EGLUtils.h b/opengl/tests/lib/include/EGLUtils.h index eb9571d478..cfa378f354 100644 --- a/opengl/tests/lib/include/EGLUtils.h +++ b/opengl/tests/lib/include/EGLUtils.h @@ -280,6 +280,8 @@ String8 EGLUtils::decodeColorSpace(EGLint colorSpace) { return String8("EGL_GL_COLORSPACE_SRGB_KHR"); case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: return String8("EGL_GL_COLORSPACE_DISPLAY_P3_EXT"); + case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: + return String8("EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"); case EGL_GL_COLORSPACE_LINEAR_KHR: return String8("EGL_GL_COLORSPACE_LINEAR_KHR"); default: diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml index 26771e19b5..8d661aea43 100644 --- a/opengl/tools/glgen2/registry/egl.xml +++ b/opengl/tools/glgen2/registry/egl.xml @@ -894,6 +894,11 @@ <unused start="0x3481" end="0x348F"/> </enums> + <enums namespace="EGL" start="0x3490" end="0x349F" vendor="EXT" comment="Reserved for Courtney Goeltzenleuchter - Android (gitlab EGL bug 69)"> + <enum value="0x3490" name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/> + <unused start="0x3491" end="0x349F"/> + </enums> + <!-- Please remember that new enumerant allocations must be obtained by request to the Khronos API registrar (see comments at the top of this file) File requests in the Khronos Bugzilla, EGL project, Registry @@ -903,8 +908,8 @@ <!-- Reservable for future use. To generate a new range, allocate multiples of 16 starting at the lowest available point in this block. --> - <enums namespace="EGL" start="0x3490" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> - <unused start="0x3490" end="0x3FFF"/> + <enums namespace="EGL" start="0x34A0" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> + <unused start="0x34A0" end="0x3FFF"/> </enums> <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL."> @@ -2250,6 +2255,11 @@ <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/> </require> </extension> + <extension name="EGL_EXT_gl_colorspace_display_p3_passthrough" supported="egl"> + <require> + <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/> + </require> + </extension> <extension name="EGL_EXT_image_dma_buf_import" supported="egl"> <require> <enum name="EGL_LINUX_DMA_BUF_EXT"/> diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 9e748d8975..9ba4140312 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -2638,6 +2638,7 @@ void CursorInputMapper::configure(nsecs_t when, mOrientation = internalViewport->orientation; } } + getPolicy()->updatePointerDisplay(); bumpGeneration(); } } @@ -2783,7 +2784,7 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = ADISPLAY_ID_DEFAULT; + displayId = mPointerController->getDisplayId(); } else { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); @@ -3601,6 +3602,9 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { if (mPointerController == nullptr) { mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + getPolicy()->updatePointerDisplay(); + } else if (viewportChanged) { + getPolicy()->updatePointerDisplay(); } } else { mPointerController.clear(); @@ -5387,7 +5391,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + int32_t displayId = mPointerController->getDisplayId(); + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, @@ -6294,6 +6299,7 @@ void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering) { int32_t metaState = getContext()->getGlobalMetaState(); + int32_t displayId = mViewport.displayId; if (mPointerController != nullptr) { if (down || hovering) { @@ -6304,13 +6310,14 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } + displayId = mPointerController->getDisplayId(); } if (mPointerSimple.down && !down) { mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0, /* deviceTimestamp */ 0, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, @@ -6323,7 +6330,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0, /* deviceTimestamp */ 0, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, @@ -6338,7 +6345,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0, /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, @@ -6348,7 +6355,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send move. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, @@ -6362,7 +6369,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, 0, /* deviceTimestamp */ 0, @@ -6373,7 +6380,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send hover move. - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, /* deviceTimestamp */ 0, @@ -6395,7 +6402,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0, /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &pointerCoords, @@ -6457,8 +6464,9 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 ALOG_ASSERT(false); } } - - NotifyMotionArgs args(when, getDeviceId(), source, mViewport.displayId, policyFlags, + int32_t displayId = mPointerController != nullptr ? + mPointerController->getDisplayId() : mViewport.displayId; + NotifyMotionArgs args(when, getDeviceId(), source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index fe1c50bc75..5a78df75ff 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -342,6 +342,9 @@ public: /* Gets the affine calibration associated with the specified device. */ virtual TouchAffineTransformation getTouchAffineTransformation( const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0; + + /* Update the pointer controller associated with the specified display. */ + virtual void updatePointerDisplay() = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index e94dd94868..e60b3f48b5 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -58,6 +58,9 @@ public: /* Gets the absolute location of the pointer. */ virtual void getPosition(float* outX, float* outY) const = 0; + /* Gets the id of the display where the pointer should be shown. */ + virtual int32_t getDisplayId() const = 0; + enum Transition { // Fade/unfade immediately. TRANSITION_IMMEDIATE, diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 04b87d5a33..b18cae3e8f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -57,6 +57,7 @@ class FakePointerController : public PointerControllerInterface { float mMinX, mMinY, mMaxX, mMaxY; float mX, mY; int32_t mButtonState; + int32_t mDisplayId; protected: virtual ~FakePointerController() { } @@ -64,7 +65,7 @@ protected: public: FakePointerController() : mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0), - mButtonState(0) { + mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) { } void setBounds(float minX, float minY, float maxX, float maxY) { @@ -75,6 +76,10 @@ public: mMaxY = maxY; } + void setDisplayId(int32_t displayId) { + mDisplayId = displayId; + } + virtual void setPosition(float x, float y) { mX = x; mY = y; @@ -93,6 +98,10 @@ public: *outY = mY; } + virtual int32_t getDisplayId() const { + return mDisplayId; + } + private: virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const { *outMinX = mMinX; @@ -262,6 +271,9 @@ private: virtual std::string getDeviceAlias(const InputDeviceIdentifier&) { return ""; } + + virtual void updatePointerDisplay() { + } }; @@ -3127,6 +3139,30 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); } +TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { + CursorInputMapper* mapper = new CursorInputMapper(mDevice); + addMapperAndConfigure(mapper); + + // Setup PointerController for second display. + constexpr int32_t SECOND_DISPLAY_ID = 1; + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + mFakePointerController->setButtonState(0); + mFakePointerController->setDisplayId(SECOND_DISPLAY_ID); + + NotifyMotionArgs args; + process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], + 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId); +} + // --- TouchInputMapperTest --- @@ -4625,7 +4661,6 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } - // --- MultiTouchInputMapperTest --- class MultiTouchInputMapperTest : public TouchInputMapperTest { @@ -6246,4 +6281,30 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { ASSERT_EQ(DISPLAY_ID, args.displayId); } +TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { + // Setup PointerController for second display. + sp<FakePointerController> fakePointerController = new FakePointerController(); + fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + fakePointerController->setPosition(100, 200); + fakePointerController->setButtonState(0); + fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID); + mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + // Check source is mouse that would obtain the PointerController. + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources()); + + NotifyMotionArgs motionArgs; + processPosition(mapper, 100, 100); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); +} + } // namespace android diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 8dc80cce18..776efab19c 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -32,8 +32,9 @@ SensorService::SensorEventConnection::SensorEventConnection( const String16& opPackageName, bool hasSensorAccess) : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false), mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr), - mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName), - mDestroyed(false), mHasSensorAccess(hasSensorAccess) { + mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0), + mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false), + mHasSensorAccess(hasSensorAccess) { mChannel = new BitTube(mService->mSocketBufferSize); #if DEBUG_CONNECTIONS mEventsReceived = mEventsSentFromCache = mEventsSent = 0; @@ -405,9 +406,6 @@ void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_eve reAllocateCacheLocked(events, count); } else { // The events do not fit within the cache: drop the oldest events. - ALOGW("Dropping events from cache (%d / %d) to save %d newer events", mCacheSize, - mMaxCacheSize, count); - int freeSpace = mMaxCacheSize - mCacheSize; // Drop up to the currently cached number of events to make room for new events @@ -419,6 +417,18 @@ void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_eve // Determine the number of new events to copy into the cache int eventsToCopy = std::min(mMaxCacheSize, count); + constexpr nsecs_t kMinimumTimeBetweenDropLogNs = 2 * 1000 * 1000 * 1000; // 2 sec + if (events[0].timestamp - mTimeOfLastEventDrop > kMinimumTimeBetweenDropLogNs) { + ALOGW("Dropping %d cached events (%d/%d) to save %d/%d new events. %d events previously" + " dropped", cachedEventsToDrop, mCacheSize, mMaxCacheSize, eventsToCopy, + count, mEventsDropped); + mEventsDropped = 0; + mTimeOfLastEventDrop = events[0].timestamp; + } else { + // Record the number dropped + mEventsDropped += cachedEventsToDrop + newEventsToDrop; + } + // Check for any flush complete events in the events that will be dropped countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop); countFlushCompleteEventsLocked(events, newEventsToDrop); diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index eefd81a258..061809f363 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -165,6 +165,8 @@ private: sensors_event_t *mEventCache; int mCacheSize, mMaxCacheSize; + int64_t mTimeOfLastEventDrop; + int mEventsDropped; String8 mPackageName; const String16 mOpPackageName; #if DEBUG_CONNECTIONS diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 42b7146fd1..1a13f77182 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -24,6 +24,7 @@ cc_test { }, srcs: [ ":libsurfaceflinger_sources", + "libsurfaceflinger_unittest_main.cpp", "CompositionTest.cpp", "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index bfd34cd4db..86f1a39b81 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -27,6 +27,8 @@ #include "TimeStats/TimeStats.h" +#include "libsurfaceflinger_unittest_main.h" + using namespace android::surfaceflinger; using namespace google::protobuf; @@ -486,6 +488,10 @@ TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) { } TEST_F(TimeStatsTest, canSurviveMonkey) { + if (g_noSlowTests) { + GTEST_SKIP(); + } + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); for (size_t i = 0; i < 10000000; ++i) { diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp new file mode 100644 index 0000000000..bc1f00d906 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "libsurfaceflinger_unittest_main.h" + +// ------------------------------------------------------------------------ +// To pass extra command line arguments to the Google Test executable from +// atest, you have to use this somewhat verbose syntax: +// +// clang-format off +// +// atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:<--flag>[:<value>] +// +// For example: +// +// atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:--no-slow +// +// clang-format on +// ------------------------------------------------------------------------ + +// Set to true if "--no-slow" is passed to the test. +bool g_noSlowTests = false; + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--no-slow") == 0) { + g_noSlowTests = true; + } + } + + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h new file mode 100644 index 0000000000..e742c5079c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h @@ -0,0 +1,20 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// Set to true if "--no-slow" is passed to the test. +extern bool g_noSlowTests; diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp index f936e95d38..158ef9ceaa 100644 --- a/services/vr/bufferhubd/consumer_channel.cpp +++ b/services/vr/bufferhubd/consumer_channel.cpp @@ -153,6 +153,17 @@ Status<void> ConsumerChannel::OnConsumerRelease(Message& message, } } +void ConsumerChannel::OnProducerGained() { + // Clear the signal if exist. There is a possiblity that the signal still + // exist in consumer client when producer gains the buffer, e.g. newly added + // consumer fail to acquire the previous posted buffer in time. Then, when + // producer gains back the buffer, posts the buffer again and signal the + // consumer later, there won't be an signal change in eventfd, and thus, + // consumer will miss the posted buffer later. Thus, we need to clear the + // signal in consumer clients if the signal exist. + ClearAvailable(); +} + void ConsumerChannel::OnProducerPosted() { acquired_ = false; released_ = false; diff --git a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h index de0f23c5d0..5fb4ec1725 100644 --- a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h +++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h @@ -26,6 +26,7 @@ class ConsumerChannel : public BufferHubChannel { uint64_t client_state_mask() const { return client_state_mask_; } BufferInfo GetBufferInfo() const override; + void OnProducerGained(); void OnProducerPosted(); void OnProducerClosed(); diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp index ab1d94c3cd..b541fb3963 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -424,7 +424,7 @@ Status<void> ProducerChannel::OnProducerPost(Message&, // Signal any interested consumers. If there are none, the buffer will stay // in posted state until a consumer comes online. This behavior guarantees // that no frame is silently dropped. - for (auto consumer : consumer_channels_) { + for (auto& consumer : consumer_channels_) { consumer->OnProducerPosted(); } @@ -437,6 +437,9 @@ Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) { ClearAvailable(); post_fence_.close(); + for (auto& consumer : consumer_channels_) { + consumer->OnProducerGained(); + } return {std::move(returned_fence_)}; } @@ -533,7 +536,7 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); - if (BufferHubDefs::IsClientReleased(current_buffer_state, + if (BufferHubDefs::IsBufferReleased(current_buffer_state & ~orphaned_consumer_bit_mask_)) { SignalAvailable(); if (orphaned_consumer_bit_mask_) { @@ -560,7 +563,7 @@ void ProducerChannel::OnConsumerOrphaned(const uint64_t& consumer_state_mask) { uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); - if (BufferHubDefs::IsClientReleased(current_buffer_state, + if (BufferHubDefs::IsBufferReleased(current_buffer_state & ~orphaned_consumer_bit_mask_)) { SignalAvailable(); } diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 7eaf7b2d73..fed8481e7b 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -39,6 +39,9 @@ cc_library_shared { "-Wno-switch-enum", "-Wno-undef", + // Have clang emit complete debug_info. + "-fstandalone-debug", + //"-DLOG_NDEBUG=0", ], |