Merge "SF: Move/Refactor prepareFrame to CompositionEngine"
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 5bde7db..f7acaf1 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <fcntl.h>
-#include <libgen.h>
-
#include <android-base/file.h>
#include <android/os/BnDumpstate.h>
#include <android/os/BnDumpstateListener.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <cutils/properties.h>
+#include <fcntl.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libgen.h>
#include <ziparchive/zip_archive.h>
+#include <fstream>
+#include <regex>
+
#include "dumpstate.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -44,6 +45,11 @@
namespace {
+struct SectionInfo {
+ std::string name;
+ int32_t size_bytes;
+};
+
sp<IDumpstate> GetDumpstateService() {
return android::interface_cast<IDumpstate>(
android::defaultServiceManager()->getService(String16("dumpstate")));
@@ -55,14 +61,79 @@
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
}
-} // namespace
+void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) {
+ int32_t e = FindEntry(archive, entry_name, data);
+ EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name;
+}
-struct SectionInfo {
- std::string name;
- status_t status;
- int32_t size_bytes;
- int32_t duration_ms;
-};
+// Extracts the main bugreport txt from the given archive and writes into output_fd.
+void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) {
+ // Read contents of main_entry.txt which is a single line indicating the name of the zip entry
+ // that contains the main bugreport txt.
+ ZipEntry main_entry;
+ GetEntry(*handle, "main_entry.txt", &main_entry);
+ std::string bugreport_txt_name;
+ bugreport_txt_name.resize(main_entry.uncompressed_length);
+ ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+ main_entry.uncompressed_length);
+
+ // Read the main bugreport txt and extract to output_fd.
+ ZipEntry entry;
+ GetEntry(*handle, bugreport_txt_name, &entry);
+ ExtractEntryToFile(*handle, &entry, output_fd);
+}
+
+bool IsSectionStart(const std::string& line, std::string* section_name) {
+ static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"};
+ std::smatch match;
+ if (std::regex_match(line, match, kSectionStart)) {
+ *section_name = match.str(1);
+ return true;
+ }
+ return false;
+}
+
+bool IsSectionEnd(const std::string& line) {
+ // Not all lines that contain "was the duration of" is a section end, but all section ends do
+ // contain "was the duration of". The disambiguation can be done by the caller.
+ return (line.find("was the duration of") != std::string::npos);
+}
+
+// Extracts the zipped bugreport and identifies the sections.
+void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) {
+ // Open the archive
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
+
+ // Extract the main entry to a temp file
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ExtractBugreport(&handle, tmp_binary.fd);
+
+ // Read line by line and identify sections
+ std::ifstream ifs(tmp_binary.path, std::ifstream::in);
+ std::string line;
+ int section_bytes = 0;
+ std::string current_section_name;
+ while (std::getline(ifs, line)) {
+ std::string section_name;
+ if (IsSectionStart(line, §ion_name)) {
+ section_bytes = 0;
+ current_section_name = section_name;
+ } else if (IsSectionEnd(line)) {
+ if (!current_section_name.empty()) {
+ sections->push_back({current_section_name, section_bytes});
+ }
+ current_section_name = "";
+ } else if (!current_section_name.empty()) {
+ section_bytes += line.length();
+ }
+ }
+
+ CloseArchive(handle);
+}
+
+} // namespace
/**
* Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
@@ -107,11 +178,11 @@
return binder::Status::ok();
}
- binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
- int32_t duration_ms) override {
+ binder::Status onSectionComplete(const ::std::string& name, int32_t, int32_t size_bytes,
+ int32_t) override {
std::lock_guard<std::mutex> lock(lock_);
if (sections_.get() != nullptr) {
- sections_->push_back({name, status, size_bytes, duration_ms});
+ sections_->push_back({name, size_bytes});
}
return binder::Status::ok();
}
@@ -208,29 +279,30 @@
void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
ZipEntry entry;
- EXPECT_EQ(FindEntry(handle, filename, &entry), 0);
+ GetEntry(handle, filename, &entry);
EXPECT_GT(entry.uncompressed_length, minsize);
EXPECT_LT(entry.uncompressed_length, maxsize);
}
};
TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
- ZipEntry mainEntryLoc;
+ ZipEntry main_entry;
// contains main entry name file
- EXPECT_EQ(FindEntry(handle, "main_entry.txt", &mainEntryLoc), 0);
+ GetEntry(handle, "main_entry.txt", &main_entry);
- char* buf = new char[mainEntryLoc.uncompressed_length];
- ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
- delete[] buf;
+ std::string bugreport_txt_name;
+ bugreport_txt_name.resize(main_entry.uncompressed_length);
+ ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+ main_entry.uncompressed_length);
// contains main entry file
- FileExists(buf, 1000000U, 50000000U);
+ FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
ZipEntry entry;
// contains main entry name file
- EXPECT_EQ(FindEntry(handle, "version.txt", &entry), 0);
+ GetEntry(handle, "version.txt", &entry);
char* buf = new char[entry.uncompressed_length + 1];
ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
@@ -244,6 +316,10 @@
FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
+TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
+ FileExists("proto/activity.proto", 100000U, 1000000U);
+}
+
// Spot check on some files pulled from the file system
TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
// FS/proc/*/mountinfo size > 0
@@ -258,6 +334,11 @@
*/
class BugreportSectionTest : public Test {
public:
+ static void SetUpTestCase() {
+ ParseSections(ZippedBugreportGenerationTest::getZipFilePath(),
+ ZippedBugreportGenerationTest::sections.get());
+ }
+
int numMatches(const std::string& substring) {
int matches = 0;
for (auto const& section : *ZippedBugreportGenerationTest::sections) {
@@ -267,10 +348,11 @@
}
return matches;
}
+
void SectionExists(const std::string& sectionName, int minsize) {
for (auto const& section : *ZippedBugreportGenerationTest::sections) {
if (sectionName == section.name) {
- EXPECT_GE(section.size_bytes, minsize);
+ EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
return;
}
}
@@ -278,71 +360,59 @@
}
};
-// Test all sections are generated without timeouts or errors
-TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
- for (auto const& section : *ZippedBugreportGenerationTest::sections) {
- EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
- }
-}
-
TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
- int numSections = numMatches("DUMPSYS CRITICAL");
+ int numSections = numMatches("CRITICAL");
EXPECT_GE(numSections, 3);
}
TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
- int numSections = numMatches("DUMPSYS HIGH");
+ int numSections = numMatches("HIGH");
EXPECT_GE(numSections, 2);
}
TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
- int allSections = numMatches("DUMPSYS");
- int criticalSections = numMatches("DUMPSYS CRITICAL");
- int highSections = numMatches("DUMPSYS HIGH");
+ int allSections = ZippedBugreportGenerationTest::sections->size();
+ int criticalSections = numMatches("CRITICAL");
+ int highSections = numMatches("HIGH");
int normalSections = allSections - criticalSections - highSections;
EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
<< "High:" << highSections << "Normal:" << normalSections << ")";
}
-TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
- int numSections = numMatches("proto/");
- EXPECT_GE(numSections, 1);
-}
-
// Test if some critical sections are being generated.
TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
- SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
+ SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
}
TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
- SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
- SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
+ SectionExists("CRITICAL activity", /* bytes= */ 5000);
+ SectionExists("activity", /* bytes= */ 10000);
}
TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
- SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
+ SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
}
TEST_F(BugreportSectionTest, WindowSectionGenerated) {
- SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
+ SectionExists("CRITICAL window", /* bytes= */ 20000);
}
TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
- SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
- SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
+ SectionExists("HIGH connectivity", /* bytes= */ 3000);
+ SectionExists("connectivity", /* bytes= */ 5000);
}
TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
- SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
+ SectionExists("HIGH meminfo", /* bytes= */ 100000);
}
TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
- SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
+ SectionExists("batterystats", /* bytes= */ 1000);
}
TEST_F(BugreportSectionTest, WifiSectionGenerated) {
- SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
+ SectionExists("wifi", /* bytes= */ 100000);
}
class DumpstateBinderTest : public Test {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d786b84..6bd97d58 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -494,6 +494,12 @@
}
}
+#ifdef __ANDROID_VNDK__
+constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
+#else
+constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
+#endif
+
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
@@ -502,6 +508,7 @@
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ?
threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
+ writeInt32(kHeader);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
@@ -559,6 +566,12 @@
updateWorkSourceRequestHeaderPosition();
int32_t workSource = readInt32();
threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+ // vendor header
+ int32_t header = readInt32();
+ if (header != kHeader) {
+ ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header);
+ return false;
+ }
// Interface descriptor.
const String16 str(readString16());
if (str == interface) {
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..8ed09f8 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -199,6 +199,10 @@
int32_t type = data.readInt32();
int32_t format = data.readInt32();
native_handle_t *resource = data.readNativeHandle();
+ // Avoid a crash in native_handle_close if resource is nullptr
+ if (resource == nullptr) {
+ return BAD_VALUE;
+ }
sp<ISensorEventConnection> ch =
createSensorDirectConnection(opPackageName, size, type, format, resource);
native_handle_close(resource);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
index 3786d1d..861dc6c 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -191,7 +191,8 @@
enum class ConfigFileType : uint32_t {
kLensMetrics,
kDeviceMetrics,
- kDeviceConfiguration
+ kDeviceConfiguration,
+ kDeviceEdid
};
struct DisplayProtocol {
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index e383bb2..b7abb99 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -85,6 +85,8 @@
DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1,
// Request the per device configuration data file.
DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2,
+ // Request the edid data for the display.
+ DVR_CONFIGURATION_DATA_DEVICE_EDID = 3,
};
// dvr_display_manager.h
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 87162c0..8980a92 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -44,6 +44,18 @@
Endpoint::Create(display::DisplayProtocol::kClientPath)) {
hardware_composer_.Initialize(
hidl, primary_display_id, request_display_callback);
+
+ uint8_t port;
+ const auto error = hidl->getDisplayIdentificationData(
+ primary_display_id, &port, &display_identification_data_);
+ if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
+ if (error !=
+ android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
+ ALOGI("DisplayService: identification data error\n");
+ } else {
+ ALOGI("DisplayService: identification data unsupported\n");
+ }
+ }
}
bool DisplayService::IsInitialized() const {
@@ -204,6 +216,12 @@
case display::ConfigFileType::kDeviceConfiguration:
property_name = kDvrDeviceConfigProperty;
break;
+ case display::ConfigFileType::kDeviceEdid:
+ if (display_identification_data_.size() == 0) {
+ return ErrorStatus(ENOENT);
+ }
+ return std::string(display_identification_data_.begin(),
+ display_identification_data_.end());
default:
return ErrorStatus(EINVAL);
}
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index e0f2edd..d45a61f 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -18,6 +18,8 @@
#include "epoll_event_dispatcher.h"
#include "hardware_composer.h"
+#include "DisplayHardware/DisplayIdentification.h"
+
namespace android {
namespace dvr {
@@ -117,6 +119,8 @@
DisplayService(const DisplayService&) = delete;
void operator=(const DisplayService&) = delete;
+
+ DisplayIdentificationData display_identification_data_;
};
} // namespace dvr
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index fc40eaf..019815c 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -4237,53 +4237,55 @@
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
- nsecs_t finishTime = commandEntry->eventTime;
+ const nsecs_t finishTime = commandEntry->eventTime;
uint32_t seq = commandEntry->seq;
- bool handled = commandEntry->handled;
+ const bool handled = commandEntry->handled;
// Handle post-event policy actions.
DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
- if (dispatchEntry) {
- nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
- if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- std::string msg =
- StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
- connection->getWindowName().c_str(), eventDuration * 0.000001f);
- dispatchEntry->eventEntry->appendDescription(msg);
- ALOGI("%s", msg.c_str());
- }
-
- bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
- restartEvent = afterKeyEventLockedInterruptible(connection,
- dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
- restartEvent = afterMotionEventLockedInterruptible(connection,
- dispatchEntry, motionEntry, handled);
- } else {
- restartEvent = false;
- }
-
- // Dequeue the event and start the next cycle.
- // Note that because the lock might have been released, it is possible that the
- // contents of the wait queue to have been drained, so we need to double-check
- // a few things.
- if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
- connection->waitQueue.dequeue(dispatchEntry);
- traceWaitQueueLength(connection);
- if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
- connection->outboundQueue.enqueueAtHead(dispatchEntry);
- traceOutboundQueueLength(connection);
- } else {
- releaseDispatchEntry(dispatchEntry);
- }
- }
-
- // Start the next dispatch cycle for this connection.
- startDispatchCycleLocked(now(), connection);
+ if (!dispatchEntry) {
+ return;
}
+
+ nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+ if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+ std::string msg =
+ StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
+ connection->getWindowName().c_str(), eventDuration * 0.000001f);
+ dispatchEntry->eventEntry->appendDescription(msg);
+ ALOGI("%s", msg.c_str());
+ }
+
+ bool restartEvent;
+ if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+ restartEvent =
+ afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
+ } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+ restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
+ handled);
+ } else {
+ restartEvent = false;
+ }
+
+ // Dequeue the event and start the next cycle.
+ // Note that because the lock might have been released, it is possible that the
+ // contents of the wait queue to have been drained, so we need to double-check
+ // a few things.
+ if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
+ connection->waitQueue.dequeue(dispatchEntry);
+ traceWaitQueueLength(connection);
+ if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+ connection->outboundQueue.enqueueAtHead(dispatchEntry);
+ traceOutboundQueueLength(connection);
+ } else {
+ releaseDispatchEntry(dispatchEntry);
+ }
+ }
+
+ // Start the next dispatch cycle for this connection.
+ startDispatchCycleLocked(now(), connection);
}
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7acb470..952643c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -89,12 +89,13 @@
if (mSupportKernelTimer) {
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
std::chrono::milliseconds(mSetIdleTimerMs),
- [this] { resetKernelTimerCallback(); },
- [this] { expiredKernelTimerCallback(); });
+ [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ [this] { kernelIdleTimerCallback(TimerState::Expired); });
} else {
mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
- std::chrono::milliseconds(mSetIdleTimerMs), [this] { resetTimerCallback(); },
- [this] { expiredTimerCallback(); });
+ std::chrono::milliseconds(mSetIdleTimerMs),
+ [this] { idleTimerCallback(TimerState::Reset); },
+ [this] { idleTimerCallback(TimerState::Expired); });
}
mIdleTimer->start();
}
@@ -102,16 +103,17 @@
if (mSetTouchTimerMs > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
mTouchTimer = std::make_unique<scheduler::OneShotTimer>(
- std::chrono::milliseconds(mSetTouchTimerMs), [this] { resetTouchTimerCallback(); },
- [this] { expiredTouchTimerCallback(); });
+ std::chrono::milliseconds(mSetTouchTimerMs),
+ [this] { touchTimerCallback(TimerState::Reset); },
+ [this] { touchTimerCallback(TimerState::Expired); });
mTouchTimer->start();
}
if (mSetDisplayPowerTimerMs > 0) {
mDisplayPowerTimer = std::make_unique<scheduler::OneShotTimer>(
std::chrono::milliseconds(mSetDisplayPowerTimerMs),
- [this] { resetDisplayPowerTimerCallback(); },
- [this] { expiredDisplayPowerTimerCallback(); });
+ [this] { displayPowerTimerCallback(TimerState::Reset); },
+ [this] { displayPowerTimerCallback(TimerState::Expired); });
mDisplayPowerTimer->start();
}
}
@@ -363,23 +365,22 @@
RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
+ if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) {
return;
}
- mContentRefreshRate = refreshRateRound;
- ATRACE_INT("ContentFPS", mContentRefreshRate);
+ mFeatures.contentRefreshRate = refreshRateRound;
+ ATRACE_INT("ContentFPS", refreshRateRound);
- mIsHDRContent = isHDR;
- ATRACE_INT("ContentHDR", mIsHDRContent);
+ mFeatures.isHDRContent = isHDR;
+ ATRACE_INT("ContentHDR", isHDR);
- mCurrentContentFeatureState = refreshRateRound > 0
- ? ContentFeatureState::CONTENT_DETECTION_ON
- : ContentFeatureState::CONTENT_DETECTION_OFF;
+ mFeatures.contentDetection =
+ refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
newRefreshRateType = calculateRefreshRateType();
- if (mRefreshRateType == newRefreshRateType) {
+ if (mFeatures.refreshRateType == newRefreshRateType) {
return;
}
- mRefreshRateType = newRefreshRateType;
+ mFeatures.refreshRateType = newRefreshRateType;
}
changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
}
@@ -433,7 +434,7 @@
void Scheduler::setDisplayPowerState(bool normal) {
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mIsDisplayPowerStateNormal = normal;
+ mFeatures.isDisplayPowerStateNormal = normal;
}
if (mDisplayPowerTimer) {
@@ -445,60 +446,41 @@
mLayerHistory.clearHistory();
}
-void Scheduler::resetTimerCallback() {
- handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
- ATRACE_INT("ExpiredIdleTimer", 0);
-}
+void Scheduler::kernelIdleTimerCallback(TimerState state) {
+ ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
-void Scheduler::resetKernelTimerCallback() {
- ATRACE_INT("ExpiredKernelIdleTimer", 0);
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+ if (!mGetCurrentRefreshRateTypeCallback || !mGetVsyncPeriod) return;
+
+ const auto type = mGetCurrentRefreshRateTypeCallback();
+ if (state == TimerState::Reset && type == RefreshRateType::PERFORMANCE) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
- resyncToHardwareVsync(true, mGetVsyncPeriod());
- }
+ resyncToHardwareVsync(true /* makeAvailable */, mGetVsyncPeriod());
+ } else if (state == TimerState::Expired && type != RefreshRateType::PERFORMANCE) {
+ // Disable HW VSYNC if the timer expired, as we don't need it enabled if
+ // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
+ // need to update the DispSync model anyway.
+ disableHardwareVsync(false /* makeUnavailable */);
}
}
-void Scheduler::expiredTimerCallback() {
- handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
- ATRACE_INT("ExpiredIdleTimer", 1);
+void Scheduler::idleTimerCallback(TimerState state) {
+ handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+ ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
-void Scheduler::resetTouchTimerCallback() {
- handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
- ATRACE_INT("TouchState", 1);
+void Scheduler::touchTimerCallback(TimerState state) {
+ const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+ handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */);
+ ATRACE_INT("TouchState", static_cast<int>(touch));
}
-void Scheduler::expiredTouchTimerCallback() {
- handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
- ATRACE_INT("TouchState", 0);
-}
-
-void Scheduler::resetDisplayPowerTimerCallback() {
- handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
- ATRACE_INT("ExpiredDisplayPowerTimer", 0);
-}
-
-void Scheduler::expiredDisplayPowerTimerCallback() {
- handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
- ATRACE_INT("ExpiredDisplayPowerTimer", 1);
-}
-
-void Scheduler::expiredKernelTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- ATRACE_INT("ExpiredKernelIdleTimer", 1);
- if (mGetCurrentRefreshRateTypeCallback) {
- if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
- // Disable HW Vsync if the timer expired, as we don't need it
- // enabled if we're not pushing frames, and if we're in PERFORMANCE
- // mode then we'll need to re-update the DispSync model anyways.
- disableHardwareVsync(false);
- }
- }
+void Scheduler::displayPowerTimerCallback(TimerState state) {
+ handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
+ true /* eventOnContentDetection */);
+ ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
std::string Scheduler::doDump() {
@@ -519,12 +501,11 @@
}
*currentState = newState;
newRefreshRateType = calculateRefreshRateType();
- if (mRefreshRateType == newRefreshRateType) {
+ if (mFeatures.refreshRateType == newRefreshRateType) {
return;
}
- mRefreshRateType = newRefreshRateType;
- if (eventOnContentDetection &&
- mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+ mFeatures.refreshRateType = newRefreshRateType;
+ if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
event = ConfigEvent::Changed;
}
}
@@ -533,37 +514,38 @@
Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
// HDR content is not supported on PERFORMANCE mode
- if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
+ if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
return RefreshRateType::DEFAULT;
}
// If Display Power is not in normal operation we want to be in performance mode.
// When coming back to normal mode, a grace period is given with DisplayPowerTimer
- if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
+ if (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) {
return RefreshRateType::PERFORMANCE;
}
// As long as touch is active we want to be in performance mode
- if (mCurrentTouchState == TouchState::ACTIVE) {
+ if (mFeatures.touch == TouchState::Active) {
return RefreshRateType::PERFORMANCE;
}
// If timer has expired as it means there is no new content on the screen
- if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
+ if (mFeatures.idleTimer == TimerState::Expired) {
return RefreshRateType::DEFAULT;
}
// If content detection is off we choose performance as we don't know the content fps
- if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
+ if (mFeatures.contentDetection == ContentDetectionState::Off) {
return RefreshRateType::PERFORMANCE;
}
// Content detection is on, find the appropriate refresh rate with minimal error
+ const float rate = static_cast<float>(mFeatures.contentRefreshRate);
auto iter = min_element(mRefreshRateConfigs.getRefreshRates().cbegin(),
mRefreshRateConfigs.getRefreshRates().cend(),
- [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
- return std::abs(l.second->fps - static_cast<float>(rate)) <
- std::abs(r.second->fps - static_cast<float>(rate));
+ [rate](const auto& lhs, const auto& rhs) -> bool {
+ return std::abs(lhs.second->fps - rate) <
+ std::abs(rhs.second->fps - rate);
});
RefreshRateType currRefreshRateType = iter->first;
@@ -571,11 +553,10 @@
// 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
// align well with both
constexpr float MARGIN = 0.05f;
- float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
- float(mContentRefreshRate);
+ float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / rate;
if (std::abs(std::round(ratio) - ratio) > MARGIN) {
while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
- ratio = iter->second->fps / float(mContentRefreshRate);
+ ratio = iter->second->fps / rate;
if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
currRefreshRateType = iter->first;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 123036e..0d9d7aa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -200,10 +200,9 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
- enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
- enum class IdleTimerState { EXPIRED, RESET };
- enum class TouchState { INACTIVE, ACTIVE };
- enum class DisplayPowerTimerState { EXPIRED, RESET };
+ enum class ContentDetectionState { Off, On };
+ enum class TimerState { Reset, Expired };
+ enum class TouchState { Inactive, Active };
// Creates a connection on the given EventThread and forwards the given callbacks.
sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
@@ -212,26 +211,12 @@
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
- // Function that is called when the timer resets.
- void resetTimerCallback();
- // Function that is called when the timer expires.
- void expiredTimerCallback();
- // Function that is called when the timer resets when paired with a display
- // driver timeout in the kernel. This enables hardware vsync when we move
- // out from idle.
- void resetKernelTimerCallback();
- // Function that is called when the timer expires when paired with a display
- // driver timeout in the kernel. This disables hardware vsync when we move
- // into idle.
- void expiredKernelTimerCallback();
- // Function that is called when the touch timer resets.
- void resetTouchTimerCallback();
- // Function that is called when the touch timer expires.
- void expiredTouchTimerCallback();
- // Function that is called when the display power timer resets.
- void resetDisplayPowerTimerCallback();
- // Function that is called when the display power timer expires.
- void expiredDisplayPowerTimerCallback();
+ // Update feature state machine to given state when corresponding timer resets or expires.
+ void kernelIdleTimerCallback(TimerState);
+ void idleTimerCallback(TimerState);
+ void touchTimerCallback(TimerState);
+ void displayPowerTimerCallback(TimerState);
+
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
// handles various timer features to change the refresh rate.
@@ -304,16 +289,19 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
std::mutex mFeatureStateLock;
- ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
- ContentFeatureState::CONTENT_DETECTION_OFF;
- IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
- TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
- DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
- DisplayPowerTimerState::EXPIRED;
- uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
- RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
- bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
- bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
+
+ struct {
+ ContentDetectionState contentDetection = ContentDetectionState::Off;
+ TimerState idleTimer = TimerState::Reset;
+ TouchState touch = TouchState::Inactive;
+ TimerState displayPowerTimer = TimerState::Expired;
+
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ uint32_t contentRefreshRate = 0;
+
+ bool isHDRContent = false;
+ bool isDisplayPowerStateNormal = true;
+ } mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 3da4336..6204779 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -21,11 +21,14 @@
#include "vkjson.h"
#include <assert.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
-#include <cmath>
+#include <json/json.h>
+
+#include <algorithm>
#include <cinttypes>
+#include <cmath>
#include <cstdio>
#include <limits>
#include <memory>
@@ -33,8 +36,6 @@
#include <type_traits>
#include <utility>
-#include <json/json.h>
-
namespace {
inline bool IsIntegral(double value) {
@@ -46,6 +47,14 @@
#endif
}
+// Floating point fields of Vulkan structure use single precision. The string
+// output of max double value in c++ will be larger than Java double's infinity
+// value. Below fake double max/min values are only to serve the safe json text
+// parsing in between C++ and Java, becasue Java json library simply cannot
+// handle infinity.
+static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max();
+static const double SAFE_DOUBLE_MIN = 0.99 * std::numeric_limits<double>::min();
+
template <typename T> struct EnumTraits;
template <> struct EnumTraits<VkPhysicalDeviceType> {
static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
@@ -851,7 +860,8 @@
template <typename T, typename = EnableForArithmetic<T>>
inline Json::Value ToJsonValue(const T& value) {
- return Json::Value(static_cast<double>(value));
+ return Json::Value(
+ std::clamp(static_cast<double>(value), SAFE_DOUBLE_MIN, SAFE_DOUBLE_MAX));
}
inline Json::Value ToJsonValue(const uint64_t& value) {