Merge "SF: Adding Mock Display and tests for RefreshRateStats."
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 9aa1075..e938b10 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -99,8 +99,9 @@
"utils.cpp",
],
static_libs: [
+ "libincidentcompanion",
"libdumpsys",
- "libserviceutils"
+ "libserviceutils",
],
}
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index eb0d50d..b13478c 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -31,6 +31,13 @@
namespace {
+struct DumpstateInfo {
+ public:
+ Dumpstate* ds = nullptr;
+ int32_t calling_uid = -1;
+ std::string calling_package;
+};
+
static binder::Status exception(uint32_t code, const std::string& msg) {
MYLOGE("%s (%d) ", msg.c_str(), code);
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
@@ -42,14 +49,15 @@
}
static void* callAndNotify(void* data) {
- Dumpstate& ds = *static_cast<Dumpstate*>(data);
- ds.Run();
+ DumpstateInfo& ds_info = *static_cast<DumpstateInfo*>(data);
+ ds_info.ds->Run(ds_info.calling_uid, ds_info.calling_package);
MYLOGD("Finished Run()\n");
return nullptr;
}
class DumpstateToken : public BnDumpstateToken {};
-}
+
+} // namespace
DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
}
@@ -98,8 +106,8 @@
}
// TODO(b/111441001): Hook up to consent service & copy final br only if user approves.
-binder::Status DumpstateService::startBugreport(int32_t /* calling_uid */,
- const std::string& /* calling_package */,
+binder::Status DumpstateService::startBugreport(int32_t calling_uid,
+ const std::string& calling_package,
const android::base::unique_fd& bugreport_fd,
const android::base::unique_fd& screenshot_fd,
int bugreport_mode,
@@ -132,6 +140,11 @@
ds_.listener_ = listener;
}
+ DumpstateInfo ds_info;
+ ds_info.ds = &ds_;
+ ds_info.calling_uid = calling_uid;
+ ds_info.calling_package = calling_package;
+
pthread_t thread;
status_t err = pthread_create(&thread, nullptr, callAndNotify, &ds_);
if (err != 0) {
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 8396acd..907a67c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -38,6 +38,9 @@
/* User denied consent to share the bugreport with the specified app */
const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3;
+ /* The request to get user consent timed out */
+ const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+
/**
* Called on an error condition with one of the error codes listed above.
*/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7958865..41c42d7 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -51,6 +51,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/os/IIncidentCompanion.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <dumpsys.h>
@@ -83,15 +84,19 @@
using android::UNKNOWN_ERROR;
using android::Vector;
using android::base::StringPrintf;
+using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
using android::os::dumpstate::DumpstateSectionReporter;
using android::os::dumpstate::GetPidByName;
using android::os::dumpstate::PropertiesHelper;
+typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult;
+
/* read before root is shed */
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = nullptr;
+static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
// TODO: variables and functions below should be part of dumpstate object
@@ -165,6 +170,13 @@
return false;
}
+static bool UnlinkAndLogOnError(const std::string& file) {
+ if (unlink(file.c_str()) != -1) {
+ MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
+ return false;
+ }
+ return true;
+}
} // namespace
} // namespace os
@@ -657,6 +669,32 @@
return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}
+Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportApproved() {
+ std::lock_guard<std::mutex> lock(lock_);
+ result_ = APPROVED;
+ MYLOGD("User approved consent to share bugreport\n");
+ return android::binder::Status::ok();
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportDenied() {
+ std::lock_guard<std::mutex> lock(lock_);
+ result_ = DENIED;
+ MYLOGW("User denied consent to share bugreport\n");
+ return android::binder::Status::ok();
+}
+
+UserConsentResult Dumpstate::ConsentCallback::getResult() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return result_;
+}
+
+uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
+ return Nanotime() - start_time_;
+}
+
void Dumpstate::PrintHeader() const {
std::string build, fingerprint, radio, bootloader, network;
char date[80];
@@ -1892,14 +1930,6 @@
ds.path_ = new_path;
}
}
- // The zip file lives in an internal directory. Copy it over to output.
- if (ds.options_->bugreport_fd.get() != -1) {
- 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) {
@@ -2192,8 +2222,8 @@
options_ = std::move(options);
}
-Dumpstate::RunStatus Dumpstate::Run() {
- Dumpstate::RunStatus status = RunInternal();
+Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
+ Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
if (listener_ != nullptr) {
switch (status) {
case Dumpstate::RunStatus::OK:
@@ -2202,10 +2232,16 @@
case Dumpstate::RunStatus::HELP:
break;
case Dumpstate::RunStatus::INVALID_INPUT:
- listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
break;
case Dumpstate::RunStatus::ERROR:
- listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+ break;
+ case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);
+ break;
+ case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
+ listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
break;
}
}
@@ -2233,7 +2269,8 @@
* Bugreports are first generated in a local directory and later copied to the caller's fd if
* supplied.
*/
-Dumpstate::RunStatus Dumpstate::RunInternal() {
+Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
+ const std::string& calling_package) {
LogDumpOptions(*options_);
if (!options_->ValidateOptions()) {
MYLOGE("Invalid options specified\n");
@@ -2271,6 +2308,12 @@
return RunStatus::OK;
}
+ if (options_->bugreport_fd.get() != -1) {
+ // If the output needs to be copied over to the caller's fd, get user consent.
+ android::String16 package(calling_package.c_str());
+ CheckUserConsent(calling_uid, package);
+ }
+
// Redirect output if needed
bool is_redirecting = options_->OutputToFile();
@@ -2422,11 +2465,24 @@
TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
}
- /* rename or zip the (now complete) .tmp file to its final location */
+ // Rename, and/or zip the (now complete) .tmp file within the internal directory.
if (options_->OutputToFile()) {
FinalizeFile();
}
+ // Share the final file with the caller if the user has consented.
+ Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
+ if (options_->bugreport_fd.get() != -1) {
+ status = CopyBugreportIfUserConsented();
+ if (status != Dumpstate::RunStatus::OK &&
+ status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+ // Do an early return if there were errors. We make an exception for consent
+ // timing out because it's possible the user got distracted. In this case the
+ // bugreport is not shared but made available for manual retrieval.
+ return status;
+ }
+ }
+
/* vibrate a few but shortly times to let user know it's finished */
if (options_->do_vibrate) {
for (int i = 0; i < 3; i++) {
@@ -2458,7 +2514,73 @@
tombstone_data_.clear();
anr_data_.clear();
- return RunStatus::OK;
+ return (consent_callback_ != nullptr &&
+ consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
+ ? USER_CONSENT_TIMED_OUT
+ : RunStatus::OK;
+}
+
+void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
+ consent_callback_ = new ConsentCallback();
+ const String16 incidentcompanion("incidentcompanion");
+ sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+ if (ics != nullptr) {
+ MYLOGD("Checking user consent via incidentcompanion service\n");
+ android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+ calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
+ consent_callback_.get());
+ } else {
+ MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
+ }
+}
+
+void Dumpstate::CleanupFiles() {
+ android::os::UnlinkAndLogOnError(tmp_path_);
+ android::os::UnlinkAndLogOnError(screenshot_path_);
+ android::os::UnlinkAndLogOnError(path_);
+}
+
+Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
+ MYLOGD("User denied consent; deleting files and returning\n");
+ CleanupFiles();
+ return USER_CONSENT_DENIED;
+}
+
+Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() {
+ // If the caller has asked to copy the bugreport over to their directory, we need explicit
+ // user consent.
+ UserConsentResult consent_result = consent_callback_->getResult();
+ if (consent_result == UserConsentResult::UNAVAILABLE) {
+ // User has not responded yet.
+ uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
+ if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
+ uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+ MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
+ sleep(delay_seconds);
+ }
+ consent_result = consent_callback_->getResult();
+ }
+ if (consent_result == UserConsentResult::DENIED) {
+ // User has explicitly denied sharing with the app. To be safe delete the
+ // internal bugreport & tmp files.
+ return HandleUserConsentDenied();
+ }
+ if (consent_result == UserConsentResult::APPROVED) {
+ 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));
+ }
+ return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
+ } else if (consent_result == UserConsentResult::UNAVAILABLE) {
+ // consent_result is still UNAVAILABLE. The user has likely not responded yet.
+ // Since we do not have user consent to share the bugreport it does not get
+ // copied over to the calling app but remains in the internal directory from
+ // where the user can manually pull it.
+ return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
+ }
+ // Unknown result; must be a programming error.
+ MYLOGE("Unknown user consent result:%d\n", consent_result);
+ return Dumpstate::RunStatus::ERROR;
}
/* Main entry point for dumpstate binary. */
@@ -2467,7 +2589,14 @@
Dumpstate::RunStatus status = options->Initialize(argc, argv);
if (status == Dumpstate::RunStatus::OK) {
ds.SetOptions(std::move(options));
- status = ds.Run();
+ // When directly running dumpstate binary, the output is not expected to be written
+ // to any external file descriptor.
+ assert(ds.options_->bugreport_fd.get() == -1);
+
+ // calling_uid and calling_package are for user consent to share the bugreport with
+ // an app; they are irrelvant here because bugreport is only written to a local
+ // directory, and not shared.
+ status = ds.Run(-1 /* calling_uid */, "" /* calling_package */);
}
switch (status) {
@@ -2481,9 +2610,10 @@
ShowUsage();
exit(1);
case Dumpstate::RunStatus::ERROR:
- exit(2);
- default:
- fprintf(stderr, "Unknown status: %d\n", status);
+ FALLTHROUGH_INTENDED;
+ case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+ FALLTHROUGH_INTENDED;
+ case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
exit(2);
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3dfe4e9..4766d82 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
#include <android-base/macros.h>
#include <android-base/unique_fd.h>
+#include <android/os/BnIncidentAuthListener.h>
#include <android/os/IDumpstate.h>
#include <android/os/IDumpstateListener.h>
#include <utils/StrongPointer.h>
@@ -192,7 +193,7 @@
friend class DumpstateTest;
public:
- enum RunStatus { OK, HELP, INVALID_INPUT, ERROR };
+ enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
// The mode under which the bugreport should be run. Each mode encapsulates a few options.
enum BugreportMode {
@@ -319,7 +320,7 @@
struct DumpOptions;
/* Main entry point for running a complete bugreport. */
- RunStatus Run();
+ RunStatus Run(int32_t calling_uid, const std::string& calling_package);
/* Sets runtime options. */
void SetOptions(std::unique_ptr<DumpOptions> options);
@@ -447,12 +448,47 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A callback to IncidentCompanion service, which checks user consent for sharing the
+ // bugreport with the calling app. If the user has not responded yet to the dialog it will
+ // be neither confirmed nor denied.
+ class ConsentCallback : public android::os::BnIncidentAuthListener {
+ public:
+ ConsentCallback();
+ android::binder::Status onReportApproved() override;
+ android::binder::Status onReportDenied() override;
+
+ enum ConsentResult { APPROVED, DENIED, UNAVAILABLE };
+
+ ConsentResult getResult();
+
+ // Returns the time since creating this listener
+ uint64_t getElapsedTimeMs() const;
+
+ private:
+ ConsentResult result_;
+ uint64_t start_time_;
+ std::mutex lock_;
+ };
+
private:
- RunStatus RunInternal();
+ RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
+
+ void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+
+ // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
+ // but leaves the log file alone.
+ void CleanupFiles();
+
+ RunStatus HandleUserConsentDenied();
+
+ // Copies bugreport artifacts over to the caller's directories provided there is user consent.
+ RunStatus CopyBugreportIfUserConsented();
// Used by GetInstance() only.
explicit Dumpstate(const std::string& version = VERSION_CURRENT);
+ android::sp<ConsentCallback> consent_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
diff --git a/data/etc/android.hardware.telephony.ims.xml b/data/etc/android.hardware.telephony.ims.xml
new file mode 100644
index 0000000..eeb7b00
--- /dev/null
+++ b/data/etc/android.hardware.telephony.ims.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<!-- Feature for devices that support IMS via ImsService APIs. -->
+<permissions>
+ <feature name="android.hardware.telephony.ims" />
+</permissions>
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 9dc7431..660e3c3 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -284,16 +284,7 @@
} else {
// The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily
// load ANGLE and call the updatable opt-in/out logic:
-
- // Check if ANGLE is enabled. Workaround for several bugs:
- // b/119305693 b/119322355 b/119305887
- // Something is not working correctly in the feature library
- char prop[PROPERTY_VALUE_MAX];
- property_get("debug.angle.enable", prop, "0");
- void* featureSo = nullptr;
- if (atoi(prop)) {
- featureSo = loadLibrary("feature_support");
- }
+ void* featureSo = loadLibrary("feature_support");
if (featureSo) {
ALOGV("loaded ANGLE's opt-in/out logic from namespace");
mUseAngle = checkAngleRules(featureSo);
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
new file mode 100644
index 0000000..45eab00
--- /dev/null
+++ b/libs/incidentcompanion/Android.bp
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+filegroup {
+ name: "incidentcompanion_aidl",
+ srcs: [
+ "binder/android/os/IIncidentAuthListener.aidl",
+ "binder/android/os/IIncidentCompanion.aidl",
+ ],
+ path: "binder",
+}
+
+cc_library_static {
+ name: "libincidentcompanion",
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ aidl: {
+ local_include_dirs: ["binder"],
+ export_aidl_headers: true,
+ },
+ srcs: [
+ ":incidentcompanion_aidl",
+ ],
+ export_include_dirs: ["binder"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+}
+
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
new file mode 100644
index 0000000..5484be8
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Callback for IIncidentCompanion.
+ *
+ * @hide
+ */
+oneway interface IIncidentAuthListener {
+ /**
+ * The user approved the incident or bug report to be sent.
+ */
+ void onReportApproved();
+
+ /**
+ * The user did not approve the incident or bug report to be sent.
+ */
+ void onReportDenied();
+}
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
new file mode 100644
index 0000000..6bf98d2
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -0,0 +1,72 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+import android.os.IIncidentAuthListener;
+
+/**
+ * Helper service for incidentd and dumpstated to provide user feedback
+ * and authorization for bug and inicdent reports to be taken.
+ *
+ * @hide
+ */
+interface IIncidentCompanion {
+ /**
+ * Request an authorization for an incident or bug report.
+ * // TODO(b/111441001): Add the permission
+ * <p>
+ * This function requires the ___ permission.
+ *
+ * @param callingUid The original application that requested the report. This function
+ * returns via the callback whether the application should be trusted. It is up
+ * to the caller to actually implement the restriction to take or not take
+ * the incident or bug report.
+ * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
+ * a dialog will be shown as a notification.
+ * @param callback Interface to receive results. The results may not come back for
+ * a long (user's choice) time, or ever (if they never respond to the notification).
+ * Authorization requests are not persisted across reboot. It is up to the calling
+ * service to request another authorization after reboot if they still would like
+ * to send their report.
+ */
+ oneway void authorizeReport(int callingUid, String callingPackage,
+ int flags, IIncidentAuthListener callback);
+
+ /**
+ * Cancel an authorization.
+ */
+ oneway void cancelAuthorization(IIncidentAuthListener callback);
+
+ /**
+ * Return the list of pending approvals.
+ */
+ List<String> getPendingReports();
+
+ /**
+ * The user has authorized the report to be shared.
+ *
+ * @param uri the report.
+ */
+ void approveReport(String uri);
+
+ /**
+ * The user has denied the report from being shared.
+ *
+ * @param uri the report.
+ */
+ void denyReport(String uri);
+}
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9d92435..57181c2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -215,10 +215,6 @@
return true;
}
-static bool isMainDisplay(int32_t displayId) {
- return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
static void dumpRegion(std::string& dump, const Region& region) {
if (region.isEmpty()) {
dump += "<empty>";
@@ -2722,8 +2718,7 @@
}
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
- // TODO: support sending secondary display events to input filter
- return mInputFilterEnabled && isMainDisplay(args->displayId);
+ return mInputFilterEnabled;
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3edec40..ed177fb 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -48,9 +48,51 @@
public:
FakeInputDispatcherPolicy() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasNotCalled() {
+ ASSERT_FALSE(mInputEventFiltered)
+ << "Expected filterInputEvent() to not have been called.";
}
private:
+ bool mInputEventFiltered;
+ nsecs_t mTime;
+ int32_t mAction;
+ int32_t mDisplayId;
+
virtual void notifyConfigurationChanged(nsecs_t) {
}
@@ -70,7 +112,26 @@
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent*, uint32_t) {
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
+ mTime = keyEvent->getEventTime();
+ mAction = keyEvent->getAction();
+ mDisplayId = keyEvent->getDisplayId();
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
+ mTime = motionEvent->getEventTime();
+ mAction = motionEvent->getAction();
+ mDisplayId = motionEvent->getDisplayId();
+ break;
+ }
+ }
+
+ mInputEventFiltered = true;
return true;
}
@@ -99,6 +160,13 @@
virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
return false;
}
+
+ void reset() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
};
@@ -404,16 +472,6 @@
void setFocus() {
mFocused = true;
}
-
- void assertNoEvents() {
- uint32_t consumeSeq;
- InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_NE(OK, status)
- << mName.c_str()
- << ": should not have received any events, so consume(..) should not return OK.";
- }
protected:
virtual bool handled() {
return true;
@@ -469,6 +527,40 @@
INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
+static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid key event.
+ NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+
+ return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
+ POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+ AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
+ pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
+ /* videoFrames */ {});
+
+ return args;
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -740,4 +832,76 @@
monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
}
+class InputFilterTest : public InputDispatcherTest {
+protected:
+ static constexpr int32_t SECOND_DISPLAY_ID = 1;
+
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ NotifyMotionArgs motionArgs;
+
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+
+ void testNotifyKey(bool expectToBeFiltered) {
+ NotifyKeyArgs keyArgs;
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+ mDispatcher->notifyKey(&keyArgs);
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
+ mDispatcher->notifyKey(&keyArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+};
+
+// Test InputFilter for MotionEvent
+TEST_F(InputFilterTest, MotionEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if touch events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Test touch on both primary and second display, and check if both events are filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Test touch on both primary and second display, and check if both events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+}
+
+// Test InputFilter for KeyEvent
+TEST_F(InputFilterTest, KeyEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if key event aren't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Send a key event, and check if it is filtered.
+ testNotifyKey(/*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Send a key event, and check if it isn't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2d00f0c..69b5728 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -52,11 +52,9 @@
// Sets the bounds to use
virtual void setBounds(const ui::Size&) = 0;
- // Sets the layer stack filter for this output. If singleLayerStack is true,
- // this output displays just the single layer stack specified by
- // singleLayerStackId. Otherwise all layer stacks will be visible on this
- // output.
- virtual void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) = 0;
+ // Sets the layer stack filtering settings for this output. See
+ // belongsInOutput for full details.
+ virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
// Sets the color transform matrix to use
virtual void setColorTransform(const mat4&) = 0;
@@ -96,8 +94,14 @@
// logical (aka layer stack) space.
virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
- // Tests whether a given layerStackId belongs in this output
- virtual bool belongsInOutput(uint32_t layerStackId) const = 0;
+ // Tests whether a given layerStackId belongs in this output.
+ // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
+ // unless the layer should display on the primary output only and this is not the primary output
+
+ // A layer belongs to the output if its layerStackId matches. Additionally
+ // if the layer should only show in the internal (primary) display only and
+ // this output allows that.
+ virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
protected:
~Output() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 521e1d7..180c40b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,7 +38,7 @@
void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
void setBounds(const ui::Size&) override;
- void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) override;
+ void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const mat4&) override;
void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
@@ -58,7 +58,7 @@
OutputCompositionState& editState() override;
Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
- bool belongsInOutput(uint32_t) const override;
+ bool belongsInOutput(uint32_t, bool) const override;
// Testing
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 96a0e0f..024ed45 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -34,12 +34,11 @@
// If false, this output is not considered secure
bool isSecure{false};
- // If true, only layer stacks with a matching layerStack value will be
- // displayed. If false, all layer stacks will be displayed.
- bool singleLayerStack{true};
+ // If true, this output displays layers that are internal-only
+ bool layerStackInternal{false};
// The layer stack to display on this display
- uint32_t singleLayerStackId{~0u};
+ uint32_t layerStackId{~0u};
// The physical space screen bounds
Rect bounds;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 37f7007..445f0bb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -35,7 +35,7 @@
MOCK_METHOD6(setProjection,
void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
MOCK_METHOD1(setBounds, void(const ui::Size&));
- MOCK_METHOD2(setLayerStackFilter, void(bool, uint32_t));
+ MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const mat4&));
MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
@@ -54,7 +54,7 @@
MOCK_METHOD0(editState, OutputCompositionState&());
MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
- MOCK_CONST_METHOD1(belongsInOutput, bool(uint32_t));
+ MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dbf77b0..7fb67bd 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -75,9 +75,9 @@
dirtyEntireOutput();
}
-void Output::setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) {
- mState.singleLayerStack = singleLayerStack;
- mState.singleLayerStackId = singleLayerStackId;
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+ mState.layerStackId = layerStackId;
+ mState.layerStackInternal = isInternal;
dirtyEntireOutput();
}
@@ -175,8 +175,10 @@
return dirty;
}
-bool Output::belongsInOutput(uint32_t layerStackId) const {
- return !mState.singleLayerStack || (layerStackId == mState.singleLayerStackId);
+bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+ // The layerStackId's must match, and also the layer must not be internal
+ // only when not on an internal output.
+ return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
}
void Output::dirtyEntireOutput() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 7d8765f..78807ff 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -22,11 +22,9 @@
void OutputCompositionState::dump(std::string& out) const {
dumpVal(out, "isEnabled", isEnabled);
dumpVal(out, "isSecure", isSecure);
- if (singleLayerStack) {
- out.append("layerStack=<any>");
- } else {
- dumpVal(out, "layerStack", singleLayerStackId);
- }
+
+ dumpVal(out, "layerStack", layerStackId);
+ dumpVal(out, "layerStackInternal", layerStackInternal);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index f060cff..fe12825 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -153,10 +153,10 @@
mOutput.editState().bounds = displaySize;
const uint32_t layerStack = 123u;
- mOutput.setLayerStackFilter(true, layerStack);
+ mOutput.setLayerStackFilter(layerStack, true);
- EXPECT_TRUE(mOutput.getState().singleLayerStack);
- EXPECT_EQ(layerStack, mOutput.getState().singleLayerStackId);
+ EXPECT_TRUE(mOutput.getState().layerStackInternal);
+ EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
}
@@ -260,5 +260,32 @@
}
}
+/* ------------------------------------------------------------------------
+ * Output::belongsInOutput()
+ */
+
+TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
+ const uint32_t layerStack1 = 123u;
+ const uint32_t layerStack2 = 456u;
+
+ // If the output accepts layerStack1 and internal-only layers....
+ mOutput.setLayerStackFilter(layerStack1, true);
+
+ // Any layer with layerStack1 belongs to it, internal-only or not.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+
+ // If the output accepts layerStack21 but not internal-only layers...
+ mOutput.setLayerStackFilter(layerStack1, false);
+
+ // Only non-internal layers with layerStack1 belong to it.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 54111de..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -166,7 +166,7 @@
// ----------------------------------------------------------------------------
void DisplayDevice::setLayerStack(uint32_t stack) {
- mCompositionDisplay->setLayerStackFilter(!isPrimary(), stack);
+ mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
}
// ----------------------------------------------------------------------------
@@ -330,7 +330,7 @@
}
uint32_t DisplayDevice::getLayerStack() const {
- return mCompositionDisplay->getState().singleLayerStackId;
+ return mCompositionDisplay->getState().layerStackId;
}
const ui::Transform& DisplayDevice::getTransform() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0f83464..685f419 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -213,6 +213,7 @@
virtual ~Layer();
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+ bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
// ------------------------------------------------------------------------
// Geometry setting functions.
@@ -332,6 +333,9 @@
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
+ // Deprecated, please use compositionengine::Output::belongsInOutput()
+ // instead.
+ // TODO(lpique): Move the remaining callers (screencap) to the new function.
bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7b25adb..281f6b7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -19,8 +19,11 @@
#include <pthread.h>
#include <sched.h>
#include <sys/types.h>
+
#include <chrono>
#include <cstdint>
+#include <optional>
+#include <type_traits>
#include <android-base/stringprintf.h>
@@ -35,18 +38,66 @@
#include "EventThread.h"
using namespace std::chrono_literals;
-using android::base::StringAppendF;
-
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
+using base::StringAppendF;
+using base::StringPrintf;
+
+namespace {
+
+auto vsyncPeriod(VSyncRequest request) {
+ return static_cast<std::underlying_type_t<VSyncRequest>>(request);
+}
+
+std::string toString(VSyncRequest request) {
+ switch (request) {
+ case VSyncRequest::None:
+ return "VSyncRequest::None";
+ case VSyncRequest::Single:
+ return "VSyncRequest::Single";
+ default:
+ return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
+ }
+}
+
+std::string toString(const EventThreadConnection& connection) {
+ return StringPrintf("Connection{%p, %s}", &connection,
+ toString(connection.vsyncRequest).c_str());
+}
+
+std::string toString(const DisplayEventReceiver::Event& event) {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ return StringPrintf("Hotplug{displayId=%u, %s}", event.header.id,
+ event.hotplug.connected ? "connected" : "disconnected");
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ return StringPrintf("VSync{displayId=%u, count=%u}", event.header.id,
+ event.vsync.count);
+ default:
+ return "Event{}";
+ }
+}
+
+DisplayEventReceiver::Event makeHotplug(uint32_t displayId, nsecs_t timestamp, bool connected) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+ event.hotplug.connected = connected;
+ return event;
+}
+
+DisplayEventReceiver::Event makeVSync(uint32_t displayId, nsecs_t timestamp, uint32_t count) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+ event.vsync.count = count;
+ return event;
+}
+
+} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
ResyncCallback resyncCallback)
: resyncCallback(std::move(resyncCallback)),
- count(-1),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -65,8 +116,8 @@
return NO_ERROR;
}
-status_t EventThreadConnection::setVsyncRate(uint32_t count) {
- mEventThread->setVsyncRate(count, this);
+status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
+ mEventThread->setVsyncRate(rate, this);
return NO_ERROR;
}
@@ -107,18 +158,16 @@
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
: mVSyncSource(src),
mVSyncSourceUnique(std::move(uniqueSrc)),
- mInterceptVSyncsCallback(interceptVSyncsCallback) {
+ mInterceptVSyncsCallback(interceptVSyncsCallback),
+ mThreadName(threadName) {
if (src == nullptr) {
mVSyncSource = mVSyncSourceUnique.get();
}
- for (auto& event : mVSyncEvent) {
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- event.header.id = 0;
- event.header.timestamp = 0;
- event.vsync.count = 0;
- }
- mThread = std::thread(&EventThread::threadMain, this);
+ mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+ threadMain(lock);
+ });
pthread_setname_np(mThread.native_handle(), threadName);
@@ -178,14 +227,17 @@
}
}
-void EventThread::setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) {
- if (int32_t(count) >= 0) { // server must protect against bad params
- std::lock_guard<std::mutex> lock(mMutex);
- const int32_t new_count = (count == 0) ? -1 : count;
- if (connection->count != new_count) {
- connection->count = new_count;
- mCondition.notify_all();
- }
+void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) {
+ if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
+ if (connection->vsyncRequest != request) {
+ connection->vsyncRequest = request;
+ mCondition.notify_all();
}
}
@@ -201,164 +253,91 @@
std::lock_guard<std::mutex> lock(mMutex);
- if (connection->count < 0) {
- connection->count = 0;
+ if (connection->vsyncRequest == VSyncRequest::None) {
+ connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
}
}
void EventThread::onScreenReleased() {
std::lock_guard<std::mutex> lock(mMutex);
- if (!mUseSoftwareVSync) {
- // disable reliance on h/w vsync
- mUseSoftwareVSync = true;
+ if (!mVSyncState.synthetic) {
+ mVSyncState.synthetic = true;
mCondition.notify_all();
}
}
void EventThread::onScreenAcquired() {
std::lock_guard<std::mutex> lock(mMutex);
- if (mUseSoftwareVSync) {
- // resume use of h/w vsync
- mUseSoftwareVSync = false;
+ if (mVSyncState.synthetic) {
+ mVSyncState.synthetic = false;
mCondition.notify_all();
}
}
void EventThread::onVSyncEvent(nsecs_t timestamp) {
std::lock_guard<std::mutex> lock(mMutex);
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = 0;
- mVSyncEvent[0].header.timestamp = timestamp;
- mVSyncEvent[0].vsync.count++;
+
+ mPendingEvents.push_back(makeVSync(mVSyncState.displayId, timestamp, ++mVSyncState.count));
mCondition.notify_all();
}
void EventThread::onHotplugReceived(DisplayType displayType, bool connected) {
std::lock_guard<std::mutex> lock(mMutex);
- DisplayEventReceiver::Event event;
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
- event.header.id = displayType == DisplayType::Primary ? 0 : 1;
- event.header.timestamp = systemTime();
- event.hotplug.connected = connected;
-
- mPendingEvents.push(event);
+ const uint32_t displayId = displayType == DisplayType::Primary ? 0 : 1;
+ mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));
mCondition.notify_all();
}
-void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
- std::unique_lock<std::mutex> lock(mMutex);
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+ DisplayEventConsumers consumers;
+
while (mKeepRunning) {
- DisplayEventReceiver::Event event;
- std::vector<sp<EventThreadConnection>> signalConnections;
- signalConnections = waitForEventLocked(&lock, &event);
+ std::optional<DisplayEventReceiver::Event> event;
- // dispatch events to listeners...
- for (const sp<EventThreadConnection>& conn : signalConnections) {
- // now see if we still need to report this event
- status_t err = conn->postEvent(event);
- if (err == -EAGAIN || err == -EWOULDBLOCK) {
- // The destination doesn't accept events anymore, it's probably
- // full. For now, we just drop the events on the floor.
- // FIXME: Note that some events cannot be dropped and would have
- // to be re-sent later.
- // Right-now we don't have the ability to do this.
- ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
- conn.get());
- } else if (err < 0) {
- // handle any other error on the pipe as fatal. the only
- // reasonable thing to do is to clean-up this connection.
- // The most common error we'll get here is -EPIPE.
- removeDisplayEventConnectionLocked(conn);
- }
- }
- }
-}
-
-// This will return when (1) a vsync event has been received, and (2) there was
-// at least one connection interested in receiving it when we started waiting.
-std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked(
- std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) {
- std::vector<sp<EventThreadConnection>> signalConnections;
-
- while (signalConnections.empty() && mKeepRunning) {
- bool eventPending = false;
- bool waitForVSync = false;
-
- size_t vsyncCount = 0;
- nsecs_t timestamp = 0;
- for (auto& event : mVSyncEvent) {
- timestamp = event.header.timestamp;
- if (timestamp) {
- // we have a vsync event to dispatch
- if (mInterceptVSyncsCallback) {
- mInterceptVSyncsCallback(timestamp);
- }
- *outEvent = event;
- event.header.timestamp = 0;
- vsyncCount = event.vsync.count;
- break;
- }
+ // Determine next event to dispatch.
+ if (!mPendingEvents.empty()) {
+ event = mPendingEvents.front();
+ mPendingEvents.pop_front();
}
- if (!timestamp) {
- // no vsync event, see if there are some other event
- eventPending = !mPendingEvents.empty();
- if (eventPending) {
- // we have some other event to dispatch
- *outEvent = mPendingEvents.front();
- mPendingEvents.pop();
- }
+ const bool vsyncPending =
+ event && event->header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+
+ if (mInterceptVSyncsCallback && vsyncPending) {
+ mInterceptVSyncsCallback(event->header.timestamp);
}
- // find out connections waiting for events
+ bool vsyncRequested = false;
+
+ // Find connections that should consume this event.
auto it = mDisplayEventConnections.begin();
while (it != mDisplayEventConnections.end()) {
- sp<EventThreadConnection> connection(it->promote());
- if (connection != nullptr) {
- bool added = false;
- if (connection->count >= 0) {
- // we need vsync events because at least
- // one connection is waiting for it
- waitForVSync = true;
- if (timestamp) {
- // we consume the event only if it's time
- // (ie: we received a vsync event)
- if (connection->count == 0) {
- // fired this time around
- connection->count = -1;
- signalConnections.push_back(connection);
- added = true;
- } else if (connection->count == 1 ||
- (vsyncCount % connection->count) == 0) {
- // continuous event, and time to report it
- signalConnections.push_back(connection);
- added = true;
- }
- }
+ if (const auto connection = it->promote()) {
+ vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
+ if (event && shouldConsumeEvent(*event, connection)) {
+ consumers.push_back(connection);
}
- if (eventPending && !timestamp && !added) {
- // we don't have a vsync event to process
- // (timestamp==0), but we have some pending
- // messages.
- signalConnections.push_back(connection);
- }
++it;
} else {
- // we couldn't promote this reference, the connection has
- // died, so clean-up!
it = mDisplayEventConnections.erase(it);
}
}
+ if (!consumers.empty()) {
+ dispatchEvent(*event, consumers);
+ consumers.clear();
+ }
+
// Here we figure out if we need to enable or disable vsyncs
- if (timestamp && !waitForVSync) {
+ if (vsyncPending && !vsyncRequested) {
// we received a VSYNC but we have no clients
// don't report it, and disable VSYNC events
disableVSyncLocked();
- } else if (!timestamp && waitForVSync) {
+ } else if (!vsyncPending && vsyncRequested) {
// we have at least one client, so we want vsync enabled
// (TODO: this function is called right after we finish
// notifying clients of a vsync, so this call will be made
@@ -368,53 +347,74 @@
enableVSyncLocked();
}
- // note: !timestamp implies signalConnections.isEmpty(), because we
- // don't populate signalConnections if there's no vsync pending
- if (!timestamp && !eventPending) {
- // wait for something to happen
- if (waitForVSync) {
- // This is where we spend most of our time, waiting
- // for vsync events and new client registrations.
- //
- // If the screen is off, we can't use h/w vsync, so we
- // use a 16ms timeout instead. It doesn't need to be
- // precise, we just need to keep feeding our clients.
- //
- // We don't want to stall if there's a driver bug, so we
- // use a (long) timeout when waiting for h/w vsync, and
- // generate fake events when necessary.
- bool softwareSync = mUseSoftwareVSync;
- auto timeout = softwareSync ? 16ms : 1000ms;
- if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
- if (!softwareSync) {
- ALOGW("Timed out waiting for hw vsync; faking it");
- }
- // FIXME: how do we decide which display id the fake
- // vsync came from ?
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = 0;
- mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- mVSyncEvent[0].vsync.count++;
- }
- } else {
- // Nobody is interested in vsync, so we just want to sleep.
- // h/w vsync should be disabled, so this will wait until we
- // get a new connection, or an existing connection becomes
- // interested in receiving vsync again.
- mCondition.wait(*lock);
+ if (event) {
+ continue;
+ }
+
+ // Wait for event or client registration/request.
+ if (vsyncRequested) {
+ // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
+ // display is off, keep feeding clients at 60 Hz.
+ const auto timeout = mVSyncState.synthetic ? 16ms : 1000ms;
+ if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
+ ALOGW_IF(!mVSyncState.synthetic, "Faking VSYNC due to driver stall");
+
+ mPendingEvents.push_back(makeVSync(mVSyncState.displayId,
+ systemTime(SYSTEM_TIME_MONOTONIC),
+ ++mVSyncState.count));
}
+ } else {
+ mCondition.wait(lock);
}
}
+}
- // here we're guaranteed to have a timestamp and some connections to signal
- // (The connections might have dropped out of mDisplayEventConnections
- // while we were asleep, but we'll still have strong references to them.)
- return signalConnections;
+bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ return true;
+
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ switch (connection->vsyncRequest) {
+ case VSyncRequest::None:
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::None;
+ return true;
+ case VSyncRequest::Periodic:
+ return true;
+ default:
+ return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
+ }
+
+ default:
+ return false;
+ }
+}
+
+void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) {
+ for (const auto& consumer : consumers) {
+ switch (consumer->postEvent(event)) {
+ case NO_ERROR:
+ break;
+
+ case -EAGAIN:
+ // TODO: Try again if pipe is full.
+ ALOGW("Failed dispatching %s for %s", toString(event).c_str(),
+ toString(*consumer).c_str());
+ break;
+
+ default:
+ // Treat EPIPE and other errors as fatal.
+ removeDisplayEventConnectionLocked(consumer);
+ }
+ }
}
void EventThread::enableVSyncLocked() {
- if (!mUseSoftwareVSync) {
- // never enable h/w VSYNC when screen is off
+ if (!mVSyncState.synthetic) {
if (!mVsyncEnabled) {
mVsyncEnabled = true;
mVSyncSource->setCallback(this);
@@ -434,16 +434,22 @@
void EventThread::dump(std::string& result) const {
std::lock_guard<std::mutex> lock(mMutex);
- StringAppendF(&result, "VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
- StringAppendF(&result, " soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
- StringAppendF(&result, " numListeners=%zu,\n events-delivered: %u\n",
- mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count);
- for (const wp<EventThreadConnection>& weak : mDisplayEventConnections) {
- sp<EventThreadConnection> connection = weak.promote();
- StringAppendF(&result, " %p: count=%d\n", connection.get(),
- connection != nullptr ? connection->count : 0);
+
+ StringAppendF(&result, "%s: VSYNC %s\n", mThreadName, mDebugVsyncEnabled ? "on" : "off");
+ StringAppendF(&result, " VSyncState{displayId=%u, count=%u%s}\n", mVSyncState.displayId,
+ mVSyncState.count, mVSyncState.synthetic ? ", synthetic" : "");
+
+ StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size());
+ for (const auto& event : mPendingEvents) {
+ StringAppendF(&result, " %s\n", toString(event).c_str());
}
- StringAppendF(&result, " other-events-pending: %zu\n", mPendingEvents.size());
+
+ StringAppendF(&result, " connections (count=%zu):\n", mDisplayEventConnections.size());
+ for (const auto& ptr : mDisplayEventConnections) {
+ if (const auto connection = ptr.promote()) {
+ StringAppendF(&result, " %s\n", toString(*connection).c_str());
+ }
+ }
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a411885..1a8ebb7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -18,11 +18,10 @@
#include <sys/types.h>
-#include <array>
#include <condition_variable>
#include <cstdint>
+#include <deque>
#include <mutex>
-#include <queue>
#include <thread>
#include <vector>
@@ -46,6 +45,13 @@
using ResyncCallback = std::function<void()>;
+enum class VSyncRequest {
+ None = -1,
+ Single = 0,
+ Periodic = 1,
+ // Subsequent values are periods.
+};
+
class VSyncSource {
public:
class Callback {
@@ -68,7 +74,7 @@
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
- status_t setVsyncRate(uint32_t count) override;
+ status_t setVsyncRate(uint32_t rate) override;
void requestNextVsync() override; // asynchronous
// Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
// in order to update the configs.
@@ -77,10 +83,7 @@
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
- // count >= 1 : continuous event. count is the vsync rate
- // count == 0 : one-shot event that has not fired
- // count ==-1 : one-shot event that fired this round / disabled
- int32_t count;
+ VSyncRequest vsyncRequest = VSyncRequest::None;
private:
virtual void onFirstRef();
@@ -113,7 +116,7 @@
virtual status_t registerDisplayEventConnection(
const sp<EventThreadConnection>& connection) = 0;
- virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0;
+ virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
bool resetIdleTimer) = 0;
@@ -137,7 +140,7 @@
sp<EventThreadConnection> createEventConnection(ResyncCallback resyncCallback) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
- void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override;
+ void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection,
bool resetIdleTimer) override;
@@ -157,18 +160,22 @@
private:
friend EventThreadTest;
+ using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
+
// TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+ void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
- void threadMain();
- std::vector<sp<EventThreadConnection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
- DisplayEventReceiver::Event* event)
- REQUIRES(mMutex);
+ bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const REQUIRES(mMutex);
+ void dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) REQUIRES(mMutex);
void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
REQUIRES(mMutex);
+
void enableVSyncLocked() REQUIRES(mMutex);
void disableVSyncLocked() REQUIRES(mMutex);
@@ -181,18 +188,30 @@
// TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
- // constants
+
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const char* const mThreadName;
std::thread mThread;
mutable std::mutex mMutex;
mutable std::condition_variable mCondition;
- // protected by mLock
std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
- std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
- std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex);
- bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
+ std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+
+ // VSYNC state of connected display.
+ struct VSyncState {
+ uint32_t displayId = 0;
+
+ // Number of VSYNC events since display was connected.
+ uint32_t count = 0;
+
+ // True if VSYNC should be faked, e.g. when display is off.
+ bool synthetic = false;
+ };
+
+ VSyncState mVSyncState GUARDED_BY(mMutex);
+
bool mVsyncEnabled GUARDED_BY(mMutex) = false;
bool mKeepRunning GUARDED_BY(mMutex) = true;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e298e20..299499d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -28,6 +28,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <unordered_map>
#include <cutils/properties.h>
#include <log/log.h>
@@ -2231,7 +2232,8 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
bool hwcLayerDestroyed = false;
const auto displayId = displayDevice->getId();
- if (display->belongsInOutput(layer->getLayerStack())) {
+ if (display->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
drawRegion.andSelf(bounds);
@@ -2884,7 +2886,9 @@
// if not, pick the only display it's on.
hintDisplay = nullptr;
for (const auto& [token, display] : mDisplays) {
- if (display->getCompositionDisplay()->belongsInOutput(layer->getLayerStack())) {
+ if (display->getCompositionDisplay()
+ ->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
if (hintDisplay) {
hintDisplay = nullptr;
break;
@@ -3068,7 +3072,7 @@
const Layer::State& s(layer->getDrawingState());
// only consider the layers on the given layer stack
- if (!display->belongsInOutput(layer->getLayerStack())) {
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
return;
}
@@ -3194,7 +3198,7 @@
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
- if (display->belongsInOutput(layer->getLayerStack())) {
+ if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
display->editState().dirtyRegion.orSelf(dirty);
}
}
@@ -4363,8 +4367,8 @@
// ---------------------------------------------------------------------------
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto)
- NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
+ bool asProto) NO_THREAD_SAFETY_ANALYSIS {
std::string result;
IPCThreadState* ipc = IPCThreadState::self();
@@ -4388,114 +4392,36 @@
strerror(-err), err);
}
- bool dumpAll = true;
- size_t index = 0;
- size_t numArgs = args.size();
+ using namespace std::string_literals;
- if (numArgs) {
- if ((index < numArgs) &&
- (args[index] == String16("--list"))) {
- index++;
- listLayersLocked(args, index, result);
- dumpAll = false;
- }
+ static const std::unordered_map<std::string, Dumper> dumpers = {
+ {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
+ {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
+ {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+ {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })},
+ {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
+ {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+ {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
+ {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
+ {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+ {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+ {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+ {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+ {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ };
- if ((index < numArgs) &&
- (args[index] == String16("--latency"))) {
- index++;
- dumpStatsLocked(args, index, result);
- dumpAll = false;
- }
+ const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
- if ((index < numArgs) &&
- (args[index] == String16("--latency-clear"))) {
- index++;
- clearStatsLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dispsync"))) {
- index++;
- mPrimaryDispSync->dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--static-screen"))) {
- index++;
- dumpStaticScreenStats(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--frame-events"))) {
- index++;
- dumpFrameEventsLocked(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
- index++;
- dumpWideColorInfo(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--enable-layer-stats"))) {
- index++;
- mLayerStats.enable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--disable-layer-stats"))) {
- index++;
- mLayerStats.disable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--clear-layer-stats"))) {
- index++;
- mLayerStats.clear();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dump-layer-stats"))) {
- index++;
- mLayerStats.dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--frame-composition"))) {
- index++;
- dumpFrameCompositionInfo(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--display-identification"))) {
- index++;
- dumpDisplayIdentificationData(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--timestats"))) {
- index++;
- mTimeStats->parseArgs(asProto, args, index, result);
- dumpAll = false;
- }
- }
-
- if (dumpAll) {
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ } else {
if (asProto) {
LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
} else {
- dumpAllLocked(args, index, result);
+ dumpAllLocked(args, result);
}
}
@@ -4507,43 +4433,29 @@
return NO_ERROR;
}
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */, size_t& /* index */,
- std::string& result) const {
+void SurfaceFlinger::listLayersLocked(std::string& result) const {
mCurrentState.traverseInZOrder(
[&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
}
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
- std::string& result) const {
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
+void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
- if (name.isEmpty()) {
- mAnimFrameTracker.dumpStats(result);
- } else {
+ if (args.size() > 1) {
+ const auto name = String8(args[1]);
mCurrentState.traverseInZOrder([&](Layer* layer) {
if (name == layer->getName()) {
layer->dumpFrameStats(result);
}
});
+ } else {
+ mAnimFrameTracker.dumpStats(result);
}
}
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
- std::string& /* result */) {
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
+void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
- if (name.isEmpty() || (name == layer->getName())) {
+ if (args.size() < 2 || String8(args[1]) == layer->getName()) {
layer->clearFrameStats();
}
});
@@ -4551,6 +4463,10 @@
mAnimFrameTracker.clearStats();
}
+void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
+ mTimeStats->parseArgs(asProto, args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4576,6 +4492,26 @@
result.append("]");
}
+void SurfaceFlinger::dumpVSync(std::string& result) const {
+ const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
+ const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
+ "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
+ " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
+ vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset,
+ appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod());
+
+ StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled");
+
+ if (mUseScheduler) {
+ mScheduler->dump(mAppConnectionHandle, result);
+ } else {
+ mEventThread->dump(result);
+ }
+}
+
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -4779,15 +4715,8 @@
return layersProto;
}
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
- std::string& result) const {
- bool colorize = false;
- if (index < args.size()
- && (args[index] == String16("--color"))) {
- colorize = true;
- index++;
- }
-
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+ const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
// figure out if we're stuck somewhere
@@ -4817,27 +4746,14 @@
result.append("Sync configuration: ");
colorizer.reset(result);
result.append(SyncFeatures::getInstance().toString());
- result.append("\n");
+ result.append("\n\n");
colorizer.bold(result);
- result.append("DispSync configuration:\n");
+ result.append("VSYNC configuration:\n");
colorizer.reset(result);
-
- const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
- const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
- StringAppendF(&result,
- "app phase %" PRId64 " ns, "
- "sf phase %" PRId64 " ns, "
- "early app phase %" PRId64 " ns, "
- "early sf phase %" PRId64 " ns, "
- "early app gl phase %" PRId64 " ns, "
- "early sf gl phase %" PRId64 " ns, "
- "present offset %" PRId64 " ns (refresh %" PRId64 " ns)\n",
- vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset,
- appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod());
-
- // Dump static screen stats
+ dumpVSync(result);
result.append("\n");
+
dumpStaticScreenStats(result);
result.append("\n");
@@ -4911,17 +4827,6 @@
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
- StringAppendF(&result, " use Scheduler: %s\n", mUseScheduler ? "true" : "false");
- /*
- * VSYNC state
- */
- if (mUseScheduler) {
- mScheduler->dump(mAppConnectionHandle, result);
- } else {
- mEventThread->dump(result);
- }
- result.append("\n");
-
/*
* Tracing state
*/
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 28ed004..68a602c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -69,6 +69,7 @@
#include <atomic>
#include <cstdint>
+#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -76,6 +77,7 @@
#include <set>
#include <string>
#include <thread>
+#include <type_traits>
#include <unordered_map>
#include <utility>
@@ -787,19 +789,9 @@
};
}
- /* ------------------------------------------------------------------------
- * Debugging & dumpsys
+ /*
+ * Display identification
*/
-public:
- status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
- return doDump(fd, Vector<String16>(), asProto);
- }
-
- status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
- return doDump(fd, args, asProto);
- }
-
-private:
sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const {
const auto it = mPhysicalDisplayTokens.find(displayId);
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
@@ -831,17 +823,48 @@
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
- void listLayersLocked(const Vector<String16>& args, size_t& index, std::string& result) const;
- void dumpStatsLocked(const Vector<String16>& args, size_t& index, std::string& result) const
- REQUIRES(mStateLock);
- void clearStatsLocked(const Vector<String16>& args, size_t& index, std::string& result);
- void dumpAllLocked(const Vector<String16>& args, size_t& index, std::string& result) const
- REQUIRES(mStateLock);
+ /*
+ * Debugging & dumpsys
+ */
bool startDdmConnection();
- void appendSfConfigString(std::string& result) const;
+ using DumpArgs = Vector<String16>;
+ using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
+ template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+ static Dumper dumper(F&& dump) {
+ using namespace std::placeholders;
+ return std::bind(std::forward<F>(dump), _3);
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper dumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _3);
+ }
+
+ template <typename F>
+ Dumper argsDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _3);
+ }
+
+ template <typename F>
+ Dumper protoDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _2, _3);
+ }
+
+ void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+
+ void appendSfConfigString(std::string& result) const;
+ void listLayersLocked(std::string& result) const;
+ void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+ void clearStatsLocked(const DumpArgs& args, std::string& result);
+ void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void logFrameStats();
+ void dumpVSync(std::string& result) const REQUIRES(mStateLock);
void dumpStaticScreenStats(std::string& result) const;
// Not const because each Layer needs to query Fences and cache timestamps.
void dumpFrameEventsLocked(std::string& result);
@@ -858,7 +881,16 @@
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
}
- status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+ status_t doDump(int fd, const DumpArgs& args, bool asProto);
+
+ status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
+ return doDump(fd, DumpArgs(), asProto);
+ }
+
+ status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
+ return doDump(fd, args, asProto);
+ }
/* ------------------------------------------------------------------------
* VrFlinger
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 6a5488a..8d3776b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,19 +32,12 @@
namespace android {
-void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
- std::string& result) {
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
ATRACE_CALL();
- if (args.size() > index + 10) {
- ALOGD("Invalid args count");
- return;
- }
-
std::unordered_map<std::string, int32_t> argsMap;
- while (index < args.size()) {
+ for (size_t index = 0; index < args.size(); ++index) {
argsMap[std::string(String8(args[index]).c_str())] = index;
- ++index;
}
if (argsMap.count("-disable")) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71c3ed7..e8fbcab 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -77,7 +77,7 @@
TimeStats() = default;
~TimeStats() = default;
- void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, std::string& result);
+ void parseArgs(bool asProto, const Vector<String16>& args, std::string& result);
bool isEnabled();
void incrementTotalFrames();
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 86f1a39..0f95cf9 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -131,7 +131,6 @@
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
- size_t index = 0;
std::string result;
Vector<String16> args;
@@ -162,7 +161,7 @@
ALOGD("Invalid control command");
}
- EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+ EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result));
return result;
}