diff options
67 files changed, 2226 insertions, 273 deletions
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index 1d5b738c04..eb0d50da28 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -44,7 +44,7 @@ static binder::Status error(uint32_t code, const std::string& msg) { static void* callAndNotify(void* data) { Dumpstate& ds = *static_cast<Dumpstate*>(data); ds.Run(); - MYLOGE("Finished Run()\n"); + MYLOGD("Finished Run()\n"); return nullptr; } @@ -104,9 +104,6 @@ binder::Status DumpstateService::startBugreport(int32_t /* calling_uid */, const android::base::unique_fd& screenshot_fd, int bugreport_mode, const sp<IDumpstateListener>& listener) { - // TODO(b/111441001): - // 1. check DUMP permission (again)? - // 2. check if primary user? If non primary user the consent service will reject anyway. MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL && @@ -143,7 +140,18 @@ binder::Status DumpstateService::startBugreport(int32_t /* calling_uid */, return binder::Status::ok(); } +binder::Status DumpstateService::cancelBugreport() { + // This is a no-op since the cancellation is done from java side via setting sys properties. + // See BugreportManagerServiceImpl. + // TODO(b/111441001): maybe make native and java sides use different binder interface + // to avoid these annoyances. + return binder::Status::ok(); +} + status_t DumpstateService::dump(int fd, const Vector<String16>&) { + std::string destination = ds_.options_->bugreport_fd.get() != -1 + ? StringPrintf("[fd:%d]", ds_.options_->bugreport_fd.get()) + : ds_.bugreport_internal_dir_.c_str(); dprintf(fd, "id: %d\n", ds_.id_); dprintf(fd, "pid: %d\n", ds_.pid_); dprintf(fd, "update_progress: %s\n", ds_.options_->do_progress_updates ? "true" : "false"); @@ -154,8 +162,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "args: %s\n", ds_.options_->args.c_str()); 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, "bugreport_dir: %s\n", destination.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/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index b6ba32d695..faeea5350a 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -47,6 +47,9 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst const android::base::unique_fd& screenshot_fd, int bugreport_mode, const sp<IDumpstateListener>& listener) override; + // No-op + binder::Status cancelBugreport(); + private: Dumpstate& ds_; std::mutex lock_; diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md index 6ac17d8772..273a5a645b 100644 --- a/cmds/dumpstate/README.md +++ b/cmds/dumpstate/README.md @@ -49,7 +49,7 @@ adb shell mkdir /data/nativetest64 Then run: ``` -mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test +mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test ``` And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`): diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 26351572db..f58535eed0 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -79,4 +79,9 @@ interface IDumpstate { void startBugreport(int callingUid, @utf8InCpp String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode, IDumpstateListener listener); + + /* + * Cancels the bugreport currently in progress. + */ + void cancelBugreport(); } diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index bcd0cb7966..8396acd0b4 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl @@ -44,14 +44,9 @@ interface IDumpstateListener { oneway void onError(int errorCode); /** - * Called when taking bugreport finishes successfully - * - * @param durationMs time capturing bugreport took in milliseconds - * @param title title for the bugreport; helpful in reminding the user why they took it - * @param description detailed description for the bugreport + * Called when taking bugreport finishes successfully. */ - oneway void onFinished(long durationMs, @utf8InCpp String title, - @utf8InCpp String description); + oneway void onFinished(); // TODO(b/111441001): Remove old methods when not used anymore. void onProgressUpdated(int progress); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 8c8cd51968..7958865178 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -135,10 +135,6 @@ static int Open(std::string path, int flags, mode_t mode = 0) { 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); @@ -169,17 +165,6 @@ static bool CopyFileToFd(const std::string& input_file, int out_fd) { 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 @@ -1675,6 +1660,7 @@ static void ShowUsage() { "progress (requires -o and -B)\n" " -R: take bugreport in remote mode (requires -o, -z, -d and -B, " "shouldn't be used with -P)\n" + " -w: start binder service and make it wait for a call to startBugreport\n" " -v: prints the dumpstate header and exit\n"); } @@ -1806,16 +1792,9 @@ static void MaybeResolveSymlink(std::string* path) { static void PrepareToWriteToFile() { 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_ = - StringPrintf("%s-%s-%s", base_name_part1.c_str(), device_name.c_str(), build_id.c_str()); + ds.base_name_ = StringPrintf("bugreport-%s-%s", 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_)); @@ -1838,17 +1817,16 @@ static void PrepareToWriteToFile() { std::string destination = ds.options_->bugreport_fd.get() != -1 ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get()) - : ds.bugreport_dir_.c_str(); + : ds.bugreport_internal_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", - 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()); + destination.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"); @@ -1915,18 +1893,13 @@ static void FinalizeFile() { } } // The zip file lives in an internal directory. Copy it over to output. - bool copy_succeeded = false; if (ds.options_->bugreport_fd.get() != -1) { - copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get()); - } 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())) { + bool copy_succeeded = + android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get()); + if (!copy_succeeded && remove(ds.path_.c_str())) { MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno)); } - } + } // else - the file just remains in the internal directory. } } if (do_text_file) { @@ -1952,8 +1925,8 @@ static void FinalizeFile() { /* Broadcasts that we are done with the bugreport */ static void SendBugreportFinishedBroadcast() { // TODO(b/111441001): use callback instead of broadcast. - if (!ds.final_path_.empty()) { - MYLOGI("Final bugreport path: %s\n", ds.final_path_.c_str()); + if (!ds.path_.empty()) { + MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); // clang-format off std::vector<std::string> am_args = { @@ -1961,7 +1934,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.final_path_, + "--es", "android.intent.extra.BUGREPORT", ds.path_, "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ }; // clang-format on @@ -1983,7 +1956,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.final_path_)); + am_args.push_back(SHA256_file_hash(ds.path_)); SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); } else { SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); @@ -2121,7 +2094,6 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI("wifi_only: %d\n", options.wifi_only); MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); MYLOGI("fd: %d\n", options.bugreport_fd.get()); - 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()); MYLOGI("notification_title: %s\n", options.notification_title.c_str()); @@ -2147,12 +2119,14 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) { RunStatus status = RunStatus::OK; int c; - while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) { + while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = true; break; case 'z': do_zip_file = true; break; - case 'o': use_outfile = optarg; break; + // o=use_outfile not supported anymore. + // TODO(b/111441001): Remove when all callers have migrated. + case 'o': break; case 's': use_socket = true; break; case 'S': use_control_socket = true; break; case 'v': show_header_only = true; break; @@ -2162,6 +2136,9 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) case 'R': is_remote_mode = true; break; case 'B': do_broadcast = true; break; case 'V': break; // compatibility no-op + case 'w': + // This was already processed + break; case 'h': status = RunStatus::HELP; break; @@ -2193,9 +2170,7 @@ bool Dumpstate::DumpOptions::ValidateOptions() const { return false; } - bool has_out_file_options = !use_outfile.empty() || bugreport_fd.get() != -1; - if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && - !has_out_file_options) { + if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !OutputToFile()) { return false; } @@ -2222,9 +2197,7 @@ Dumpstate::RunStatus Dumpstate::Run() { if (listener_ != nullptr) { switch (status) { case Dumpstate::RunStatus::OK: - // TODO(b/111441001): duration argument does not make sense. Remove. - listener_->onFinished(0 /* duration */, options_->notification_title, - options_->notification_description); + listener_->onFinished(); break; case Dumpstate::RunStatus::HELP: break; @@ -2257,8 +2230,8 @@ Dumpstate::RunStatus Dumpstate::Run() { * 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. + * Bugreports are first generated in a local directory and later copied to the caller's fd if + * supplied. */ Dumpstate::RunStatus Dumpstate::RunInternal() { LogDumpOptions(*options_); @@ -2299,7 +2272,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal() { } // Redirect output if needed - bool is_redirecting = !options_->use_socket && !options_->use_outfile.empty(); + bool is_redirecting = options_->OutputToFile(); // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = @@ -2450,7 +2423,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal() { } /* rename or zip the (now complete) .tmp file to its final location */ - if (!options_->use_outfile.empty()) { + if (options_->OutputToFile()) { FinalizeFile(); } diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index c620c079a7..3dfe4e955f 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -331,6 +331,7 @@ class Dumpstate { bool do_add_date = false; bool do_zip_file = false; bool do_vibrate = true; + // Writes bugreport content to a socket; only flatfile format is supported. bool use_socket = false; bool use_control_socket = false; bool do_fb = false; @@ -342,13 +343,11 @@ class Dumpstate { bool wifi_only = false; // Whether progress updates should be published. bool do_progress_updates = false; - // File descriptor to output zip file. Takes precedence over use_outfile. + // File descriptor to output zip file. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. // TODO(b/111441001): Use this fd. android::base::unique_fd screenshot_fd; - // Partial path to output file. - std::string use_outfile; // TODO: rename to MODE. // Extra options passed as system property. std::string extra_options; @@ -367,6 +366,13 @@ class Dumpstate { /* Returns true if the options set so far are consistent. */ bool ValidateOptions() const; + + /* Returns if options specified require writing bugreport to a file */ + bool OutputToFile() const { + // If we are not writing to socket, we will write to a file. If bugreport_fd is + // specified, it is preferred. If not bugreport is written to /bugreports. + return !use_socket; + } }; // TODO: initialize fields on constructor @@ -424,13 +430,6 @@ class Dumpstate { // 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/dumpstate.rc b/cmds/dumpstate/dumpstate.rc index 2e72574738..14937b80b9 100644 --- a/cmds/dumpstate/dumpstate.rc +++ b/cmds/dumpstate/dumpstate.rc @@ -17,3 +17,9 @@ service dumpstatez /system/bin/dumpstate -S -d -z \ class main disabled oneshot + +# bugreportd starts dumpstate binder service and makes it wait for a listener to connect. +service bugreportd /system/bin/dumpstate -w + class main + disabled + oneshot diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp index 78aad1137b..68d373377c 100644 --- a/cmds/dumpstate/main.cpp +++ b/cmds/dumpstate/main.cpp @@ -14,8 +14,55 @@ * limitations under the License. */ +#define LOG_TAG "dumpstate" + +#include <binder/IPCThreadState.h> + +#include "DumpstateInternal.h" +#include "DumpstateService.h" #include "dumpstate.h" +namespace { + +// Returns true if we should start the service and wait for a listener +// to bind with bugreport options. +bool ShouldStartServiceAndWait(int argc, char* argv[]) { + bool do_wait = false; + int c; + // Keep flags in sync with Dumpstate::DumpOptions::Initialize. + while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) { + switch (c) { + case 'w': + do_wait = true; + break; + default: + // Ignore all other options + break; + } + } + + // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to + // parse bugreport options. + optind = 1; + return do_wait; +} + +} // namespace + int main(int argc, char* argv[]) { - return run_main(argc, argv); + if (ShouldStartServiceAndWait(argc, argv)) { + int ret; + if ((ret = android::os::DumpstateService::Start()) != android::OK) { + MYLOGE("Unable to start 'dumpstate' service: %d", ret); + exit(1); + } + MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()"); + + // Waits forever for an incoming connection. + // TODO(b/111441001): should this time out? + android::IPCThreadState::self()->joinThreadPool(); + return 0; + } else { + return run_main(argc, argv); + } } diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index c57775fac8..570c6c9b61 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -61,9 +61,8 @@ class DumpstateListener : public IDumpstateListener { dprintf(outFd_, "\rError %d", error_code); return binder::Status::ok(); } - binder::Status onFinished(int64_t duration_ms, const ::std::string&, - const ::std::string&) override { - dprintf(outFd_, "\rFinished in %lld", (long long) duration_ms); + binder::Status onFinished() override { + dprintf(outFd_, "\rFinished"); return binder::Status::ok(); } binder::Status onProgressUpdated(int32_t progress) override { diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index cd9b97ff9d..eb73d41dc4 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -61,8 +61,7 @@ class DumpstateListenerMock : public IDumpstateListener { public: MOCK_METHOD1(onProgress, binder::Status(int32_t progress)); MOCK_METHOD1(onError, binder::Status(int32_t error_code)); - MOCK_METHOD3(onFinished, binder::Status(int64_t duration_ms, const ::std::string& title, - const ::std::string& description)); + MOCK_METHOD0(onFinished, binder::Status()); MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, @@ -173,7 +172,6 @@ TEST_F(DumpOptionsTest, InitializeNone) { EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); - EXPECT_EQ("", options_.use_outfile); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); @@ -191,7 +189,6 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { const_cast<char*>("-S"), const_cast<char*>("-d"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -201,7 +198,6 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.use_control_socket); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -228,7 +224,6 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { // Other options retain default values EXPECT_TRUE(options_.do_vibrate); - EXPECT_EQ("", options_.use_outfile); EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); EXPECT_FALSE(options_.use_control_socket); @@ -247,7 +242,6 @@ TEST_F(DumpOptionsTest, InitializeFullBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on property_set("dumpstate.options", "bugreportfull"); @@ -259,7 +253,6 @@ TEST_F(DumpOptionsTest, InitializeFullBugReport) { EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_broadcast); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -279,7 +272,6 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -294,7 +286,6 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_start_service); EXPECT_FALSE(options_.do_fb); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -312,7 +303,6 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -327,7 +317,6 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { EXPECT_TRUE(options_.is_remote_mode); EXPECT_FALSE(options_.do_vibrate); EXPECT_FALSE(options_.do_fb); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_FALSE(options_.use_control_socket); @@ -344,7 +333,6 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -359,7 +347,6 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_start_service); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -377,7 +364,6 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -391,7 +377,6 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.telephony_only); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -410,7 +395,6 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -424,7 +408,6 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.wifi_only); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -444,7 +427,6 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { const_cast<char*>("-p"), const_cast<char*>("-B"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), }; // clang-format on @@ -457,7 +439,6 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_broadcast); - EXPECT_EQ(" abc", std::string(options_.use_outfile)); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -475,7 +456,6 @@ TEST_F(DumpOptionsTest, InitializePartial1) { const_cast<char*>("dumpstate"), const_cast<char*>("-d"), const_cast<char*>("-z"), - const_cast<char*>("-o abc"), const_cast<char*>("-s"), const_cast<char*>("-S"), @@ -488,7 +468,6 @@ TEST_F(DumpOptionsTest, InitializePartial1) { EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_zip_file); // TODO: Maybe we should trim the filename - EXPECT_EQ(" abc", std::string(options_.use_outfile)); EXPECT_TRUE(options_.use_socket); EXPECT_TRUE(options_.use_control_socket); @@ -527,7 +506,6 @@ TEST_F(DumpOptionsTest, InitializePartial2) { // Other options retain default values EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); - EXPECT_EQ("", options_.use_outfile); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.use_control_socket); } @@ -562,15 +540,21 @@ TEST_F(DumpOptionsTest, InitializeUnknown) { TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) { options_.do_zip_file = true; + // Writing to socket = !writing to file. + options_.use_socket = true; EXPECT_FALSE(options_.ValidateOptions()); - options_.use_outfile = "a/b/c"; + + options_.use_socket = false; EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) { options_.do_broadcast = true; + // Writing to socket = !writing to file. + options_.use_socket = true; EXPECT_FALSE(options_.ValidateOptions()); - options_.use_outfile = "a/b/c"; + + options_.use_socket = false; EXPECT_TRUE(options_.ValidateOptions()); } @@ -579,13 +563,11 @@ TEST_F(DumpOptionsTest, ValidateOptionsNeedZipfile) { EXPECT_FALSE(options_.ValidateOptions()); options_.do_zip_file = true; - options_.use_outfile = "a/b/c"; // do_zip_file needs outfile EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsUpdateProgressNeedsBroadcast) { options_.do_progress_updates = true; - options_.use_outfile = "a/b/c"; // do_progress_updates needs outfile EXPECT_FALSE(options_.ValidateOptions()); options_.do_broadcast = true; @@ -599,7 +581,6 @@ TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) { options_.do_broadcast = true; options_.do_zip_file = true; options_.do_add_date = true; - options_.use_outfile = "a/b/c"; // do_broadcast needs outfile EXPECT_TRUE(options_.ValidateOptions()); } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index e494e9cc85..51f30da9c4 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -41,6 +41,7 @@ #include <android-base/logging.h> #include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> @@ -784,6 +785,162 @@ binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::st return ok(); } +static int32_t copy_directory_recursive(const char* from, const char* to) { + char *argv[] = { + (char*) kCpPath, + (char*) "-F", /* delete any existing destination file first (--remove-destination) */ + (char*) "-p", /* preserve timestamps, ownership, and permissions */ + (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ + (char*) "-P", /* Do not follow symlinks [default] */ + (char*) "-d", /* don't dereference symlinks */ + (char*) from, + (char*) to + }; + + LOG(DEBUG) << "Copying " << from << " to " << to; + return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); +} + +// TODO(narayan): We should pass through the ceDataInode so that we can call +// clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence +// the copy. +// +// TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we +// should validate that volumeUuid is either nullptr or TEST, we won't support +// anything else. +// +// TODO(narayan): We need to be clearer about the expected behaviour for the +// case where a snapshot already exists. We either need to clear the contents +// of the snapshot directory before we make a copy, or we need to ensure that +// the caller always clears it before requesting a snapshot. +binder::Status InstalldNativeService::snapshotAppData( + const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, int32_t user, int32_t storageFlags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> lock(mLock); + + const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; + const char* package_name = packageName.c_str(); + + binder::Status res = ok(); + bool clear_ce_on_exit = false; + bool clear_de_on_exit = false; + + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] { + if (clear_de_on_exit) { + auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to delete app data snapshot: " << to; + } + } + + if (clear_ce_on_exit) { + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to delete app data snapshot: " << to; + } + } + }; + + auto scope_guard = android::base::make_scope_guard(deleter); + + // The app may not have any data at all, in which case it's OK to skip here. + auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); + if (access(from_ce.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from_ce; + return ok(); + } + + if (storageFlags & FLAG_STORAGE_DE) { + auto from = create_data_user_de_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_de_rollback_path(volume_uuid, user); + + int rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + clear_de_on_exit = true; + return res; + } + } + + if (storageFlags & FLAG_STORAGE_CE) { + auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_ce_rollback_path(volume_uuid, user); + int rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + clear_ce_on_exit = true; + return res; + } + } + + return res; +} + +binder::Status InstalldNativeService::restoreAppDataSnapshot( + const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, + const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, + const int32_t user, int32_t storageFlags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> lock(mLock); + + const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; + const char* package_name = packageName.c_str(); + + auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, + user, package_name); + auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, + user, package_name); + + const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && + (access(from_ce.c_str(), F_OK) == 0); + const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) && + (access(from_de.c_str(), F_OK) == 0); + + if (!needs_ce_rollback && !needs_de_rollback) { + return ok(); + } + + // We know we're going to rollback one of the CE or DE data, so we clear + // application data first. Note that it's possible that we're asked to + // restore both CE & DE data but that one of the restores fail. Leaving the + // app with no data in those cases is arguably better than leaving the app + // with mismatched / stale data. + LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; + binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode); + if (!res.isOk()) { + return res; + } + + if (needs_ce_rollback) { + auto to_ce = create_data_user_ce_path(volume_uuid, user); + int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from_ce + " to " + to_ce); + return res; + } + } + + if (needs_de_rollback) { + auto to_de = create_data_user_de_path(volume_uuid, user); + int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); + if (rc != 0) { + // TODO(narayan): Should we clear clear the rolled back CE data if + // something goes wrong here ? We're choosing between leaving the + // app devoid of all its data or with just its ce data installed. + res = error(rc, "Failed copying " + from_de + " to " + to_de); + return res; + } + } + + // Finally, restore the SELinux label on the app data. + return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); +} + binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, const std::unique_ptr<std::string>& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, @@ -808,19 +965,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: auto to = create_data_app_package_path(to_uuid, data_app_name); auto to_parent = create_data_app_path(to_uuid); - char *argv[] = { - (char*) kCpPath, - (char*) "-F", /* delete any existing destination file first (--remove-destination) */ - (char*) "-p", /* preserve timestamps, ownership, and permissions */ - (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ - (char*) "-P", /* Do not follow symlinks [default] */ - (char*) "-d", /* don't dereference symlinks */ - (char*) from.c_str(), - (char*) to_parent.c_str() - }; - - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); + int rc = copy_directory_recursive(from.c_str(), to_parent.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; @@ -848,25 +993,11 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: goto fail; } - char *argv[] = { - (char*) kCpPath, - (char*) "-F", /* delete any existing destination file first (--remove-destination) */ - (char*) "-p", /* preserve timestamps, ownership, and permissions */ - (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ - (char*) "-P", /* Do not follow symlinks [default] */ - (char*) "-d", /* don't dereference symlinks */ - nullptr, - nullptr - }; - { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); - argv[6] = (char*) from.c_str(); - argv[7] = (char*) to.c_str(); - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); + int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; @@ -875,11 +1006,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: { auto from = create_data_user_ce_package_path(from_uuid, user, package_name); auto to = create_data_user_ce_path(to_uuid, user); - argv[6] = (char*) from.c_str(); - argv[7] = (char*) to.c_str(); - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); + int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index d482472605..098a0c2985 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -60,6 +60,12 @@ public: binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); + binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, const int32_t user, int32_t storageFlags); + binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, const int32_t appId, const int64_t ceDataInode, + const std::string& seInfo, const int32_t user, int32_t storageFlags); + binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index dfd62bf743..3d093b8883 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -105,6 +105,15 @@ interface IInstalld { int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); + void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int userId, int storageFlags); + void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags); + + // TODO(narayan) we need an API to delete the app data snapshot as well. + // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, + // in @utf8InCpp String packageName, int userId, int storageFlags); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 9dad99563d..e6017cb761 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -21,6 +21,8 @@ #include <sys/xattr.h> #include <android-base/logging.h> +#include <android-base/file.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> @@ -240,5 +242,180 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } +static bool mkdirs(const std::string& path, mode_t mode) { + struct stat sb; + if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { + return true; + } + + if (!mkdirs(android::base::Dirname(path), mode)) { + return false; + } + + return (::mkdir(path.c_str(), mode) != -1); +} + +TEST_F(ServiceTest, CreateAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); + + auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); + auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); + + ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); + ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); + + auto deleter = [&rollback_ce_dir, &rollback_de_dir, + &fake_package_ce_path, &fake_package_de_path]() { + delete_dir_contents(rollback_ce_dir, true); + delete_dir_contents(rollback_de_dir, true); + delete_dir_contents(fake_package_ce_path, true); + delete_dir_contents(fake_package_de_path, true); + rmdir(rollback_ce_dir.c_str()); + rmdir(rollback_de_dir.c_str()); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of the CE content but not the DE content. + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, FLAG_STORAGE_CE).isOk()); + + std::string ce_content, de_content; + // At this point, we should have the CE content but not the DE content. + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_FALSE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", ce_content); + + // Modify the CE content, so we can assert later that it's reflected + // in the snapshot. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of the DE content but not the CE content. + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, FLAG_STORAGE_DE).isOk()); + + // At this point, both the CE as well as the DE content should be fully + // populated. + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", ce_content); + ASSERT_EQ("TEST_CONTENT_DE", de_content); + + // Modify the DE content, so we can assert later that it's reflected + // in our final snapshot. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of both the CE as well as the DE content. + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content); + ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); +} + +TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); + + auto deleter = [&rollback_ce_dir, &rollback_de_dir]() { + delete_dir_contents(rollback_ce_dir, true); + delete_dir_contents(rollback_de_dir, true); + rmdir(rollback_ce_dir.c_str()); + rmdir(rollback_de_dir.c_str()); + }; + + auto scope_guard = android::base::make_scope_guard(deleter); + + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, FLAG_STORAGE_CE).isOk()); + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, FLAG_STORAGE_DE).isOk()); + + // The snapshot calls must succeed but there should be no snapshot + // created. + struct stat sb; + ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); +} + +TEST_F(ServiceTest, RestoreAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); + + auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); + auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); + + ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); + ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); + + auto deleter = [&rollback_ce_dir, &rollback_de_dir, + &fake_package_ce_path, &fake_package_de_path]() { + delete_dir_contents(rollback_ce_dir, true); + delete_dir_contents(rollback_de_dir, true); + delete_dir_contents(fake_package_ce_path, true); + delete_dir_contents(fake_package_de_path, true); + rmdir(rollback_ce_dir.c_str()); + rmdir(rollback_de_dir.c_str()); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + // Write contents to the rollback location. We'll write the same files to the + // app data location and make sure the restore has overwritten them. + ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700)); + ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700)); + ASSERT_TRUE(android::base::WriteStringToFile( + "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), + "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + + std::string ce_content, de_content; + ASSERT_TRUE(android::base::ReadFileToString( + fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("CE_RESTORE_CONTENT", ce_content); + ASSERT_EQ("DE_RESTORE_CONTENT", de_content); +} + + } // namespace installd } // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index ad7a5ae6a2..ce99fff27a 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -544,5 +544,35 @@ TEST_F(UtilsTest, MatchExtension_Invalid) { EXPECT_EQ(0, MatchExtension("docx")); } +TEST_F(UtilsTest, TestRollbackPaths) { + EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo")); + EXPECT_EQ("/data/misc_ce/10/rollback/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo")); + + EXPECT_EQ("/data/misc_de/0/rollback/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo")); + EXPECT_EQ("/data/misc_de/10/rollback/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo")); + + EXPECT_EQ("/data/misc_ce/0/rollback", + create_data_misc_ce_rollback_path(nullptr, 0)); + EXPECT_EQ("/data/misc_ce/10/rollback", + create_data_misc_ce_rollback_path(nullptr, 10)); + + EXPECT_EQ("/data/misc_de/0/rollback", + create_data_misc_de_rollback_path(nullptr, 0)); + EXPECT_EQ("/data/misc_de/10/rollback", + create_data_misc_de_rollback_path(nullptr, 10)); + + // These last couple of cases are never exercised in production because we + // only snapshot apps in the primary data partition. Exercise them here for + // the sake of completeness. + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example", + create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example", + create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index bbf14cb5f7..24f5eab132 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -194,6 +194,27 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/user_de/%u", data.c_str(), userid); } + +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name); +} + +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name); +} + /** * Create the path name for media for a certain userid. */ diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 206ad4158a..430f515980 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -61,6 +61,13 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name); +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name); + std::string create_data_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml new file mode 100644 index 0000000000..34644872eb --- /dev/null +++ b/data/etc/android.software.secure_lock_screen.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<permissions> + <feature name="android.software.secure_lock_screen" /> +</permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index d6021c007b..6cbe4ae0fe 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -45,6 +45,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml index 8b5a46187f..915e579ec1 100644 --- a/data/etc/go_handheld_core_hardware.xml +++ b/data/etc/go_handheld_core_hardware.xml @@ -43,6 +43,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 060a3346c8..619d017e69 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -51,6 +51,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index 6db2627e12..52524ca957 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml @@ -51,6 +51,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index e2ab71a853..0f364c1eb0 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml @@ -35,6 +35,7 @@ <!-- basic system services --> <feature name="android.software.home_screen" /> + <feature name="android.software.secure_lock_screen" /> <!-- input management and third-party input method editors --> <feature name="android.software.input_methods" /> diff --git a/include/android/sensor.h b/include/android/sensor.h index 1a1b01064a..e9d5c16d0d 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -495,6 +495,7 @@ struct ASensorEventQueue; * - ASensorEventQueue_hasEvents() * - ASensorEventQueue_getEvents() * - ASensorEventQueue_setEventRate() + * - ASensorEventQueue_requestAdditionalInfoEvents() */ typedef struct ASensorEventQueue ASensorEventQueue; @@ -779,6 +780,29 @@ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); +#if __ANDROID_API__ >= __ANDROID_API_Q__ +/** + * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on + * the given {@link ASensorEventQueue}. + * + * Sensor data events are always delivered to the {@ASensorEventQueue}. + * + * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through + * {@link ASensorEventQueue_getEvents}. The client is responsible for checking + * {@link ASensorEvent#type} to determine the event type prior to handling of + * the event. + * + * The client must be tolerant of any value for + * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future + * and may delivered to the client. + * + * \param queue {@link ASensorEventQueue} to configure + * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events, + * false to stop receiving events + * \return 0 on success or a negative error code on failure + */ +int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable); +#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */ /*****************************************************************************/ diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 13b630ba57..05731871d9 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -29,6 +29,7 @@ #include <sys/cdefs.h> #include <android/hardware_buffer.h> +#include <android/hdr_metadata.h> #include <android/native_window.h> __BEGIN_DECLS @@ -60,10 +61,11 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb __INTRODUCED_IN(29); /** - * Destroys the |surface_control| object. After releasing the ASurfaceControl the caller no longer - * has ownership of the AsurfaceControl. + * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer + * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long + * as their parent remains on display. */ -void ASurfaceControl_destroy(ASurfaceControl* surface_control) __INTRODUCED_IN(29); +void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); struct ASurfaceTransaction; @@ -94,24 +96,94 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_I void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); /** + * An opaque handle returned during a callback that can be used to query general stats and stats for + * surfaces which were either removed or for which buffers were updated after this transaction was + * applied. + */ +typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; + +/** * Since the transactions are applied asynchronously, the * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame * including the updates in a transaction was presented. * * |context| is the optional context provided by the client that is passed into * the callback. - * |present_fence| is the sync fence that signals when the transaction has been presented. - * The recipient of the callback takes ownership of the present_fence and is responsible for closing - * it. * - * It is safe to assume that once the present fence singals, that reads for all buffers, - * submitted in previous transactions, which are not in the surface tree after a transaction is - * applied, are finished and the buffers may be reused. + * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query + * information about the transaction. The handle is only valid during the the callback. * * THREADING * The transaction completed callback can be invoked on any thread. */ -typedef void (*ASurfaceTransaction_OnComplete)(void* context, int32_t present_fence); +typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) + __INTRODUCED_IN(29); + +/** + * Returns the timestamp of when the frame was latched by the framework. Once a frame is + * latched by the framework, it is presented at the following hardware vsync. + */ +int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) + __INTRODUCED_IN(29); + +/** + * Returns a sync fence that signals when the transaction has been presented. + * The recipient of the callback takes ownership of the fence and is responsible for closing + * it. + */ +int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) + __INTRODUCED_IN(29); + +/** + * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the + * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions. + * When the client is done using the array, it must release it by calling + * ASurfaceTransactionStats_releaseASurfaceControls. + * + * |outASurfaceControlsSize| returns the size of the ASurfaceControls array. + */ +void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl*** outASurfaceControls, + size_t* outASurfaceControlsSize) + __INTRODUCED_IN(29); +/** + * Releases the array of ASurfaceControls that were returned by + * ASurfaceTransactionStats_getASurfaceControls. + */ +void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) + __INTRODUCED_IN(29); + +/** + * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered + * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until + * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. + */ +int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl* surface_control) + __INTRODUCED_IN(29); + +/** + * The returns the fence used to signal the release of the PREVIOUS buffer set on + * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the + * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS + * buffer is already released. The recipient of the callback takes ownership of the + * previousReleaseFenceFd and is responsible for closing it. + * + * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a + * transaction which is applied, the framework takes a ref on this buffer. The framework treats the + * addition of a buffer to a particular surface as a unique ref. When a transaction updates or + * removes a buffer from a surface, or removes the surface itself from the tree, this ref is + * guaranteed to be released in the OnComplete callback for this transaction. The + * ASurfaceControlStats provided in the callback for this surface may contain an optional fence + * which must be signaled before the ref is assumed to be released. + * + * The client must ensure that all pending refs on a buffer are released before attempting to reuse + * this buffer, otherwise synchronization errors may occur. + */ +int ASurfaceTransactionStats_getPreviousReleaseFenceFd( + ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl* surface_control) + __INTRODUCED_IN(29); /** * Sets the callback that will be invoked when the updates from this transaction @@ -121,6 +193,16 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* context, int32_t present_fe void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); +/** + * Reparents the |surface_control| from its old parent to the |new_parent| surface control. + * Any children of the* reparented |surface_control| will remain children of the |surface_control|. + * + * The |new_parent| can be null. Surface controls with a null parent do not appear on the display. + */ +void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, ASurfaceControl* new_parent) + __INTRODUCED_IN(29); + /* Parameter for ASurfaceTransaction_setVisibility */ enum { ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0, @@ -148,15 +230,26 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, /** * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the - * fence_fd should be a file descriptor that is signaled when all pending work + * acquire_fence_fd should be a file descriptor that is signaled when all pending work * for the buffer is complete and the buffer can be safely read. * - * The frameworks takes ownership of the |fence_fd| passed and is responsible + * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible * for closing it. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, AHardwareBuffer* buffer, - int fence_fd = -1) __INTRODUCED_IN(29); + int acquire_fence_fd = -1) __INTRODUCED_IN(29); + +/** + * Updates the color for |surface_control|. This will make the background color for the + * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|, + * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha| + * will be the dataspace and alpha set for the background color layer. + */ +void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, float r, float g, float b, + float alpha, ADataSpace dataspace) + __INTRODUCED_IN(29); /** * |source| the sub-rect within the buffer's content to be rendered inside the surface's area @@ -189,8 +282,9 @@ enum { * opaque or visual errors can occur. */ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, int8_t transparency) - __INTRODUCED_IN(29); + ASurfaceControl* surface_control, + int8_t transparency) + __INTRODUCED_IN(29); /** * Updates the region for the content on this surface updated in this @@ -200,6 +294,50 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect rects[], uint32_t count) __INTRODUCED_IN(29); +/** + * Specifies a desiredPresentTime for the transaction. The framework will try to present + * the transaction at or after the time specified. + * + * Transactions will not be presented until all of their acquire fences have signaled even if the + * app requests an earlier present time. + * + * If an earlier transaction has a desired present time of x, and a later transaction has a desired + * present time that is before x, the later transaction will not preempt the earlier transaction. + */ +void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, + int64_t desiredPresentTime) __INTRODUCED_IN(29); + +/** + * Sets the alpha for the buffer. It uses a premultiplied blending. + * + * The |alpha| must be between 0.0 and 1.0. + */ +void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, float alpha) + __INTRODUCED_IN(29); + +/* + * SMPTE ST 2086 "Mastering Display Color Volume" static metadata + * + * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering + * the surface's buffer. + */ +void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + struct AHdrMetadata_smpte2086* metadata) + __INTRODUCED_IN(29); + +/* + * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface. + * + * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering + * the surface's buffer. + */ +void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + struct AHdrMetadata_cta861_3* metadata) + __INTRODUCED_IN(29); + #endif // __ANDROID_API__ >= 29 __END_DECLS diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index a20154f834..ad8287c203 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -25,4 +25,9 @@ cc_library_static { host_supported: true, vendor_available: true, export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + }, } diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp new file mode 100644 index 0000000000..28cb13827d --- /dev/null +++ b/libs/cputimeinstate/Android.bp @@ -0,0 +1,30 @@ +cc_library { + name: "libtimeinstate", + srcs: ["cputimeinstate.cpp"], + shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", + "liblog", + "libnetdutils" + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], +} + +cc_test { + name: "libtimeinstate_test", + srcs: ["testtimeinstate.cpp"], + shared_libs: [ + "libtimeinstate", + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], +} + diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp new file mode 100644 index 0000000000..4cddf94720 --- /dev/null +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libtimeinstate" + +#include "cputimeinstate.h" + +#include <dirent.h> +#include <errno.h> +#include <inttypes.h> + +#include <mutex> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + +#include <android-base/file.h> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <bpf/BpfMap.h> +#include <libbpf.h> +#include <log/log.h> + +#define BPF_FS_PATH "/sys/fs/bpf/" + +using android::base::StringPrintf; +using android::base::unique_fd; + +namespace android { +namespace bpf { + +typedef struct { + uint32_t uid; + uint32_t freq; +} time_key_t; + +typedef struct { + uint64_t ar[100]; +} val_t; + +static std::mutex gInitializedMutex; +static bool gInitialized = false; +static uint32_t gNPolicies = 0; +static std::vector<std::vector<uint32_t>> gPolicyFreqs; +static std::vector<std::vector<uint32_t>> gPolicyCpus; +static std::set<uint32_t> gAllFreqs; +static unique_fd gMapFd; + +static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { + std::string data; + + if (!android::base::ReadFileToString(path, &data)) { + ALOGD("Failed to read file %s", path.c_str()); + return false; + } + + auto strings = android::base::Split(data, " \n"); + for (const auto &s : strings) { + if (s.empty()) continue; + uint32_t n; + if (!android::base::ParseUint(s, &n)) { + ALOGD("Failed to parse file %s", path.c_str()); + return false; + } + out->emplace_back(n); + } + return true; +} + +static int isPolicyFile(const struct dirent *d) { + return android::base::StartsWith(d->d_name, "policy"); +} + +static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) { + uint32_t policyN1, policyN2; + if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 || + sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1) + return 0; + return policyN1 - policyN2; +} + +static bool initGlobals() { + std::lock_guard<std::mutex> guard(gInitializedMutex); + if (gInitialized) return true; + + struct dirent **dirlist; + const char basepath[] = "/sys/devices/system/cpu/cpufreq"; + int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); + if (ret == -1) return false; + gNPolicies = ret; + + std::vector<std::string> policyFileNames; + for (uint32_t i = 0; i < gNPolicies; ++i) { + policyFileNames.emplace_back(dirlist[i]->d_name); + free(dirlist[i]); + } + free(dirlist); + + for (const auto &policy : policyFileNames) { + std::vector<uint32_t> freqs; + for (const auto &name : {"available", "boost"}) { + std::string path = + StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); + if (!readNumbersFromFile(path, &freqs)) return false; + } + std::sort(freqs.begin(), freqs.end()); + gPolicyFreqs.emplace_back(freqs); + + for (auto freq : freqs) gAllFreqs.insert(freq); + + std::vector<uint32_t> cpus; + std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); + if (!readNumbersFromFile(path, &cpus)) return false; + gPolicyCpus.emplace_back(cpus); + } + + gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; + if (gMapFd < 0) return false; + + gInitialized = true; + return true; +} + +static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { + std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", + eventType.c_str(), eventName.c_str()); + int prog_fd = bpf_obj_get(path.c_str()); + if (prog_fd < 0) { + ALOGD("bpf_obj_get() failed for program %s", path.c_str()); + return false; + } + if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) { + ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(), + eventName.c_str()); + return false; + } + return true; +} + +// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. +// Returns true on success, false otherwise. +// Tracking is active only once a live process has successfully called this function; if the calling +// process dies then it must be called again to resume tracking. +// This function should *not* be called while tracking is already active; doing so is unnecessary +// and can lead to accounting errors. +bool startTrackingUidCpuFreqTimes() { + return attachTracepointProgram("sched", "sched_switch") && + attachTracepointProgram("power", "cpu_frequency"); +} + +// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. +// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors +// using the format: +// [[t0_0, t0_1, ...], +// [t1_0, t1_1, ...], ...] +// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. +bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) { + if (!gInitialized && !initGlobals()) return false; + time_key_t key = {.uid = uid, .freq = 0}; + + freqTimes->clear(); + freqTimes->resize(gNPolicies); + std::vector<uint32_t> idxs(gNPolicies, 0); + + val_t value; + for (uint32_t freq : gAllFreqs) { + key.freq = freq; + int ret = findMapEntry(gMapFd, &key, &value); + if (ret) { + if (errno == ENOENT) + memset(&value.ar, 0, sizeof(value.ar)); + else + return false; + } + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; + uint64_t time = 0; + for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; + idxs[i] += 1; + (*freqTimes)[i].emplace_back(time); + } + } + + return true; +} + +// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. +// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to +// vectors of vectors using the format: +// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], +// uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } +// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. +bool getUidsCpuFreqTimes( + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) { + if (!gInitialized && !initGlobals()) return false; + + int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); + if (fd < 0) return false; + BpfMap<time_key_t, val_t> m(fd); + + std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; + for (uint32_t i = 0; i < gNPolicies; ++i) { + std::unordered_map<uint32_t, uint32_t> freqIdxs; + for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; + policyFreqIdxs.emplace_back(freqIdxs); + } + + auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, + const BpfMap<time_key_t, val_t> &) { + if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { + std::vector<std::vector<uint64_t>> v; + for (uint32_t i = 0; i < gNPolicies; ++i) { + std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0); + v.emplace_back(v2); + } + (*freqTimeMap)[key.uid] = v; + } + + for (size_t policy = 0; policy < gNPolicies; ++policy) { + for (const auto &cpu : gPolicyCpus[policy]) { + uint32_t cpuTime = val.ar[cpu]; + if (cpuTime == 0) continue; + auto freqIdx = policyFreqIdxs[policy][key.freq]; + (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime; + } + } + return android::netdutils::status::ok; + }; + return isOk(m.iterateWithValue(fn)); +} + +// Clear all time in state data for a given uid. Returns false on error, true otherwise. +bool clearUidCpuFreqTimes(uint32_t uid) { + if (!gInitialized && !initGlobals()) return false; + time_key_t key = {.uid = uid, .freq = 0}; + + std::vector<uint32_t> idxs(gNPolicies, 0); + for (auto freq : gAllFreqs) { + key.freq = freq; + if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; + } + return true; +} + +} // namespace bpf +} // namespace android diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h new file mode 100644 index 0000000000..0205452766 --- /dev/null +++ b/libs/cputimeinstate/cputimeinstate.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <unordered_map> +#include <vector> + +namespace android { +namespace bpf { + +bool startTrackingUidCpuFreqTimes(); +bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes); +bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap); +bool clearUidCpuFreqTimes(unsigned int uid); + +} // namespace bpf +} // namespace android diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp new file mode 100644 index 0000000000..9837865dfb --- /dev/null +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -0,0 +1,58 @@ + +#include <unordered_map> +#include <vector> + +#include <gtest/gtest.h> + +#include <cputimeinstate.h> + +namespace android { +namespace bpf { + +using std::vector; + +TEST(TimeInStateTest, SingleUid) { + vector<vector<uint64_t>> times; + ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); + EXPECT_FALSE(times.empty()); +} + +TEST(TimeInStateTest, AllUid) { + vector<size_t> sizes; + std::unordered_map<uint32_t, vector<vector<uint64_t>>> map; + ASSERT_TRUE(getUidsCpuFreqTimes(&map)); + + ASSERT_FALSE(map.empty()); + + auto firstEntry = map.begin()->second; + for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); + + for (const auto &vec : map) { + ASSERT_EQ(vec.second.size(), sizes.size()); + for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + } +} + +TEST(TimeInStateTest, RemoveUid) { + vector<vector<uint64_t>> times, times2; + ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); + ASSERT_FALSE(times.empty()); + + uint64_t sum = 0; + for (size_t i = 0; i < times.size(); ++i) { + for (auto x : times[i]) sum += x; + } + ASSERT_GT(sum, (uint64_t)0); + + ASSERT_TRUE(clearUidCpuFreqTimes(0)); + + ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); + ASSERT_EQ(times2.size(), times.size()); + for (size_t i = 0; i < times.size(); ++i) { + ASSERT_EQ(times2[i].size(), times[i].size()); + for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); + } +} + +} // namespace bpf +} // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 40b55fa28b..ab929731f3 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -97,6 +97,9 @@ status_t layer_state_t::write(Parcel& output) const output.writeStrongBinder(cachedBuffer.token); output.writeInt32(cachedBuffer.bufferId); + output.writeFloat(colorAlpha); + output.writeUint32(static_cast<uint32_t>(colorDataspace)); + return NO_ERROR; } @@ -170,6 +173,9 @@ status_t layer_state_t::read(const Parcel& input) cachedBuffer.token = input.readStrongBinder(); cachedBuffer.bufferId = input.readInt32(); + colorAlpha = input.readFloat(); + colorDataspace = static_cast<ui::Dataspace>(input.readUint32()); + return NO_ERROR; } @@ -382,6 +388,14 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eCachedBufferChanged; cachedBuffer = other.cachedBuffer; } + if (other.what & eColorAlphaChanged) { + what |= eColorAlphaChanged; + colorAlpha = other.colorAlpha; + } + if (other.what & eColorDataspaceChanged) { + what |= eColorDataspaceChanged; + colorDataspace = other.colorDataspace; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a82054bd19..6c1c52e1bd 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -102,6 +102,27 @@ void ComposerService::composerServiceDied() mDeathObserver = nullptr; } +class DefaultComposerClient: public Singleton<DefaultComposerClient> { + Mutex mLock; + sp<SurfaceComposerClient> mClient; + friend class Singleton<ComposerService>; +public: + static sp<SurfaceComposerClient> getComposerClient() { + DefaultComposerClient& dc = DefaultComposerClient::getInstance(); + Mutex::Autolock _l(dc.mLock); + if (dc.mClient == nullptr) { + dc.mClient = new SurfaceComposerClient; + } + return dc.mClient; + } +}; +ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient); + + +sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { + return DefaultComposerClient::getComposerClient(); +} + // --------------------------------------------------------------------------- // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs @@ -132,26 +153,76 @@ void TransactionCompletedListener::startListeningLocked() { mListening = true; } -CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) { +CallbackId TransactionCompletedListener::addCallbackFunction( + const TransactionCompletedCallback& callbackFunction, + const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& + surfaceControls) { std::lock_guard<std::mutex> lock(mMutex); startListeningLocked(); CallbackId callbackId = getNextIdLocked(); - mCallbacks.emplace(callbackId, callback); + mCallbacks[callbackId].callbackFunction = callbackFunction; + + auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls; + + for (const auto& surfaceControl : surfaceControls) { + callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; + } + return callbackId; } +void TransactionCompletedListener::addSurfaceControlToCallbacks( + const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId>& callbackIds) { + std::lock_guard<std::mutex> lock(mMutex); + + for (auto callbackId : callbackIds) { + mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, + std::forward_as_tuple( + surfaceControl->getHandle()), + std::forward_as_tuple(surfaceControl)); + } +} + void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::lock_guard lock(mMutex); + /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered + * callbackIds, except for when Transactions are merged together. This probably cannot be + * solved before this point because the Transactions could be merged together and applied in a + * different process. + * + * Fortunately, we get all the callbacks for this listener for the same frame together at the + * same time. This means if any Transactions were merged together, we will get their callbacks + * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the + * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl> + * that could possibly exist for the callbacks. + */ + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) { for (auto callbackId : callbackIds) { - const auto& callback = mCallbacks[callbackId]; - if (!callback) { + auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId]; + surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end()); + } + } + + for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) { + for (auto callbackId : callbackIds) { + auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId]; + if (!callbackFunction) { ALOGE("cannot call null callback function, skipping"); continue; } - callback(transactionStats); + std::vector<SurfaceControlStats> surfaceControlStats; + for (const auto& surfaceStats : transactionStats.surfaceStats) { + surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl], + surfaceStats.acquireTime, + surfaceStats.previousReleaseFence); + } + + callbackFunction(transactionStats.latchTime, transactionStats.presentFence, + surfaceControlStats); mCallbacks.erase(callbackId); } } @@ -329,7 +400,11 @@ layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<Surfac void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( const sp<SurfaceControl>& sc) { - mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc); + auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; + callbackInfo.surfaceControls.insert(sc); + + TransactionCompletedListener::getInstance() + ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( @@ -589,6 +664,36 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorAlpha( + const sp<SurfaceControl>& sc, float alpha) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eColorAlphaChanged; + s->colorAlpha = alpha; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorDataspace( + const sp<SurfaceControl>& sc, ui::Dataspace dataspace) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eColorDataspaceChanged; + s->colorDataspace = dataspace; + + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform( const sp<SurfaceControl>& sc, uint32_t transform) { layer_state_t* s = getLayerState(sc); @@ -770,9 +875,12 @@ SurfaceComposerClient::Transaction::addTransactionCompletedCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext) { auto listener = TransactionCompletedListener::getInstance(); - auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1); + auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + const auto& surfaceControls = + mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls; - CallbackId callbackId = listener->addCallback(callbackWithContext); + CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls); mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace( callbackId); @@ -872,6 +980,54 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry( + const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) { + setCrop_legacy(sc, source); + + int x = dst.left; + int y = dst.top; + float xScale = dst.getWidth() / static_cast<float>(source.getWidth()); + float yScale = dst.getHeight() / static_cast<float>(source.getHeight()); + float matrix[4] = {1, 0, 0, 1}; + + switch (transform) { + case NATIVE_WINDOW_TRANSFORM_FLIP_H: + matrix[0] = -xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = yScale; + x += source.getWidth(); + break; + case NATIVE_WINDOW_TRANSFORM_FLIP_V: + matrix[0] = xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = -yScale; + y += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_90: + matrix[0] = 0; matrix[1] = -yScale; + matrix[2] = xScale; matrix[3] = 0; + x += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_180: + matrix[0] = -xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = -yScale; + x += source.getWidth(); + y += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_270: + matrix[0] = 0; matrix[1] = yScale; + matrix[2] = -xScale; matrix[3] = 0; + y += source.getWidth(); + break; + default: + matrix[0] = xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = yScale; + break; + } + setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]); + setPosition(sc, x, y); + + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index af314208de..c1750cfb54 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -85,6 +85,8 @@ struct layer_state_t { eCornerRadiusChanged = 0x80000000, eFrameChanged = 0x1'00000000, eCachedBufferChanged = 0x2'00000000, + eColorAlphaChanged = 0x3'00000000, + eColorDataspaceChanged = 0x4'00000000, }; layer_state_t() @@ -110,7 +112,9 @@ struct layer_state_t { dataspace(ui::Dataspace::UNKNOWN), surfaceDamageRegion(), api(-1), - colorTransform(mat4()) { + colorTransform(mat4()), + colorAlpha(0), + colorDataspace(ui::Dataspace::UNKNOWN) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -180,6 +184,9 @@ struct layer_state_t { #endif cached_buffer_t cachedBuffer; + + float colorAlpha; + ui::Dataspace colorDataspace; }; struct ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index db315c2ea5..24b656b543 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -53,35 +53,24 @@ class Region; // --------------------------------------------------------------------------- -using TransactionCompletedCallbackTakesContext = - std::function<void(void* /*context*/, const TransactionStats&)>; -using TransactionCompletedCallback = std::function<void(const TransactionStats&)>; - -class TransactionCompletedListener : public BnTransactionCompletedListener { - TransactionCompletedListener(); - - CallbackId getNextIdLocked() REQUIRES(mMutex); - - std::mutex mMutex; - - bool mListening GUARDED_BY(mMutex) = false; - - CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; - - std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex); - -public: - static sp<TransactionCompletedListener> getInstance(); - static sp<ITransactionCompletedListener> getIInstance(); - - void startListeningLocked() REQUIRES(mMutex); - - CallbackId addCallback(const TransactionCompletedCallback& callback); - - // Overrides BnTransactionCompletedListener's onTransactionCompleted - void onTransactionCompleted(ListenerStats stats) override; +struct SurfaceControlStats { + SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time, + const sp<Fence>& prevReleaseFence) + : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {} + + sp<SurfaceControl> surfaceControl; + nsecs_t acquireTime = -1; + sp<Fence> previousReleaseFence; }; +using TransactionCompletedCallbackTakesContext = + std::function<void(void* /*context*/, nsecs_t /*latchTime*/, + const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& /*stats*/)>; +using TransactionCompletedCallback = + std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& /*stats*/)>; + // --------------------------------------------------------------------------- class SurfaceComposerClient : public RefBase @@ -167,6 +156,8 @@ public: // ------------------------------------------------------------------------ // surface creation / destruction + static sp<SurfaceComposerClient> getDefault(); + //! Create a surface sp<SurfaceControl> createSurface( const String8& name,// name of the surface @@ -335,6 +326,12 @@ public: Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); + // Sets the alpha of the background color layer if it exists. + Transaction& setColorAlpha(const sp<SurfaceControl>& sc, float alpha); + + // Sets the dataspace of the background color layer if it exists. + Transaction& setColorDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); + Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform); Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc, bool transformToDisplayInverse); @@ -387,6 +384,9 @@ public: Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation); + Transaction& setGeometry(const sp<SurfaceControl>& sc, + const Rect& source, const Rect& dst, int transform); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -465,6 +465,50 @@ public: // --------------------------------------------------------------------------- +class TransactionCompletedListener : public BnTransactionCompletedListener { + TransactionCompletedListener(); + + CallbackId getNextIdLocked() REQUIRES(mMutex); + + std::mutex mMutex; + + bool mListening GUARDED_BY(mMutex) = false; + + CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& iBinder) const { + return std::hash<IBinder*>{}(iBinder.get()); + } + }; + + struct CallbackTranslation { + TransactionCompletedCallback callbackFunction; + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; + }; + + std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex); + +public: + static sp<TransactionCompletedListener> getInstance(); + static sp<ITransactionCompletedListener> getIInstance(); + + void startListeningLocked() REQUIRES(mMutex); + + CallbackId addCallbackFunction( + const TransactionCompletedCallback& callbackFunction, + const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& + surfaceControls); + + void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId>& callbackIds); + + // Overrides BnTransactionCompletedListener's onTransactionCompleted + void onTransactionCompleted(ListenerStats stats) override; +}; + +// --------------------------------------------------------------------------- + }; // namespace android #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h new file mode 100644 index 0000000000..7e1313b993 --- /dev/null +++ b/libs/nativewindow/include/android/hdr_metadata.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hdr_metadata.h + */ + +#ifndef ANDROID_HDR_METADATA_H +#define ANDROID_HDR_METADATA_H + +#include <inttypes.h> + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * These structures are used to define the display's capabilities for HDR content. + * They can be used to better tone map content to user's display. + */ + +/** + * Color is defined in CIE XYZ coordinates. + */ +struct AColor_xy { + float x; + float y; +}; + +/** + * SMPTE ST 2086 "Mastering Display Color Volume" static metadata + */ +struct AHdrMetadata_smpte2086 { + struct AColor_xy displayPrimaryRed; + struct AColor_xy displayPrimaryGreen; + struct AColor_xy displayPrimaryBlue; + struct AColor_xy whitePoint; + float maxLuminance; + float minLuminance; +}; + +/** + * CTA 861.3 "HDR Static Metadata Extension" static metadata + */ +struct AHdrMetadata_cta861_3 { + float maxContentLightLevel; + float maxFrameAverageLightLevel; +}; + +__END_DECLS + +#endif // ANDROID_HDR_METADATA_H diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index f57bf9c9d1..c5a994278b 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -608,7 +608,17 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, - sp<Fence> bufferFence) { + sp<Fence> bufferFence, bool readCache, + bool persistCache) { + if (readCache) { + auto cachedImage = mImageCache.find(buffer->getId()); + + if (cachedImage != mImageCache.end()) { + bindExternalTextureImage(texName, *cachedImage->second); + return NO_ERROR; + } + } + std::unique_ptr<Image> newImage = createImage(); bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), @@ -644,9 +654,35 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<Graphi } } + // We don't always want to persist to the cache, e.g. on older devices we + // might bind for synchronization purpoeses, but that might leak if we never + // call drawLayers again, so it's just better to recreate the image again + // if needed when we draw. + if (persistCache) { + mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); + } + return NO_ERROR; } +void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) { + // destroy old image references that we're not going to draw with. + std::unordered_set<uint64_t> bufIds; + for (auto layer : layers) { + if (layer.source.buffer.buffer != nullptr) { + bufIds.emplace(layer.source.buffer.buffer->getId()); + } + } + + for (auto it = mImageCache.begin(); it != mImageCache.end();) { + if (bufIds.count(it->first) == 0) { + it = mImageCache.erase(it); + } else { + it++; + } + } +} + FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { // Translate win by the rounded corners rect coordinates, to have all values in // layer coordinate space. @@ -748,6 +784,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, return fbo.getStatus(); } + evictImages(layers); + setViewportAndProjection(display.physicalDisplay, display.clip); setOutputDataSpace(display.outputDataspace); @@ -781,8 +819,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE; bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, - layer.source.buffer.fence); + layer.source.buffer.fence, readCache, /*persistCache=*/true); usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index b596242216..e094860f6e 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -30,6 +30,7 @@ #include <GLES2/gl2.h> #include <renderengine/RenderEngine.h> #include <renderengine/private/Description.h> +#include <unordered_map> #define EGL_NO_CONFIG ((EGLConfig)0) @@ -133,7 +134,10 @@ private: // Defines the viewport, and sets the projection matrix to the projection // defined by the clip. void setViewportAndProjection(Rect viewport, Rect clip); - status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence); + status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence, + bool readCache, bool persistCache); + // Evicts stale images from the buffer cache. + void evictImages(const std::vector<LayerSettings>& layers); // Computes the cropping window for the layer and sets up cropping // coordinates for the mesh. FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh); @@ -179,6 +183,9 @@ private: // supports sRGB, DisplayP3 color spaces. const bool mUseColorManagement = false; + // Cache of GL images that we'll store per GraphicBuffer ID + std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache; + class FlushTracer { public: FlushTracer(GLESRenderEngine* engine); diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 4d53205421..56ac71416a 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -32,6 +32,16 @@ namespace renderengine { // Metadata describing the input buffer to render from. struct Buffer { + // Hint for whether to use the Image cache or not. + // If NO_CACHE is specified, then upload the contents of the GraphicBuffer + // to the GPU, without checking against any implementation defined cache. + // If USE_CACHE is specified, then check against an implementation defined + // cache first. If there is an Image cached for the given GraphicBuffer id, + // then use that instead of the provided buffer contents. If there is no + // cached image or the RenderEngine implementation does not support caching, + // then use the GraphicBuffer contents. + enum class CachingHint { NO_CACHE, USE_CACHE }; + // Buffer containing the image that we will render. // If buffer == nullptr, then the rest of the fields in this struct will be // ignored. @@ -40,6 +50,9 @@ struct Buffer { // Fence that will fire when the buffer is ready to be bound. sp<Fence> fence = nullptr; + // Caching hint to use when uploading buffer contents. + CachingHint cacheHint = CachingHint::NO_CACHE; + // Texture identifier to bind the external texture to. // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. uint32_t textureName = 0; diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 46ba7c6250..4438d454c0 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -29,6 +29,7 @@ #include <sensor/ISensorEventConnection.h> #include <android/sensor.h> +#include <hardware/sensors-base.h> using std::min; @@ -188,6 +189,52 @@ void SensorEventQueue::sendAck(const ASensorEvent* events, int count) { return; } +ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const { + // Check if this Sensor Event Queue is registered to receive each type of event. If it is not, + // then do not copy the event into the final buffer. Minimize the number of copy operations by + // finding consecutive sequences of events that the Sensor Event Queue should receive and only + // copying the events once an unregistered event type is reached. + bool intervalStartLocSet = false; + size_t intervalStartLoc = 0; + size_t eventsInInterval = 0; + ssize_t eventsCopied = 0; + + for (size_t i = 0; i < count; i++) { + bool includeEvent = + (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo); + + if (includeEvent) { + // Do not copy events yet since there may be more consecutive events that should be + // copied together. Track the start location and number of events in the current + // sequence. + if (!intervalStartLocSet) { + intervalStartLoc = i; + intervalStartLocSet = true; + eventsInInterval = 0; + } + eventsInInterval++; + } + + // Shift the events from the already processed interval once an event that should not be + // included is reached or if this is the final event to be processed. + if (!includeEvent || (i + 1 == count)) { + // Only shift the events if the interval did not start with the first event. If the + // interval started with the first event, the events are already in their correct + // location. + if (intervalStartLoc > 0) { + memmove(&events[eventsCopied], &events[intervalStartLoc], + eventsInInterval * sizeof(ASensorEvent)); + } + eventsCopied += eventsInInterval; + + // Reset the interval information + eventsInInterval = 0; + intervalStartLocSet = false; + } + } + return eventsCopied; +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h index 8176578526..8c3fde0fa1 100644 --- a/libs/sensor/include/sensor/SensorEventQueue.h +++ b/libs/sensor/include/sensor/SensorEventQueue.h @@ -34,6 +34,7 @@ struct ASensorEvent; // Concrete types for the NDK struct ASensorEventQueue { ALooper* looper; + bool requestAdditionalInfo; }; // ---------------------------------------------------------------------------- @@ -92,6 +93,13 @@ public: void sendAck(const ASensorEvent* events, int count); status_t injectSensorEvent(const ASensorEvent& event); + + // Filters the given sensor events in place and returns the new number of events. + // + // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is + // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed. + ssize_t filterEvents(ASensorEvent* events, size_t count) const; + private: sp<Looper> getLooper() const; sp<ISensorEventConnection> mSensorEventConnection; diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 9fd84bcb5a..c9a7668563 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -21,6 +21,7 @@ cc_test { srcs: [ "Sensor_test.cpp", + "SensorEventQueue_test.cpp", ], shared_libs: [ diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp new file mode 100644 index 0000000000..1eb5883ff3 --- /dev/null +++ b/libs/sensor/tests/SensorEventQueue_test.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <gtest/gtest.h> +#include <utils/Errors.h> + +#include <android/sensor.h> +#include <hardware/sensors-base.h> +#include <sensor/SensorManager.h> +#include <sensor/SensorEventQueue.h> + +namespace android { + +class SensorEventQueueTest : public ::testing::Test { +protected: + typedef std::vector<int32_t> Events; + + SensorEventQueueTest() {}; + + virtual void SetUp() override { + SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest")); + mQueue = manager.createEventQueue(); + } + + void configureAdditionalInfo(bool enable) { + mQueue->requestAdditionalInfo = enable; + } + + Events filterEvents(const Events &types) const { + // Convert the events into SensorEvent array + ASensorEvent* events = new ASensorEvent[types.size()]; + for (size_t i = 0; i < types.size(); i++) { + events[i].type = types[i]; + } + + // Filter the events + ssize_t filteredCount = mQueue->filterEvents(events, types.size()); + + // Copy the result into an output vector + Events result; + for (size_t i = 0; i < filteredCount; i++) { + result.push_back(events[i].type); + } + delete[] events; + + return result; + } + + Events getExpectedEvents(const Events &events) const { + Events output; + for (size_t i = 0; i != events.size(); i++) { + // Copy events if the event queue is configured to receive them + if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) { + output.push_back(events[i]); + } + } + return output; + } + + void runFilterTest(const Events& events) { + Events filtered = filterEvents(events); + Events expected = getExpectedEvents(events); + EXPECT_EQ(expected.size(), filtered.size()); + EXPECT_EQ(expected, filtered); + } + +private: + sp<SensorEventQueue> mQueue; +}; + +TEST_F(SensorEventQueueTest, FilterZeroEvents) { + configureAdditionalInfo(false /* enable */); + runFilterTest({}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) { + configureAdditionalInfo(true /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) { + configureAdditionalInfo(false /* enable */); + const Events events = {SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO}; + runFilterTest(events); + + // Update setting to request Additional Info + configureAdditionalInfo(true /* enable */); + runFilterTest(events); + + // Update setting to stop requesting Additional Info + configureAdditionalInfo(true /* enable */); + runFilterTest(events); +} + +} // namespace android diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index ea7321e24c..2c4b5f32b1 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -227,7 +227,14 @@ void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* ou } status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, - int acquireFence, void** outData) const { + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const { + if (outBytesPerPixel) { + *outBytesPerPixel = -1; + } + if (outBytesPerStride) { + *outBytesPerStride = -1; + } auto buffer = const_cast<native_handle_t*>(bufferHandle); IMapper::Rect accessRegion = sGralloc2Rect(bounds); diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 128200e33e..acb6b01188 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -192,7 +192,8 @@ void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* ou } status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, - int acquireFence, void** outData) const { + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const { auto buffer = const_cast<native_handle_t*>(bufferHandle); IMapper::Rect accessRegion = sGralloc3Rect(bounds); @@ -208,12 +209,19 @@ status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, cons Error error; auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, - [&](const auto& tmpError, const auto& tmpData) { + [&](const auto& tmpError, const auto& tmpData, + const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) { error = tmpError; if (error != Error::NONE) { return; } *outData = tmpData; + if (outBytesPerPixel) { + *outBytesPerPixel = tmpBytesPerPixel; + } + if (outBytesPerStride) { + *outBytesPerStride = tmpBytesPerStride; + } }); // we own acquireFence even on errors diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index f408fcbe84..da24cf1c06 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -252,7 +252,10 @@ status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr) width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr); + int32_t bytesPerPixel, bytesPerStride; + + status_t res = + getBufferMapper().lock(handle, inUsage, rect, vaddr, &bytesPerPixel, &bytesPerStride); return res; } @@ -306,8 +309,10 @@ status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, - inConsumerUsage, rect, vaddr, fenceFd); + + int32_t bytesPerPixel, bytesPerStride; + status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, + vaddr, fenceFd, &bytesPerPixel, &bytesPerStride); return res; } diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index b04932938c..9e36377c3d 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -102,10 +102,10 @@ status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) return NO_ERROR; } -status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, - const Rect& bounds, void** vaddr) -{ - return lockAsync(handle, usage, bounds, vaddr, -1); +status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride); } status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, @@ -125,21 +125,23 @@ status_t GraphicBufferMapper::unlock(buffer_handle_t handle) return error; } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd) -{ - return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd); +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int fenceFd, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel, + outBytesPerStride); } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, - uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, - void** vaddr, int fenceFd) -{ +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, + uint64_t consumerUsage, const Rect& bounds, void** vaddr, + int fenceFd, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { ATRACE_CALL(); const uint64_t usage = static_cast<uint64_t>( android_convertGralloc1To0Usage(producerUsage, consumerUsage)); - return mMapper->lock(handle, usage, bounds, fenceFd, vaddr); + return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel, + outBytesPerStride); } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 92bf043064..a484bce4df 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -56,7 +56,8 @@ public: // The ownership of acquireFence is always transferred to the callee, even // on errors. virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, - int acquireFence, void** outData) const = 0; + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const = 0; // The ownership of acquireFence is always transferred to the callee, even // on errors. diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index e03cb4357c..b23d8f77e4 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -53,7 +53,8 @@ public: uint32_t* outNumInts) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, - int acquireFence, void** outData) const override; + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const override; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 510ce4ac23..b0cbcc1300 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -52,7 +52,8 @@ public: uint32_t* outNumInts) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, - int acquireFence, void** outData) const override; + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const override; diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 156bd7abcd..072926ff44 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -56,20 +56,21 @@ public: void getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts); - status_t lock(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr); + status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr); status_t unlock(buffer_handle_t handle); - status_t lockAsync(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd); + status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, + int fenceFd, int32_t* outBytesPerPixel = nullptr, + int32_t* outBytesPerStride = nullptr); - status_t lockAsync(buffer_handle_t handle, - uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, - void** vaddr, int fenceFd); + status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, + const Rect& bounds, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 843eb379d9..e3a237ef73 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -76,7 +76,8 @@ cc_library_shared { "libutils", "libui", "libhardware_legacy", - "libutils" + "libstatslog", + "libutils", ], header_libs: [ diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 2b31f6e0e6..64070e3fa3 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -57,6 +57,7 @@ #include <android-base/stringprintf.h> #include <input/Keyboard.h> #include <input/VirtualKeyMap.h> +#include <statslog.h> #define INDENT " " #define INDENT2 " " @@ -71,18 +72,21 @@ namespace android { // --- Constants --- // Maximum number of slots supported when using the slot-based Multitouch Protocol B. -static const size_t MAX_SLOTS = 32; +static constexpr size_t MAX_SLOTS = 32; // Maximum amount of latency to add to touch events while waiting for data from an // external stylus. -static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); // Maximum amount of time to wait on touch data before pushing out new pressure data. -static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); // Artificial latency on synthetic events created from stylus data without corresponding touch // data. -static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); +static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); + +// How often to report input event statistics +static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); // --- Static Functions --- @@ -4287,12 +4291,25 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } +void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { + nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t latency = now - evdevTime; + mStatistics.addValue(nanoseconds_to_microseconds(latency)); + nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; + if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, + mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev()); + mStatistics.reset(now); + } +} + void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + reportEventForStatistics(rawEvent->when); sync(rawEvent->when); } } diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 35f3c232c7..aaffce294e 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -567,6 +567,69 @@ struct CookedPointerData { } }; +/** + * Basic statistics information. + * Keep track of min, max, average, and standard deviation of the received samples. + * Used to report latency information about input events. + */ +struct LatencyStatistics { + float min; + float max; + // Sum of all samples + float sum; + // Sum of squares of all samples + float sum2; + // The number of samples + size_t count; + // The last time statistics were reported. + nsecs_t lastReportTime; + + LatencyStatistics() { + reset(systemTime(SYSTEM_TIME_MONOTONIC)); + } + + inline void addValue(float x) { + if (x < min) { + min = x; + } + if (x > max) { + max = x; + } + sum += x; + sum2 += x * x; + count++; + } + + // Get the average value. Should not be called if no samples have been added. + inline float mean() { + if (count == 0) { + return 0; + } + return sum / count; + } + + // Get the standard deviation. Should not be called if no samples have been added. + inline float stdev() { + if (count == 0) { + return 0; + } + float average = mean(); + return sqrt(sum2 / count - average * average); + } + + /** + * Reset internal state. The variable 'when' is the time when the data collection started. + * Call this to start a new data collection window. + */ + inline void reset(nsecs_t when) { + max = 0; + min = std::numeric_limits<float>::max(); + sum = 0; + sum2 = 0; + count = 0; + lastReportTime = when; + } +}; /* Keeps track of the state of single-touch protocol. */ class SingleTouchMotionAccumulator { @@ -1511,6 +1574,9 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; + // Latency statistics for touch events + struct LatencyStatistics mStatistics; + std::optional<DisplayViewport> findViewport(); void resetExternalStylus(); @@ -1580,6 +1646,8 @@ private: static void assignPointerIds(const RawState* last, RawState* current); + void reportEventForStatistics(nsecs_t evdevTime); + const char* modeToString(DeviceMode deviceMode); }; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index aa9bc15f21..ef77590761 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -109,6 +109,7 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.cornerRadius = 0.0f; mCurrentState.api = -1; mCurrentState.hasColorTransform = false; + mCurrentState.colorDataspace = ui::Dataspace::UNKNOWN; // drawing state & current state are identical mDrawingState = mCurrentState; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 95a8630148..0f83464357 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -202,6 +202,8 @@ public: mat4 colorTransform; bool hasColorTransform; + ui::Dataspace colorDataspace; // The dataspace of the background color layer + // The deque of callback handles for this frame. The back of the deque contains the most // recent callback handle. std::deque<sp<CallbackHandle>> callbackHandles; @@ -308,6 +310,8 @@ public: const std::vector<sp<CallbackHandle>>& /*handles*/) { return false; }; + virtual bool setColorAlpha(float /*alpha*/) { return false; }; + virtual bool setColorDataspace(ui::Dataspace /*dataspace*/) { return false; }; ui::Dataspace getDataSpace() const { return mCurrentDataSpace; } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h new file mode 100644 index 0000000000..ce1637afb3 --- /dev/null +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -0,0 +1,109 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <algorithm> +#include <numeric> + +#include "Scheduler/SchedulerUtils.h" +#include "android-base/stringprintf.h" + +namespace android { +namespace scheduler { + +/** + * This class is used to encapsulate configuration for refresh rates. It holds infomation + * about available refresh rates on the device, and the mapping between the numbers and human + * readable names. + */ +class RefreshRateConfigs { +public: + // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest + // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance + // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs. + enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; + + struct RefreshRate { + // Type of the refresh rate. + RefreshRateType type; + // This config ID corresponds to the position of the config in the vector that is stored + // on the device. + int configId; + // Human readable name of the refresh rate. + std::string name; + }; + + // TODO(b/122916473): Get this information from configs prepared by vendors, instead of + // baking them in. + explicit RefreshRateConfigs( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { + // This is the rate that HWC encapsulates right now when the screen is off. + RefreshRate rate; + rate.type = RefreshRateType::POWER_SAVING; + rate.configId = SCREEN_OFF_CONFIG_ID; + rate.name = "ScreenOff"; + mRefreshRates.push_back(rate); + + if (configs.size() < 1) { + return; + } + + std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod; + for (int i = 0; i < configs.size(); ++i) { + configIdToVsyncPeriod.push_back(std::make_pair(i, configs.at(i)->getVsyncPeriod())); + } + std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(), + [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) { + return a.second > b.second; + }); + + nsecs_t vsyncPeriod = configIdToVsyncPeriod.at(0).second; + if (vsyncPeriod != 0) { + const float fps = std::chrono::nanoseconds(1).count() / vsyncPeriod; + rate.type = RefreshRateType::DEFAULT; + rate.configId = configIdToVsyncPeriod.at(0).first; + rate.name = base::StringPrintf("%2.ffps", fps); + mRefreshRates.push_back(rate); + } + if (configs.size() < 2) { + return; + } + + vsyncPeriod = configIdToVsyncPeriod.at(1).second; + if (vsyncPeriod != 0) { + const float fps = std::chrono::nanoseconds(1).count() / vsyncPeriod; + rate.type = RefreshRateType::PERFORMANCE; + rate.configId = configIdToVsyncPeriod.at(1).first; + rate.name = base::StringPrintf("%2.ffps", fps); + mRefreshRates.push_back(rate); + } + + for (auto refreshRate : mRefreshRates) { + ALOGV("type: %d, id: %d, name: %s", refreshRate.type, refreshRate.configId, + refreshRate.name.c_str()); + } + } + ~RefreshRateConfigs() = default; + + const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; } + +private: + std::vector<RefreshRate> mRefreshRates; +}; + +} // namespace scheduler +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h new file mode 100644 index 0000000000..cd81e4da1e --- /dev/null +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -0,0 +1,146 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <numeric> + +#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/SchedulerUtils.h" + +#include "android-base/stringprintf.h" +#include "utils/Timers.h" + +namespace android { +namespace scheduler { + +/** + * Class to encapsulate statistics about refresh rates that the display is using. When the power + * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in + * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the + * hood (eg. the device is in DOZE, or screen off mode). + */ +class RefreshRateStats { + static constexpr int64_t MS_PER_S = 1000; + static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S; + static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN; + static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; + +public: + explicit RefreshRateStats( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) + : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)), + mPreviousRecordedTime(systemTime()) {} + ~RefreshRateStats() = default; + + // Sets power mode. We only collect the information when the power mode is not + // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based + // on config mode. + void setPowerMode(int mode) { + if (mCurrentPowerMode == mode) { + return; + } + // If power mode is normal, the time is going to be recorded under config modes. + if (mode == HWC_POWER_MODE_NORMAL) { + mCurrentPowerMode = mode; + return; + } + flushTime(); + mCurrentPowerMode = mode; + } + + // Sets config mode. If the mode has changed, it records how much time was spent in the previous + // mode. + void setConfigMode(int mode) { + if (mCurrentConfigMode == mode) { + return; + } + flushTime(); + mCurrentConfigMode = mode; + } + + // Returns a map between human readable refresh rate and number of seconds the device spent in + // that mode. + std::unordered_map<std::string, int64_t> getTotalTimes() { + // If the power mode is on, then we are probably switching between the config modes. If + // it's not then the screen is probably off. Make sure to flush times before printing + // them. + flushTime(); + + std::unordered_map<std::string, int64_t> totalTime; + for (auto config : mRefreshRateConfigs->getRefreshRates()) { + if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) { + totalTime[config.name] = mConfigModesTotalTime.at(config.configId); + } + } + return totalTime; + } + + // Traverses through the map of config modes and returns how long they've been running in easy + // to read format. + std::string doDump() { + std::ostringstream stream; + stream << "+ Refresh rate: running time in seconds\n"; + for (auto stats : getTotalTimes()) { + stream << stats.first.c_str() << ": " << getDateFormatFromMs(stats.second) << "\n"; + } + return stream.str(); + } + +private: + void flushTime() { + // Normal power mode is counted under different config modes. + if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) { + flushTimeForMode(mCurrentConfigMode); + } else { + flushTimeForMode(SCREEN_OFF_CONFIG_ID); + } + } + + // Calculates the time that passed in ms between the last time we recorded time and the time + // this method was called. + void flushTimeForMode(int mode) { + nsecs_t currentTime = systemTime(); + int64_t timeElapsedMs = ns2ms(currentTime - mPreviousRecordedTime); + mPreviousRecordedTime = currentTime; + + mConfigModesTotalTime[mode] += timeElapsedMs; + } + + // Formats the time in milliseconds into easy to read format. + static std::string getDateFormatFromMs(int64_t timeMs) { + auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY); + auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR); + auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN); + auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S); + return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64 + ".%03" PRId64, + days, hours, mins, sec, secRemainderMs); + } + + // Keeps information about refresh rate configs that device has. + std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + + int64_t mCurrentConfigMode = 0; + int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF; + + std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime; + + nsecs_t mPreviousRecordedTime; +}; + +} // namespace scheduler +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 0d587dd598..f4191e69af 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -352,4 +352,10 @@ void Scheduler::expiredTimerCallback() { } } +std::string Scheduler::doDump() { + std::ostringstream stream; + stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl; + return stream.str(); +} + } // namespace android diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index ba18d217f4..435fd14c5e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -118,6 +118,8 @@ public: void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback); // Callback that gets invoked once the idle timer is reset. void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback); + // Returns relevant information about Scheduler for dumpsys purposes. + std::string doDump(); protected: virtual std::unique_ptr<EventThread> makeEventThread( diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h index 17c57db375..edd23de983 100644 --- a/services/surfaceflinger/Scheduler/SchedulerUtils.h +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h @@ -26,6 +26,11 @@ namespace scheduler { // about layers. static constexpr size_t ARRAY_SIZE = 30; +// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently +// the config is not visible to SF, and is completely maintained by HWC. However, we would +// still like to keep track of time when the device is in this config. +static constexpr int SCREEN_OFF_CONFIG_ID = -1; + // Calculates the statistical mean (average) in the data structure (array, vector). The // function does not modify the contents of the array. template <typename T> diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3531296830..0fce71a0d7 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -738,6 +738,8 @@ void SurfaceFlinger::init() { if (mUseScheduler) { mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); }); mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); }); + mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>( + getHwComposer().getConfigs(*display->getId())); } ALOGV("Done initializing"); @@ -988,6 +990,9 @@ void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, in // Don't update config if we are already running in the desired mode. return; } + if (mUseScheduler) { + mRefreshRateStats->setConfigMode(mode); + } const auto displayId = display->getId(); LOG_ALWAYS_FATAL_IF(!displayId); @@ -3829,6 +3834,12 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (layer->setColor(s.color)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eColorAlphaChanged) { + if (layer->setColorAlpha(s.colorAlpha)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eColorDataspaceChanged) { + if (layer->setColorDataspace(s.colorDataspace)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eColorTransformChanged) { if (layer->setColorTransform(s.colorTransform)) { flags |= eTraversalNeeded; @@ -3962,8 +3973,12 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; } if (what & layer_state_t::eInputInfoChanged) { - layer->setInputInfo(s.inputInfo); - flags |= eTraversalNeeded; + if (callingThreadHasUnscopedSurfaceFlingerAccess()) { + layer->setInputInfo(s.inputInfo); + flags |= eTraversalNeeded; + } else { + ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER"); + } } std::vector<sp<CallbackHandle>> callbackHandles; if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) { @@ -4324,6 +4339,10 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int if (display->isPrimary()) { mTimeStats->setPowerMode(mode); + if (mUseScheduler) { + // Update refresh rate stats. + mRefreshRateStats->setPowerMode(mode); + } } ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str()); @@ -4850,6 +4869,15 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append(mVrFlinger->Dump()); result.append("\n"); } + + /** + * Scheduler dump state. + */ + if (mUseScheduler) { + result.append("\nScheduler state:\n"); + result.append(mScheduler->doDump() + "\n"); + result.append(mRefreshRateStats->doDump() + "\n"); + } } const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index d401a157c6..68a602cf61 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -59,6 +59,7 @@ #include "Scheduler/DispSync.h" #include "Scheduler/EventThread.h" #include "Scheduler/MessageQueue.h" +#include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VSyncModulator.h" #include "SurfaceFlingerFactory.h" @@ -1069,11 +1070,16 @@ private: SurfaceFlingerBE mBE; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; + /* ------------------------------------------------------------------------ + * Scheduler + */ bool mUseScheduler = false; std::unique_ptr<Scheduler> mScheduler; sp<Scheduler::ConnectionHandle> mAppConnectionHandle; sp<Scheduler::ConnectionHandle> mSfConnectionHandle; + std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; + /* ------------------------------------------------------------------------ */ sp<IInputFlinger> mInputFlinger; InputWindowCommands mInputWindowCommands; diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index aea602bba4..5bec502e1d 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -2,6 +2,7 @@ service surfaceflinger /system/bin/surfaceflinger class core animation user system group graphics drmrpc readproc + updatable onrestart restart zygote writepid /dev/stune/foreground/tasks socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index 216532a814..b95c0ec135 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -2664,6 +2664,17 @@ TEST_F(LayerTransactionTest, SetColorTransformOnChildAndParent) { } } +struct CallbackData { + CallbackData() = default; + CallbackData(nsecs_t time, const sp<Fence>& fence, + const std::vector<SurfaceControlStats>& stats) + : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} + + nsecs_t latchTime; + sp<Fence> presentFence; + std::vector<SurfaceControlStats> surfaceControlStats; +}; + class ExpectedResult { public: enum Transaction { @@ -2691,8 +2702,7 @@ public: ExpectedResult::Buffer bufferResult = ACQUIRED, ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { mTransactionResult = transactionResult; - mExpectedSurfaceResults.emplace(std::piecewise_construct, - std::forward_as_tuple(layer->getHandle()), + mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), std::forward_as_tuple(bufferResult, previousBufferResult)); } @@ -2709,8 +2719,8 @@ public: mExpectedPresentTime = expectedPresentTime; } - void verifyTransactionStats(const TransactionStats& transactionStats) const { - const auto& [latchTime, presentFence, surfaceStats] = transactionStats; + void verifyCallbackData(const CallbackData& callbackData) const { + const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { ASSERT_GE(latchTime, 0) << "bad latch time"; ASSERT_NE(presentFence, nullptr); @@ -2727,14 +2737,16 @@ public: ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; } - ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size()) + ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) << "wrong number of surfaces"; - for (const auto& stats : surfaceStats) { + for (const auto& stats : surfaceControlStats) { + ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; + const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) << "unexpected surface control"; - expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime); + expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); } } @@ -2745,8 +2757,9 @@ private: ExpectedResult::PreviousBuffer previousBufferResult) : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} - void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const { - const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceStats; + void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, + nsecs_t latchTime) const { + const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats; ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) << "bad acquire time"; @@ -2766,39 +2779,40 @@ private: ExpectedResult::PreviousBuffer mPreviousBufferResult; }; - struct IBinderHash { - std::size_t operator()(const sp<IBinder>& strongPointer) const { - return std::hash<IBinder*>{}(strongPointer.get()); + struct SCHash { + std::size_t operator()(const sp<SurfaceControl>& sc) const { + return std::hash<IBinder*>{}(sc->getHandle().get()); } }; ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; nsecs_t mExpectedPresentTime = -1; - std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults; + std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; }; class CallbackHelper { public: - static void function(void* callbackContext, const TransactionStats& transactionStats) { + static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { if (!callbackContext) { ALOGE("failed to get callback context"); } CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext); std::lock_guard lock(helper->mMutex); - helper->mTransactionStatsQueue.push(transactionStats); + helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); helper->mConditionVariable.notify_all(); } - void getTransactionStats(TransactionStats* outStats) { + void getCallbackData(CallbackData* outData) { std::unique_lock lock(mMutex); - if (mTransactionStatsQueue.empty()) { + if (mCallbackDataQueue.empty()) { ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), std::cv_status::timeout) << "did not receive callback"; } - *outStats = std::move(mTransactionStatsQueue.front()); - mTransactionStatsQueue.pop(); + *outData = std::move(mCallbackDataQueue.front()); + mCallbackDataQueue.pop(); } void verifyFinalState() { @@ -2806,15 +2820,15 @@ public: std::this_thread::sleep_for(500ms); std::lock_guard lock(mMutex); - EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received"; - mTransactionStatsQueue = {}; + EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; + mCallbackDataQueue = {}; } void* getContext() { return static_cast<void*>(this); } std::mutex mMutex; std::condition_variable mConditionVariable; - std::queue<TransactionStats> mTransactionStatsQueue; + std::queue<CallbackData> mCallbackDataQueue; }; class LayerCallbackTest : public LayerTransactionTest { @@ -2843,9 +2857,9 @@ public: static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, bool finalState = false) { - TransactionStats transactionStats; - ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats)); - EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats)); + CallbackData callbackData; + ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); + EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData)); if (finalState) { ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp index ba7d7f9810..4e24a64691 100644 --- a/services/vr/bufferhubd/Android.bp +++ b/services/vr/bufferhubd/Android.bp @@ -14,7 +14,6 @@ sharedLibraries = [ "libbase", - "libbufferhubservice", "libcutils", "libgtest_prod", "libgui", diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h deleted file mode 100644 index 997aeda917..0000000000 --- a/services/vr/bufferhubd/include/private/dvr/buffer_node.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ -#define ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ -// TODO(b/118891412) Remove this file - -#include <bufferhub/BufferNode.h> - -namespace android { -namespace dvr { - -typedef android::frameworks::bufferhub::V1_0::implementation::BufferNode - BufferNode; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ |