Merge 24Q4 into AOSP main
Bug: 370570306
Merged-In: I925ad7f7c2f27f5e19aaaf06d61b14140ffce666
Change-Id: I97b96ad15e4acb7395ea369edfb3489530360cda
diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h
index 765aec1..d04a2ce 100644
--- a/misc_writer/include/misc_writer/misc_writer.h
+++ b/misc_writer/include/misc_writer/misc_writer.h
@@ -47,6 +47,7 @@
kWriteDstOffset,
kSetDisplayMode,
kClearDisplayMode,
+ kWriteEagleEyePatterns,
kUnset = -1,
};
@@ -70,6 +71,10 @@
char dsttransition[32];
char dstoffset[32];
char user_preferred_resolution[32];
+ char sota_csku[8];
+ char sota_csku_signature[96];
+ char eagleEye[2000];
+ char skipUnbootableCheck[32];
} __attribute__((__packed__)) bootloader_message_vendor_t;
static constexpr uint32_t kThemeFlagOffsetInVendorSpace =
@@ -115,6 +120,8 @@
static constexpr uint32_t kDisplayModeOffsetInVendorSpace =
offsetof(bootloader_message_vendor_t, user_preferred_resolution);
static constexpr char kDisplayModePrefix[] = "mode=";
+ static constexpr uint32_t kEagleEyeOffset =
+ offsetof(bootloader_message_vendor_t, eagleEye);
// Minimum and maximum valid value for max-ram-size
static constexpr int32_t kRamSizeDefault = -1;
@@ -144,6 +151,7 @@
// Performs the stored MiscWriterActions. If |override_offset| is set, writes to the input
// offset in the vendor space of /misc instead of the default offset.
bool PerformAction(std::optional<size_t> override_offset = std::nullopt);
+ bool UpdateSotaConfig(std::optional<size_t> override_offset = std::nullopt);
private:
MiscWriterActions action_{MiscWriterActions::kUnset};
diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp
index 0f8983a..7b025f2 100644
--- a/misc_writer/misc_writer.cpp
+++ b/misc_writer/misc_writer.cpp
@@ -22,6 +22,7 @@
#include <android-base/stringprintf.h>
#include <bootloader_message/bootloader_message.h>
#include <string.h>
+#include <charconv>
namespace android {
namespace hardware {
@@ -104,7 +105,7 @@
content.resize(32);
break;
case MiscWriterActions::kSetSotaConfig:
- goto sota_config;
+ return UpdateSotaConfig(override_offset);
case MiscWriterActions::kWriteDstTransition:
offset = override_offset.value_or(kDstTransitionOffsetInVendorSpace);
content = std::string(kDstTransition) + stringdata_;
@@ -123,6 +124,11 @@
: std::string(32, 0);
content.resize(32, 0);
break;
+ case MiscWriterActions::kWriteEagleEyePatterns:
+ offset = override_offset.value_or(kEagleEyeOffset);
+ content = stringdata_;
+ content.resize(sizeof(bootloader_message_vendor_t::eagleEye), 0);
+ break;
case MiscWriterActions::kUnset:
LOG(ERROR) << "The misc writer action must be set";
return false;
@@ -133,26 +139,70 @@
LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
return false;
}
+ return true;
+}
-sota_config:
- if (action_ == MiscWriterActions::kSetSotaFlag || action_ == MiscWriterActions::kSetSotaConfig) {
- content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.state", "");
- if (content.size() != 0 && content.size() <= 40) {
- offset = kSotaStateOffsetInVendorSpace;
- if (std::string err;
- !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
- LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
- return false;
- }
+bool MiscWriter::UpdateSotaConfig(std::optional<size_t> override_offset) {
+ size_t offset = 0;
+ std::string content;
+ std::string err;
+
+ // Update sota state
+ offset = override_offset.value_or(kSotaStateOffsetInVendorSpace);
+ content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.state", "");
+ if (content.size() != 0) {
+ content.resize(sizeof(bootloader_message_vendor_t::sota_client_state));
+ if (!WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
+ return false;
}
- content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.schedule_shipmode", "");
- if (content.size() != 0 && content.size() <= 32) {
- offset = kSotaScheduleShipmodeOffsetInVendorSpace;
- if (std::string err;
- !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
- LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
- return false;
+ }
+
+ // Update sota schedule_shipmode
+ offset = override_offset.value_or(kSotaScheduleShipmodeOffsetInVendorSpace);
+ content = ::android::base::GetProperty("persist.vendor.nfc.factoryota.schedule_shipmode", "");
+ if (content.size() != 0) {
+ content.resize(sizeof(bootloader_message_vendor_t::sota_schedule_shipmode));
+ if (!WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
+ return false;
+ }
+ }
+
+ // Update sota csku signature
+ offset = override_offset.value_or(offsetof(bootloader_message_vendor_t, sota_csku_signature));
+ std::string signature;
+ signature += ::android::base::GetProperty("persist.vendor.factoryota.signature1", "");
+ signature += ::android::base::GetProperty("persist.vendor.factoryota.signature2", "");
+ signature += ::android::base::GetProperty("persist.vendor.factoryota.signature3", "");
+ if (signature.size() != 0) {
+ LOG(INFO) << "persist.vendor.factoryota.signature=" << signature;
+ if (signature.length() != 2 * sizeof(bootloader_message_vendor_t::sota_csku_signature)) {
+ LOG(ERROR) << "signature.length() should be "
+ << 2 * sizeof(bootloader_message_vendor_t::sota_csku_signature) << " not "
+ << signature.length();
+ return false;
+ }
+ content.resize(sizeof(bootloader_message_vendor_t::sota_csku_signature));
+ // Traslate hex string to bytes
+ for (size_t i = 0; i < 2 * content.size(); i += 2)
+ if (std::from_chars(&signature[i], &signature[i + 2], content[i / 2], 16).ec != std::errc{}) {
+ LOG(ERROR) << "Failed to convert " << signature << " to bytes";
+ return false;
}
+ if (!WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write signature at offset " << offset << " : " << err;
+ return false;
+ }
+
+ // Update sota csku
+ offset = override_offset.value_or(offsetof(bootloader_message_vendor_t, sota_csku));
+ content = ::android::base::GetProperty("persist.vendor.factoryota.csku", "");
+ content.resize(sizeof(bootloader_message_vendor_t::sota_csku));
+ LOG(INFO) << "persist.vendor.factoryota.csku=" << content;
+ if (!WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err;
+ return false;
}
}
diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp
index 936aa66..47649ac 100644
--- a/misc_writer/misc_writer_main.cpp
+++ b/misc_writer/misc_writer_main.cpp
@@ -57,6 +57,8 @@
std::cerr << " --set-dstoffset Write the time offset during the next dst transition\n";
std::cerr << " --set-display-mode <mode> Write the display mode at boot\n";
std::cerr << " --clear-display-mode Clear the display mode at boot\n";
+ std::cerr << " --set-trending-issue-pattern <string within 2000 byte> Write a regex string";
+ std::cerr << " --read-trending-issue-pattern Read eagleEye misc portion";
std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc "
"partition.\nDefault offset is used for each action unless "
"--override-vendor-space-offset is specified.\n";
@@ -85,6 +87,8 @@
{ "set-dstoffset", required_argument, nullptr, 0 },
{ "set-display-mode", required_argument, nullptr, 0 },
{ "clear-display-mode", no_argument, nullptr, 0 },
+ { "set-trending-issue-pattern", required_argument, nullptr, 0 },
+ { "read-trending-issue-pattern", no_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
@@ -254,6 +258,26 @@
}
misc_writer = std::make_unique<MiscWriter>(MiscWriterActions::kWriteDstOffset,
std::to_string(dst_offset));
+ } else if (option_name == "set-trending-issue-pattern"s) {
+ if (argc != 3) {
+ std::cerr << "Not the right amount of arguements, we expect 1 argument but were provide " << argc - 2;
+ return EXIT_FAILURE;
+ }
+ if (misc_writer) {
+ LOG(ERROR) << "Misc writer action has already been set";
+ return Usage(argv[0]);
+ } else if (sizeof(argv[2]) >= 2000) {
+ std::cerr << "String is too large, we only take strings smaller than 2000, but you provide " << sizeof(argv[2]);
+ return Usage(argv[0]);
+ }
+ misc_writer = std::make_unique<MiscWriter>(MiscWriterActions::kWriteEagleEyePatterns, argv[2]);
+ } else if (option_name == "read-trending-issue-pattern"s) {
+ if (misc_writer) {
+ LOG(ERROR) << "Misc writer action has already been set";
+ return Usage(argv[0]);
+ }
+ std::cerr << "function is not yet implemented";
+ return EXIT_SUCCESS;
} else {
LOG(FATAL) << "Unreachable path, option_name: " << option_name;
}
diff --git a/mm/fstab.zram.3g b/mm/fstab.zram.3g
index 2c45bcc..02d7b5a 100644
--- a/mm/fstab.zram.3g
+++ b/mm/fstab.zram.3g
@@ -1 +1 @@
-/dev/block/zram0 none swap defaults zramsize=3221225472,zram_backingdev_size=512M
+/dev/block/zram0 none swap defaults zramsize=3221225472,zram_backingdev_size=1G
diff --git a/mm/fstab.zram.50p b/mm/fstab.zram.50p
index 7a845e5..6f54d6f 100644
--- a/mm/fstab.zram.50p
+++ b/mm/fstab.zram.50p
@@ -1 +1 @@
-/dev/block/zram0 none swap defaults zramsize=50%,zram_backingdev_size=512M
+/dev/block/zram0 none swap defaults zramsize=50%,zram_backingdev_size=1G
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp
index 643b884..6f3cdf3 100644
--- a/pixelstats/Android.bp
+++ b/pixelstats/Android.bp
@@ -17,6 +17,16 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "pixelatoms_proto",
+ srcs: [
+ "pixelatoms.proto",
+ ":libstats_shared_enum_protos",
+ ":libstats_atom_options_protos",
+ ":libprotobuf-internal-descriptor-proto",
+ ],
+}
+
cc_library {
name: "pixelatoms-cpp",
vendor: true,
@@ -74,16 +84,15 @@
genrule {
name: "pixelatoms_defs.h",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/pixelatoms_defs.h --namespace hardware,google,pixel,PixelAtoms --vendor-proto $(location pixelatoms.proto)",
- srcs: [
- "pixelatoms.proto",
- ":libstats_shared_enum_protos",
- ":libstats_atom_options_protos",
- ":libprotobuf-internal-protos",
- ],
+ cmd: "$(location stats-log-api-gen) --header $(out)" +
+ " --namespace hardware,google,pixel,PixelAtoms" +
+ " --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
out: [
"pixelatoms_defs.h",
],
+ srcs: [
+ ":pixelatoms_proto",
+ ],
}
cc_library {
@@ -99,15 +108,12 @@
cmd: "$(location stats-log-api-gen) --header $(out)" +
" --module pixelstats" +
" --namespace android,hardware,google,pixel,PixelAtoms" +
- " --vendor-proto $(location pixelatoms.proto)",
+ " --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
out: [
"pixelstatsatoms.h",
],
srcs: [
- "pixelatoms.proto",
- ":libstats_shared_enum_protos",
- ":libstats_atom_options_protos",
- ":libprotobuf-internal-protos",
+ ":pixelatoms_proto",
],
}
@@ -118,15 +124,12 @@
" --module pixelstats" +
" --importHeader pixelstatsatoms.h" +
" --namespace android,hardware,google,pixel,PixelAtoms" +
- " --vendor-proto $(location pixelatoms.proto)",
+ " --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
out: [
"pixelstatsatoms.cpp",
],
srcs: [
- "pixelatoms.proto",
- ":libstats_shared_enum_protos",
- ":libstats_atom_options_protos",
- ":libprotobuf-internal-protos",
+ ":pixelatoms_proto",
],
}
@@ -196,10 +199,3 @@
],
header_libs: ["chre_api"],
}
-
-filegroup {
- name: "pixelatoms_proto",
- srcs: [
- "pixelatoms.proto",
- ],
-}
diff --git a/pixelstats/BatteryEEPROMReporter.cpp b/pixelstats/BatteryEEPROMReporter.cpp
index 2316a0e..4f73b6d 100644
--- a/pixelstats/BatteryEEPROMReporter.cpp
+++ b/pixelstats/BatteryEEPROMReporter.cpp
@@ -43,6 +43,14 @@
BatteryEEPROMReporter::BatteryEEPROMReporter() {}
+void BatteryEEPROMReporter::setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset,
+ int content) {
+ std::vector<VendorAtomValue> &val = *values;
+
+ if (offset - kVendorAtomOffset < val.size())
+ val[offset - kVendorAtomOffset].set<VendorAtomValue::intValue>(content);
+}
+
void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
const std::string &path) {
std::string file_contents;
@@ -270,6 +278,52 @@
ALOGE("Unable to report BatteryEEPROM to Stats service");
}
+void BatteryEEPROMReporter::reportEventInt32(const std::shared_ptr<IStats> &stats_client,
+ const struct BatteryHistoryInt32 &hist) {
+ std::vector<VendorAtomValue> values(23);
+
+ ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
+ "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
+ "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
+ "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%#x, full_rep:%d, "
+ "tempco:%#x, rcomp0:%#x, timer_h:%d",
+ hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
+ hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
+ hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
+ hist.min_ibatt, hist.checksum, hist.full_rep, hist.tempco, hist.rcomp0, hist.timer_h);
+
+ setAtomFieldValue(&values, BatteryEEPROM::kCycleCntFieldNumber, hist.cycle_cnt);
+ setAtomFieldValue(&values, BatteryEEPROM::kFullCapFieldNumber, hist.full_cap);
+ setAtomFieldValue(&values, BatteryEEPROM::kEsrFieldNumber, hist.esr);
+ setAtomFieldValue(&values, BatteryEEPROM::kRslowFieldNumber, hist.rslow);
+ setAtomFieldValue(&values, BatteryEEPROM::kSohFieldNumber, hist.soh);
+ setAtomFieldValue(&values, BatteryEEPROM::kBattTempFieldNumber, hist.batt_temp);
+ setAtomFieldValue(&values, BatteryEEPROM::kCutoffSocFieldNumber, hist.cutoff_soc);
+ setAtomFieldValue(&values, BatteryEEPROM::kCcSocFieldNumber, hist.cc_soc);
+ setAtomFieldValue(&values, BatteryEEPROM::kSysSocFieldNumber, hist.sys_soc);
+ setAtomFieldValue(&values, BatteryEEPROM::kMsocFieldNumber, hist.msoc);
+ setAtomFieldValue(&values, BatteryEEPROM::kBattSocFieldNumber, hist.batt_soc);
+ setAtomFieldValue(&values, BatteryEEPROM::kReserveFieldNumber, hist.reserve);
+ setAtomFieldValue(&values, BatteryEEPROM::kMaxTempFieldNumber, hist.max_temp);
+ setAtomFieldValue(&values, BatteryEEPROM::kMinTempFieldNumber, hist.min_temp);
+ setAtomFieldValue(&values, BatteryEEPROM::kMaxVbattFieldNumber, hist.max_vbatt);
+ setAtomFieldValue(&values, BatteryEEPROM::kMinVbattFieldNumber, hist.min_vbatt);
+ setAtomFieldValue(&values, BatteryEEPROM::kMaxIbattFieldNumber, hist.max_ibatt);
+ setAtomFieldValue(&values, BatteryEEPROM::kMinIbattFieldNumber, hist.min_ibatt);
+ setAtomFieldValue(&values, BatteryEEPROM::kChecksumFieldNumber, hist.checksum);
+ setAtomFieldValue(&values, BatteryEEPROM::kTempcoFieldNumber, hist.tempco);
+ setAtomFieldValue(&values, BatteryEEPROM::kRcomp0FieldNumber, hist.rcomp0);
+ setAtomFieldValue(&values, BatteryEEPROM::kTimerHFieldNumber, hist.timer_h);
+ setAtomFieldValue(&values, BatteryEEPROM::kFullRepFieldNumber, hist.full_rep);
+
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kBatteryEeprom,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report BatteryEEPROM to Stats service");
+}
+
void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &paths) {
struct BatteryHistory gmsr = {.checksum = EvtGMSR};
@@ -427,104 +481,98 @@
void BatteryEEPROMReporter::checkAndReportFGLearning(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &paths) {
- struct BatteryHistory params = {.checksum = EvtFGLearningHistory};
+ struct BatteryHistoryInt32 params = {.checksum = EvtFGLearningHistory};
+ std::string path;
struct timespec boot_time;
auto format = FormatIgnoreAddr;
- int fg_idx = 0;
+ std::vector<std::vector<uint32_t>> events;
if (paths.empty())
return;
- clock_gettime(CLOCK_MONOTONIC, &boot_time);
- for (int path_idx = 0; path_idx < paths.size(); path_idx++) {
- std::vector<std::vector<uint16_t>> events;
- std::string path = paths[path_idx];
-
- if (!path.empty() && fileExists(path)) {
- readLogbuffer(path, kNumFGLearningFieldsV2, params.checksum, format, last_lh_check_,
- events);
- if (events.size() == 0)
- readLogbuffer(path, kNumFGLearningFieldsV2, "learn", format, last_lh_check_,
- events);
- if (events.size() == 0)
- readLogbuffer(path, kNumFGLearningFields, "learn", format, last_lh_check_, events);
-
- for (int event_idx = 0; event_idx < events.size(); event_idx++) {
- std::vector<uint16_t> &event = events[event_idx];
-
- if (event.size() == kNumFGLearningFieldsV2) {
- params.full_cap = event[0]; /* fcnom */
- params.esr = event[1]; /* dpacc */
- params.rslow = event[2]; /* dqacc */
- params.full_rep = event[3]; /* fcrep */
- params.msoc = (uint8_t)(event[4] >> 8); /* repsoc */
- params.sys_soc = (uint8_t)(event[5] >> 8); /* mixsoc */
- params.batt_soc = (uint8_t)(event[6] >> 8);/* vfsoc */
- params.min_ibatt = event[7]; /* fstats */
- params.max_temp = (int8_t)(event[8] >> 8); /* avgtemp */
- params.min_temp = (int8_t)(event[9] >> 8); /* temp */
- params.max_ibatt = event[10]; /* qh */
- params.max_vbatt = event[11]; /* vcell */
- params.min_vbatt = event[12]; /* avgvcell */
- params.cycle_cnt = event[13]; /* vfocf */
- params.rcomp0 = event[14]; /* rcomp0 */
- params.tempco = event[15]; /* tempco */
- params.reserve = fg_idx ; /* battery index */
- } else if (event.size() == kNumFGLearningFields) {
- params.full_cap = event[0]; /* fcnom */
- params.esr = event[1]; /* dpacc */
- params.rslow = event[2]; /* dqacc */
- params.max_vbatt = event[3]; /* fcrep */
- params.full_rep = event[4]; /* repsoc */
- params.min_vbatt = event[5]; /* mixsoc */
- params.max_ibatt = event[6]; /* vfsoc */
- params.min_ibatt = event[7]; /* fstats */
- params.rcomp0 = event[8]; /* rcomp0 */
- params.tempco = event[9]; /* tempco */
- params.reserve = fg_idx; /* battery index */
- } else {
- ALOGE("Not support %zu fields for FG learning event", event.size());
- continue;
- }
- reportEvent(stats_client, params);
- }
- fg_idx++;
+ for (int i = 0; i < paths.size(); i++) {
+ if (fileExists(paths[i])) {
+ path = paths[i];
+ break;
}
}
+
+ /* not found */
+ if (path.empty())
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &boot_time);
+
+ readLogbuffer(path, kNumFGLearningFieldsV3, params.checksum, format, last_lh_check_, events);
+ if (events.size() == 0)
+ readLogbuffer(path, kNumFGLearningFieldsV2, params.checksum, format, last_lh_check_, events);
+
+ for (int event_idx = 0; event_idx < events.size(); event_idx++) {
+ std::vector<uint32_t> &event = events[event_idx];
+ if (event.size() == kNumFGLearningFieldsV2 ||
+ event.size() == kNumFGLearningFieldsV3) {
+ params.full_cap = event[0]; /* fcnom */
+ params.esr = event[1]; /* dpacc */
+ params.rslow = event[2]; /* dqacc */
+ params.full_rep = event[3]; /* fcrep */
+ params.msoc = (uint8_t)(event[4] >> 8); /* repsoc */
+ params.sys_soc = (uint8_t)(event[5] >> 8); /* mixsoc */
+ params.batt_soc = (uint8_t)(event[6] >> 8);/* vfsoc */
+ params.min_ibatt = event[7]; /* fstats */
+ params.max_temp = (int8_t)(event[8] >> 8); /* avgtemp */
+ params.min_temp = (int8_t)(event[9] >> 8); /* temp */
+ params.max_ibatt = event[10]; /* qh */
+ params.max_vbatt = event[11]; /* vcell */
+ params.min_vbatt = event[12]; /* avgvcell */
+ params.cycle_cnt = event[13]; /* vfocf */
+ params.rcomp0 = event[14]; /* rcomp0 */
+ params.tempco = event[15]; /* tempco */
+ if (event.size() == kNumFGLearningFieldsV3)
+ params.soh = event[16]; /* unix time */
+ } else {
+ ALOGE("Not support %zu fields for FG learning event", event.size());
+ continue;
+ }
+ reportEventInt32(stats_client, params);
+ }
last_lh_check_ = (unsigned int)boot_time.tv_sec;
}
void BatteryEEPROMReporter::checkAndReportValidation(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &paths) {
- struct BatteryHistory params = {.checksum = EvtHistoryValidation};
+ struct BatteryHistoryInt32 params = {.checksum = EvtHistoryValidation};
+ std::string path;
struct timespec boot_time;
auto format = FormatIgnoreAddr;
- int fg_idx = 0;
+ std::vector<std::vector<uint32_t>> events;
if (paths.empty())
return;
- clock_gettime(CLOCK_MONOTONIC, &boot_time);
for (int i = 0; i < paths.size(); i++) {
- std::vector<std::vector<uint16_t>> events;
- std::string path = paths[i];
+ if (fileExists(paths[i])) {
+ path = paths[i];
+ break;
+ }
+ }
- if (!path.empty() && fileExists(path)) {
- readLogbuffer(path, kNumValidationFields, params.checksum, format, last_hv_check_, events);
- for (int seq = 0; seq < events.size(); seq++) {
- std::vector<uint16_t> &event = events[seq];
- if (event.size() == kNumValidationFields) {
- params.full_cap = event[0]; /* fcnom */
- params.esr = event[1]; /* dpacc */
- params.rslow = event[2]; /* dqacc */
- params.full_rep = event[3]; /* fcrep */
- params.reserve = fg_idx;
- reportEvent(stats_client, params);
- } else {
- ALOGE("Not support %zu fields for History Validation event", event.size());
- }
- }
- fg_idx++;
+ /* not found */
+ if (path.empty())
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &boot_time);
+
+ readLogbuffer(path, kNumValidationFields, params.checksum, format, last_hv_check_, events);
+ for (int event_idx = 0; event_idx < events.size(); event_idx++) {
+ std::vector<uint32_t> &event = events[event_idx];
+ if (event.size() == kNumValidationFields) {
+ params.full_cap = event[0]; /* fcnom */
+ params.esr = event[1]; /* dpacc */
+ params.rslow = event[2]; /* dqacc */
+ params.full_rep = event[3]; /* fcrep */
+ reportEventInt32(stats_client, params);
+ } else {
+ ALOGE("Not support %zu fields for History Validation event", event.size());
}
}
last_hv_check_ = (unsigned int)boot_time.tv_sec;
diff --git a/pixelstats/BatteryFGReporter.cpp b/pixelstats/BatteryFGReporter.cpp
index aa928d5..9808b45 100644
--- a/pixelstats/BatteryFGReporter.cpp
+++ b/pixelstats/BatteryFGReporter.cpp
@@ -251,7 +251,7 @@
const std::vector<std::string> &paths) {
std::string path;
struct timespec boot_time;
- std::vector<std::vector<uint16_t>> events;
+ std::vector<std::vector<uint32_t>> events;
if (paths.empty())
return;
@@ -264,7 +264,7 @@
}
clock_gettime(CLOCK_MONOTONIC, &boot_time);
- readLogbuffer(path, kNumAbnormalEventFields, EvtFGAbnormalEvent, FormatNoAddr, last_ab_check_, events);
+ readLogbuffer(path, kNumAbnormalEventFields, EvtFGAbnormalEvent, FormatOnlyVal, last_ab_check_, events);
for (int seq = 0; seq < events.size(); seq++) {
if (events[seq].size() == kNumAbnormalEventFields) {
struct BatteryFGAbnormalData data;
diff --git a/pixelstats/DisplayStatsReporter.cpp b/pixelstats/DisplayStatsReporter.cpp
index c72af6e..67178cb 100644
--- a/pixelstats/DisplayStatsReporter.cpp
+++ b/pixelstats/DisplayStatsReporter.cpp
@@ -320,6 +320,146 @@
if (!ret.isOk())
ALOGE("Unable to report hdcp stats to Stats service");
}
+
+// Capture dsc/fec support from sysfs nodes
+bool DisplayStatsReporter::captureDisplayPortFECDSCStats(
+ const std::vector<std::string> &displayport_fecdsc_stats_paths, int64_t *pcur_data) {
+ bool report_stats = false;
+ std::string path;
+
+ if (displayport_fecdsc_stats_paths.size() < DISPLAY_PORT_DSC_STATS_SIZE) {
+ ALOGE("Number of displayport dsc support stats paths (%zu) is less than expected (%d)",
+ displayport_fecdsc_stats_paths.size(), DISPLAY_PORT_DSC_STATS_SIZE);
+ return false;
+ }
+
+ // Iterate over the sysfs nodes and collect the data
+ for (int i = 0; i < DISPLAY_PORT_DSC_STATS_SIZE; i++) {
+ // Get the sysfs path from the stats path array
+ path = displayport_fecdsc_stats_paths[i];
+
+ if (!readDisplayErrorCount(path, &(pcur_data[i]))) {
+ // Failed to read new data, keep previous data that was saved.
+ pcur_data[i] = prev_dp_dsc_data_[i];
+ } else {
+ report_stats |= (pcur_data[i] > prev_dp_dsc_data_[i]);
+ }
+ }
+
+ return report_stats;
+}
+
+void DisplayStatsReporter::logDisplayPortFECDSCStats(
+ const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_fecdsc_stats_paths) {
+ int64_t cur_data[DISPLAY_PORT_DSC_STATS_SIZE];
+ bool report_stats = false;
+
+ memcpy(cur_data, prev_dp_dsc_data_, sizeof(prev_dp_dsc_data_));
+ if (!captureDisplayPortFECDSCStats(displayport_fecdsc_stats_paths, &cur_data[0])) {
+ memcpy(prev_dp_dsc_data_, cur_data, sizeof(cur_data));
+ return;
+ }
+
+ VendorAtomValue tmp;
+ int64_t max_use_count = static_cast<int64_t>(INT32_MAX);
+ int use_count;
+ std::vector<VendorAtomValue> values(DISPLAY_PORT_DSC_STATS_SIZE);
+
+ for (int i = 0; i < DISPLAY_PORT_DSC_STATS_SIZE; i++) {
+ use_count = std::min<int64_t>(cur_data[i] - prev_dp_dsc_data_[i], max_use_count);
+ if (verifyCount(use_count, &report_stats) < 0)
+ return;
+
+ tmp.set<VendorAtomValue::intValue>(use_count);
+ values[i] = tmp;
+ }
+
+ memcpy(prev_dp_dsc_data_, cur_data, sizeof(cur_data));
+
+ if (!report_stats)
+ return;
+
+ ALOGD("Report updated DisplayPort FEC/DSC metrics to stats service");
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kDisplayPortDscSupportStats,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report DisplayPort FEC/DSC stats to Stats service");
+}
+
+// Capture maximum resolution support from sysfs nodes
+bool DisplayStatsReporter::captureDisplayPortMaxResStats(
+ const std::vector<std::string> &displayport_max_res_stats_paths, int64_t *pcur_data) {
+ bool report_stats = false;
+ std::string path;
+
+ if (displayport_max_res_stats_paths.size() < DISPLAY_PORT_MAX_RES_STATS_SIZE) {
+ ALOGE("Number of displayport maximum resolution stats paths (%zu) is less than expected "
+ "(%d)",
+ displayport_max_res_stats_paths.size(), DISPLAY_PORT_MAX_RES_STATS_SIZE);
+ return false;
+ }
+
+ // Iterate over the sysfs nodes and collect the data
+ for (int i = 0; i < DISPLAY_PORT_MAX_RES_STATS_SIZE; i++) {
+ // Get the sysfs path from the stats path array
+ path = displayport_max_res_stats_paths[i];
+
+ if (!readDisplayErrorCount(path, &(pcur_data[i]))) {
+ // Failed to read new data, keep previous data that was saved.
+ pcur_data[i] = prev_dp_max_res_data_[i];
+ } else {
+ report_stats |= (pcur_data[i] > prev_dp_max_res_data_[i]);
+ }
+ }
+
+ return report_stats;
+}
+
+void DisplayStatsReporter::logDisplayPortMaxResStats(
+ const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_max_res_stats_paths) {
+ int64_t cur_data[DISPLAY_PORT_MAX_RES_STATS_SIZE];
+ bool report_stats = false;
+
+ memcpy(cur_data, prev_dp_max_res_data_, sizeof(prev_dp_max_res_data_));
+ if (!captureDisplayPortMaxResStats(displayport_max_res_stats_paths, &cur_data[0])) {
+ memcpy(prev_dp_max_res_data_, cur_data, sizeof(cur_data));
+ return;
+ }
+
+ VendorAtomValue tmp;
+ int64_t max_use_count = static_cast<int64_t>(INT32_MAX);
+ int use_count;
+ std::vector<VendorAtomValue> values(DISPLAY_PORT_MAX_RES_STATS_SIZE);
+
+ for (int i = 0; i < DISPLAY_PORT_MAX_RES_STATS_SIZE; i++) {
+ use_count = std::min<int64_t>(cur_data[i] - prev_dp_max_res_data_[i], max_use_count);
+ if (verifyCount(use_count, &report_stats) < 0)
+ return;
+
+ tmp.set<VendorAtomValue::intValue>(use_count);
+ values[i] = tmp;
+ }
+
+ memcpy(prev_dp_max_res_data_, cur_data, sizeof(cur_data));
+
+ if (!report_stats)
+ return;
+
+ ALOGD("Report updated displayport maximum resolution metrics to stats service");
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = "",
+ .atomId = PixelAtoms::Atom::kDisplayPortMaxResolutionStats,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report DisplayPort maximum resolution stats to Stats service");
+}
+
void DisplayStatsReporter::logDisplayStats(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &display_stats_paths,
const display_stats_type stats_type) {
@@ -333,6 +473,12 @@
case HDCP_STATE:
logHDCPAuthTypeStats(stats_client, display_stats_paths);
break;
+ case DISP_PORT_DSC_STATE:
+ logDisplayPortFECDSCStats(stats_client, display_stats_paths);
+ break;
+ case DISP_PORT_MAX_RES_STATE:
+ logDisplayPortMaxResStats(stats_client, display_stats_paths);
+ break;
default:
ALOGE("Unsupport display state type(%d)", stats_type);
}
diff --git a/pixelstats/MmMetricsReporter.cpp b/pixelstats/MmMetricsReporter.cpp
index ac6c258..a74d88c 100644
--- a/pixelstats/MmMetricsReporter.cpp
+++ b/pixelstats/MmMetricsReporter.cpp
@@ -28,10 +28,16 @@
#include <pixelstats/MmMetricsReporter.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
+#include <array>
+#include <cinttypes>
+#include <cstdint>
#include <numeric>
+#include <optional>
+#include <vector>
#define SZ_4K 0x00001000
#define SZ_2M 0x00200000
@@ -124,7 +130,15 @@
{"latency_high", CmaStatusExt::kCmaAllocLatencyHighFieldNumber, false},
};
-static bool file_exists(const char *path) {
+// Oom group range names
+const std::array oom_group_range_names{
+ "[951,1000]", "[901,950]", "[851,900]", "[801,850]", "[751,800]", "[701,750]",
+ "[651,700]", "[601,650]", "[551,600]", "[501,550]", "[451,500]", "[401,450]",
+ "[351,400]", "[301,350]", "[251,300]", "[201,250]", "[200,200]", "[151,199]",
+ "[101,150]", "[51,100]", "[1,50]", "[0,0]", "[-1000,-1]",
+};
+
+static bool file_exists(const char *const path) {
struct stat sbuf;
return (stat(path, &sbuf) == 0);
@@ -166,6 +180,30 @@
return !err_require_all && !err_require_one_ion_total_pools_path;
}
+bool MmMetricsReporter::checkKernelOomUsageSupport() {
+ if (!file_exists(kProcVendorMmUsageByOom)) {
+ ALOGE("Oom score grouped memory usage metrics not supported"
+ " - %s not found.",
+ kProcVendorMmUsageByOom);
+ return false;
+ }
+ return true;
+}
+
+bool MmMetricsReporter::checkKernelGcmaSupport() {
+ std::string base_path(kGcmaBasePath);
+
+ for (auto parr : {kGcmaHourlySimpleKnobs, kGcmaHourlyHistogramKnobs}) {
+ for (auto p : kGcmaHourlySimpleKnobs) {
+ if (!file_exists((base_path + '/' + p).c_str())) {
+ ALOGE("kernel GCMA metrics not supported- %s not found.", p);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
MmMetricsReporter::MmMetricsReporter()
: kVmstatPath("/proc/vmstat"),
kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"),
@@ -176,9 +214,13 @@
kPixelStatMm("/sys/kernel/pixel_stat/mm"),
kMeminfoPath("/proc/meminfo"),
kProcStatPath("/proc/stat"),
+ kProcVendorMmUsageByOom("/proc/vendor_mm/memory_usage_by_oom_score"),
+ kGcmaBasePath("/sys/kernel/vendor_mm/gcma"),
prev_compaction_duration_(kNumCompactionDurationPrevMetrics, 0),
prev_direct_reclaim_(kNumDirectReclaimPrevMetrics, 0) {
ker_mm_metrics_support_ = checkKernelMMMetricSupport();
+ ker_oom_usage_support_ = checkKernelOomUsageSupport();
+ ker_gcma_support_ = checkKernelGcmaSupport();
}
bool MmMetricsReporter::ReadFileToUint(const std::string &path, uint64_t *val) {
@@ -632,6 +674,30 @@
}
}
+void MmMetricsReporter::logGcmaPerHour(const std::shared_ptr<IStats> &stats_client) {
+ std::vector<VendorAtomValue> values = readAndGenGcmaPerHour();
+
+ if (values.size() != 0) {
+ reportVendorAtom(stats_client, PixelAtoms::Atom::kMmGcmaSnapshot, values, "MmGcmaSnapshot");
+ }
+}
+
+void MmMetricsReporter::logMmProcessUsageByOomGroupSnapshot(
+ const std::shared_ptr<IStats> &stats_client) {
+ if (!OomUsageSupoorted())
+ return;
+
+ std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
+ if (!readMmProcessUsageByOomGroup(&ogusage))
+ return;
+
+ for (const auto &m : ogusage) {
+ std::vector<VendorAtomValue> values = genMmProcessUsageByOomGroupSnapshotAtom(m);
+ reportVendorAtom(stats_client, PixelAtoms::Atom::kMmProcessUsageByOomGroupSnapshot, values,
+ "MmProcessUsageByOomGroup");
+ }
+}
+
std::vector<VendorAtomValue> MmMetricsReporter::genPixelMmMetricsPerHour() {
if (!MmMetricsSupported())
return std::vector<VendorAtomValue>();
@@ -674,6 +740,14 @@
}
}
+void MmMetricsReporter::logGcmaPerDay(const std::shared_ptr<IStats> &stats_client) {
+ std::vector<VendorAtomValue> values = readAndGenGcmaPerDay();
+
+ if (values.size() != 0) {
+ reportVendorAtom(stats_client, PixelAtoms::Atom::kMmGcmaStats, values, "MmGcmaStats");
+ }
+}
+
std::vector<VendorAtomValue> MmMetricsReporter::genPixelMmMetricsPerDay() {
if (!MmMetricsSupported())
return std::vector<VendorAtomValue>();
@@ -1516,6 +1590,168 @@
}
}
+/*
+ * parse one line of proc fs "vendor_mm/memory_usage_by_oom_score"
+ */
+std::optional<MmMetricsReporter::OomGroupMemUsage>
+MmMetricsReporter::parseMmProcessUsageByOomGroupLine(const std::string &line) {
+ static_assert(OOM_NUM_OF_GROUPS == oom_group_range_names.size(),
+ "Error: Number of groups must match.");
+
+ std::vector<std::string> tokens = android::base::Tokenize(line, " \t");
+ if (tokens.size() < 7) {
+ ALOGE("Error: Insufficient tokens on line: %s", line.c_str());
+ return std::nullopt;
+ }
+
+ MmMetricsReporter::OomGroupMemUsage data;
+
+ // Find the matching group range name and convert it to enumerate:int32_t
+ auto it = std::find(oom_group_range_names.begin(), oom_group_range_names.end(), tokens[0]);
+ if (it == oom_group_range_names.end()) {
+ ALOGE("Error: Unknown group range: %s", tokens[0].c_str());
+ return std::nullopt;
+ }
+ data.oom_group =
+ static_cast<OomScoreAdjGroup>(std::distance(oom_group_range_names.begin(), it));
+
+ bool success = android::base::ParseInt(tokens[1], &data.nr_task) &&
+ android::base::ParseInt(tokens[2], &data.file_rss_kb) &&
+ android::base::ParseInt(tokens[3], &data.anon_rss_kb) &&
+ android::base::ParseInt(tokens[4], &data.pgtable_kb) &&
+ android::base::ParseInt(tokens[5], &data.swap_ents_kb) &&
+ android::base::ParseInt(tokens[6], &data.shmem_rss_kb) && data.nr_task >= 0 &&
+ data.file_rss_kb >= 0 && data.anon_rss_kb >= 0 && data.pgtable_kb >= 0 &&
+ data.swap_ents_kb >= 0 && data.shmem_rss_kb >= 0;
+
+ if (!success) {
+ ALOGE("Error parsing UInt values on line: %s", line.c_str());
+ return std::nullopt;
+ }
+
+ return data;
+}
+
+/*
+ * read proc fs "vendor_mm/memory_usage_by_oom_score"
+ */
+bool MmMetricsReporter::readMmProcessUsageByOomGroup(
+ std::vector<MmMetricsReporter::OomGroupMemUsage> *ogusage) {
+ ogusage->clear();
+ oom_usage_uid_++; // Unique ID per read
+ std::string path = getSysfsPath(kProcVendorMmUsageByOom);
+
+ std::string file_contents;
+ if (!android::base::ReadFileToString(path, &file_contents)) {
+ ALOGE("Error reading file: %s", path.c_str());
+ goto error_out;
+ }
+
+ for (const auto &line : android::base::Split(file_contents, "\n")) {
+ if (line.empty() || line[0] == '#')
+ continue; // Skip the header line or an empty line
+ std::optional<MmMetricsReporter::OomGroupMemUsage> parsedData =
+ parseMmProcessUsageByOomGroupLine(line);
+ if (parsedData.has_value())
+ ogusage->push_back(parsedData.value());
+ }
+
+ if (ogusage->size() != OOM_NUM_OF_GROUPS) {
+ ALOGE("Error file corrupted: number of oom_group %zu != expected %" PRId32, ogusage->size(),
+ OOM_NUM_OF_GROUPS);
+ goto error_out;
+ }
+
+ for (size_t i = 0; i < ogusage->size(); ++i) {
+ if ((*ogusage)[i].oom_group != static_cast<int32_t>(i)) {
+ goto error_out; // Mismatch found
+ }
+ }
+ return true;
+
+error_out:
+ ogusage->clear();
+ return false;
+}
+
+/*
+ * generate one MmProcessUsageByOomGroupSnapshot atom
+ * Note: number of atoms = number of oom groups
+ */
+std::vector<VendorAtomValue> MmMetricsReporter::genMmProcessUsageByOomGroupSnapshotAtom(
+ const MmMetricsReporter::OomGroupMemUsage &data) {
+ std::vector<VendorAtomValue> values;
+
+ values.push_back(VendorAtomValue(oom_usage_uid_));
+ values.push_back(VendorAtomValue(static_cast<int32_t>(data.oom_group)));
+ values.push_back(VendorAtomValue(data.nr_task));
+ values.push_back(VendorAtomValue(data.file_rss_kb));
+ values.push_back(VendorAtomValue(data.anon_rss_kb));
+ values.push_back(VendorAtomValue(data.pgtable_kb));
+ values.push_back(VendorAtomValue(data.swap_ents_kb));
+ values.push_back(VendorAtomValue(data.shmem_rss_kb));
+ return values;
+}
+
+std::vector<VendorAtomValue> MmMetricsReporter::readAndGenGcmaPerHour() {
+ uint64_t val;
+ std::string path = getSysfsPath(std::string(kGcmaBasePath) + '/' + kGcmaCached);
+ std::vector<VendorAtomValue> values;
+
+ if (!GcmaSupported())
+ return values;
+
+ if (!ReadFileToUint(path, &val)) {
+ ALOGE("Error: GCMA.cached: file %s: parsed Uint failed.", path.c_str());
+ } else if (static_cast<int64_t>(val) < 0) {
+ ALOGE("Error: GCMA.cached: value overflow.");
+ } else {
+ values.push_back(VendorAtomValue(static_cast<int64_t>(val)));
+ }
+ return values;
+}
+
+std::vector<VendorAtomValue> MmMetricsReporter::readAndGenGcmaPerDay() {
+ std::vector<VendorAtomValue> values;
+ uint64_t val;
+ std::vector<int64_t> repeatedLongValue;
+ std::string path;
+ std::string base_path(kGcmaBasePath);
+
+ if (!GcmaSupported())
+ return values;
+
+ for (auto p : kGcmaHourlySimpleKnobs) {
+ path = getSysfsPath(base_path + '/' + p);
+ if (!ReadFileToUint(path, &val)) {
+ ALOGE("Error: GCMA.%s: file %s: parsed Uint failed.", p, path.c_str());
+ goto got_error;
+ } else if (static_cast<int64_t>(val) < 0) {
+ ALOGE("Error: GCMA.%s: value overflow.", p);
+ goto got_error;
+ }
+ values.push_back(VendorAtomValue(static_cast<int64_t>(val)));
+ }
+
+ for (auto p : kGcmaHourlyHistogramKnobs) {
+ path = getSysfsPath(base_path + '/' + p);
+ if (!ReadFileToUint(path, &val)) {
+ ALOGE("Error: GCMA.%s: file %s: parsed Uint failed.", p, path.c_str());
+ goto got_error;
+ } else if (static_cast<int64_t>(val) < 0) {
+ ALOGE("Error: GCMA.%s: value overflow.", p);
+ goto got_error;
+ }
+ repeatedLongValue.push_back(static_cast<int64_t>(val));
+ }
+ values.push_back(VendorAtomValue(std::optional<std::vector<int64_t>>(repeatedLongValue)));
+ return values;
+
+got_error:
+ values.clear();
+ return values;
+}
+
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/StatsHelper.cpp b/pixelstats/StatsHelper.cpp
index 0a1b752..6fb93e4 100644
--- a/pixelstats/StatsHelper.cpp
+++ b/pixelstats/StatsHelper.cpp
@@ -237,24 +237,14 @@
void readLogbuffer(const std::string &buf_path, int num_fields, uint16_t code,
enum ReportEventFormat format, unsigned int last_check_time,
- std::vector<std::vector<uint16_t>> &events) {
- char hex_str[16];
-
- snprintf(hex_str, sizeof(hex_str), "0x%X", code);
-
- return readLogbuffer(buf_path, num_fields, hex_str, format, last_check_time, events);
-}
-
-void readLogbuffer(const std::string &buf_path, int num_fields, const char *code,
- enum ReportEventFormat format, unsigned int last_check_time,
- std::vector<std::vector<uint16_t>> &events) {
+ std::vector<std::vector<uint32_t>> &events) {
std::istringstream ss;
std::string file_contents, line;
int num, field_idx, pos, read;
- unsigned int ts, reported = 0;
- uint16_t addr, val;
- char type[16];
- std::vector<uint16_t> vect(num_fields);
+ unsigned int ts, addr, val;
+ unsigned int reported = 0;
+ uint16_t type;
+ std::vector<uint32_t> vect(num_fields);
if (!ReadFileToString(buf_path, &file_contents)) {
ALOGE("Unable to read logbuffer path: %s - %s", buf_path.c_str(), strerror(errno));
@@ -263,30 +253,29 @@
ss.str(file_contents);
while (getline(ss, line)) {
- num = sscanf(line.c_str(), "[%u.%*u] %15s%n", &ts, type, &pos);
- if (num != 2 || strncmp(type, code, strlen(code)))
+ num = sscanf(line.c_str(), "[%u.%*u] %hx%n", &ts, &type, &pos);
+ if (num != 2 || type != code)
continue;
- if (ts <= last_check_time) {
+ if (last_check_time != 0 && ts <= last_check_time) {
reported++;
continue;
}
for (field_idx = 0; field_idx < num_fields; field_idx++, pos += read) {
if (format == FormatAddrWithVal) {
- num = sscanf(&line.c_str()[pos], " %2" SCNx16 ":%4" SCNx16 "%n", &addr, &val,
- &read);
+ num = sscanf(&line.c_str()[pos], "%x:%x%n", &addr, &val, &read);
if (num != 2 || (num_fields - field_idx < 2))
break;
vect[field_idx++] = addr;
vect[field_idx] = val;
} else if (format == FormatIgnoreAddr) {
- num = sscanf(&line.c_str()[pos], " %*2" SCNx16 ":%4" SCNx16 "%n", &val, &read);
+ num = sscanf(&line.c_str()[pos], "%*[^:]:%x%n", &val, &read);
if (num != 1)
break;
vect[field_idx] = val;
- } else if (format == FormatNoAddr) {
- num = sscanf(&line.c_str()[pos], " %4" SCNx16 "%n", &val, &read);
+ } else if (format == FormatOnlyVal) {
+ num = sscanf(&line.c_str()[pos], "%x%n", &val, &read);
if (num != 1)
break;
vect[field_idx] = val;
@@ -299,7 +288,7 @@
events.push_back(vect);
}
if (events.size() > 0 || reported > 0)
- ALOGD("%s: new:%zu, reported:%d", code, events.size(), reported);
+ ALOGD("0x%04X: new:%zu, reported:%d", code, events.size(), reported);
return;
}
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index aa0f2f6..0d57011 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -51,7 +51,9 @@
using android::hardware::google::pixel::PixelAtoms::BlockStatsReported;
using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
using android::hardware::google::pixel::PixelAtoms::DisplayPanelErrorStats;
+using android::hardware::google::pixel::PixelAtoms::DisplayPortDSCSupportCountStatsReported;
using android::hardware::google::pixel::PixelAtoms::DisplayPortErrorStats;
+using android::hardware::google::pixel::PixelAtoms::DisplayPortMaxResolutionCountStatsReported;
using android::hardware::google::pixel::PixelAtoms::F2fsAtomicWriteInfo;
using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
@@ -123,6 +125,8 @@
kWifiPcieLinkStatsPath(sysfs_paths.WifiPcieLinkStatsPath),
kDisplayStatsPaths(sysfs_paths.DisplayStatsPaths),
kDisplayPortStatsPaths(sysfs_paths.DisplayPortStatsPaths),
+ kDisplayPortDSCStatsPaths(sysfs_paths.DisplayPortDSCStatsPaths),
+ kDisplayPortMaxResolutionStatsPaths(sysfs_paths.DisplayPortMaxResolutionStatsPaths),
kHDCPStatsPaths(sysfs_paths.HDCPStatsPaths),
kPDMStatePath(sysfs_paths.PDMStatePath),
kWavesPath(sysfs_paths.WavesPath),
@@ -470,6 +474,15 @@
thermal_stats_reporter_.logThermalStats(stats_client, kThermalStatsPaths);
}
+void SysfsCollector::logDisplayPortDSCStats(const std::shared_ptr<IStats> &stats_client) {
+ display_stats_reporter_.logDisplayStats(stats_client, kDisplayPortDSCStatsPaths,
+ DisplayStatsReporter::DISP_PORT_DSC_STATE);
+}
+
+void SysfsCollector::logDisplayPortMaxResolutionStats(const std::shared_ptr<IStats> &stats_client) {
+ display_stats_reporter_.logDisplayStats(stats_client, kDisplayPortMaxResolutionStatsPaths,
+ DisplayStatsReporter::DISP_PORT_MAX_RES_STATE);
+}
/**
* Report the Speech DSP state.
*/
@@ -2114,11 +2127,14 @@
logBatteryEEPROM(stats_client);
logBatteryHealth(stats_client);
logBatteryTTF(stats_client);
+ logBatteryHistoryValidation();
logBlockStatsReported(stats_client);
logCodec1Failed(stats_client);
logCodecFailed(stats_client);
logDisplayStats(stats_client);
logDisplayPortStats(stats_client);
+ logDisplayPortDSCStats(stats_client);
+ logDisplayPortMaxResolutionStats(stats_client);
logHDCPStats(stats_client);
logF2fsStats(stats_client);
logF2fsAtomicWriteInfo(stats_client);
@@ -2133,6 +2149,7 @@
logSpeakerHealthStats(stats_client);
mm_metrics_reporter_.logCmaStatus(stats_client);
mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
+ mm_metrics_reporter_.logGcmaPerDay(stats_client);
logVendorAudioHardwareStats(stats_client);
logThermalStats(stats_client);
logTempResidencyStats(stats_client);
@@ -2169,7 +2186,6 @@
void SysfsCollector::logOnce() {
logBrownout();
- logBatteryHistoryValidation();
}
void SysfsCollector::logPerHour() {
@@ -2179,6 +2195,8 @@
return;
}
mm_metrics_reporter_.logPixelMmMetricsPerHour(stats_client);
+ mm_metrics_reporter_.logGcmaPerHour(stats_client);
+ mm_metrics_reporter_.logMmProcessUsageByOomGroupSnapshot(stats_client);
logZramStats(stats_client);
if (kPowerMitigationStatsPath != nullptr && strlen(kPowerMitigationStatsPath) > 0)
mitigation_stats_reporter_.logMitigationStatsPerHour(stats_client,
@@ -2230,6 +2248,7 @@
return;
}
+ ALOGI("Time-series metrics were initiated.");
while (1) {
int readval;
union {
diff --git a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
index 8a4a893..6dc1c62 100644
--- a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
+++ b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
@@ -28,6 +28,7 @@
namespace pixel {
using aidl::android::frameworks::stats::IStats;
+using aidl::android::frameworks::stats::VendorAtomValue;
// The storage for save whole history is 928 byte
// each history contains 19 items with total size 28 byte
@@ -121,8 +122,9 @@
/* The number of elements in struct BatteryHistory for P20 series */
const int kNumBatteryHistoryFields = 19;
/* The number of elements for relaxation event */
- const int kNumFGLearningFields = 10;
const int kNumFGLearningFieldsV2 = 16;
+ /* with additional unix time field */
+ const int kNumFGLearningFieldsV3 = 17;
unsigned int last_lh_check_ = 0;
/* The number of elements for history validation event */
const int kNumValidationFields = 4;
@@ -145,12 +147,41 @@
unsigned maxdischgcurr:4;
};
+ struct BatteryHistoryInt32 {
+ int32_t cycle_cnt;
+ int32_t full_cap;
+ int32_t esr;
+ int32_t rslow;
+ int32_t soh;
+ int32_t batt_temp;
+ int32_t cutoff_soc;
+ int32_t cc_soc;
+ int32_t sys_soc;
+ int32_t msoc;
+ int32_t batt_soc;
+ int32_t reserve;
+ int32_t max_temp;
+ int32_t min_temp;
+ int32_t max_vbatt;
+ int32_t min_vbatt;
+ int32_t max_ibatt;
+ int32_t min_ibatt;
+ int32_t checksum;
+ int32_t tempco;
+ int32_t rcomp0;
+ int32_t timer_h;
+ int32_t full_rep;
+ };
+
int64_t report_time_ = 0;
int64_t getTimeSecs();
bool checkLogEvent(struct BatteryHistory hist);
void reportEvent(const std::shared_ptr<IStats> &stats_client,
const struct BatteryHistory &hist);
+ void reportEventInt32(const std::shared_ptr<IStats> &stats_client,
+ const struct BatteryHistoryInt32 &hist);
+ void setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset, int content);
const int kNum77759GMSRFields = 11;
const int kNum77779GMSRFields = 9;
diff --git a/pixelstats/include/pixelstats/DisplayStatsReporter.h b/pixelstats/include/pixelstats/DisplayStatsReporter.h
index c465c41..00267f0 100644
--- a/pixelstats/include/pixelstats/DisplayStatsReporter.h
+++ b/pixelstats/include/pixelstats/DisplayStatsReporter.h
@@ -41,6 +41,8 @@
DISP_PANEL_STATE = 0,
DISP_PORT_STATE,
HDCP_STATE,
+ DISP_PORT_DSC_STATE,
+ DISP_PORT_MAX_RES_STATE,
};
void logDisplayStats(const std::shared_ptr<IStats> &stats_client,
const std::vector<std::string> &display_stats_paths,
@@ -116,6 +118,26 @@
const std::vector<std::string> &hdcp_stats_paths);
bool captureHDCPAuthTypeStats(const std::vector<std::string> &hdcp_stats_paths,
int64_t *cur_data);
+
+ /* displayport FEC/DSC state */
+ /* Set the number of paths needed to be collected */
+ static constexpr int DISPLAY_PORT_DSC_STATS_SIZE = 2;
+
+ int64_t prev_dp_dsc_data_[DISPLAY_PORT_DSC_STATS_SIZE] = {0};
+ void logDisplayPortFECDSCStats(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_fecdsc_stats_paths);
+ bool captureDisplayPortFECDSCStats(
+ const std::vector<std::string> &displayport_fecdsc_stats_paths, int64_t *cur_data);
+
+ /* displayport maximum resolution state */
+ /* Set the number of paths needed to be collected */
+ static constexpr int DISPLAY_PORT_MAX_RES_STATS_SIZE = 11;
+
+ int64_t prev_dp_max_res_data_[DISPLAY_PORT_MAX_RES_STATS_SIZE] = {0};
+ void logDisplayPortMaxResStats(const std::shared_ptr<IStats> &stats_client,
+ const std::vector<std::string> &displayport_max_res_stats_paths);
+ bool captureDisplayPortMaxResStats(
+ const std::vector<std::string> &displayport_max_res_stats_paths, int64_t *cur_data);
};
} // namespace pixel
diff --git a/pixelstats/include/pixelstats/MmMetricsReporter.h b/pixelstats/include/pixelstats/MmMetricsReporter.h
index dc2f242..dcef6b2 100644
--- a/pixelstats/include/pixelstats/MmMetricsReporter.h
+++ b/pixelstats/include/pixelstats/MmMetricsReporter.h
@@ -36,13 +36,59 @@
*/
class MmMetricsReporter {
public:
+ // Define the enum based on the group range names
+ enum OomScoreAdjGroup : int32_t {
+ OOMR_950 = 0,
+ OOMR_900,
+ OOMR_850,
+ OOMR_800,
+ OOMR_750,
+ OOMR_700,
+ OOMR_650,
+ OOMR_600,
+ OOMR_550,
+ OOMR_500,
+ OOMR_450,
+ OOMR_400,
+ OOMR_350,
+ OOMR_300,
+ OOMR_250,
+ OOMR_200,
+ OOMS_200,
+ OOMR_150,
+ OOMR_100,
+ OOMR_050,
+ OOMR_000,
+ OOMS_000,
+ OOMR_NEGATIVE,
+ OOM_NUM_OF_GROUPS,
+ };
+
+ struct OomGroupMemUsage {
+ OomScoreAdjGroup oom_group; // the diemsion field
+ int64_t nr_task;
+ int64_t file_rss_kb;
+ int64_t anon_rss_kb;
+ int64_t pgtable_kb;
+ int64_t swap_ents_kb;
+ int64_t shmem_rss_kb;
+ };
+
MmMetricsReporter();
void aggregatePixelMmMetricsPer5Min();
void logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client);
void logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client);
+ void logGcmaPerDay(const std::shared_ptr<IStats> &stats_client);
+ void logGcmaPerHour(const std::shared_ptr<IStats> &stats_client);
+ void logMmProcessUsageByOomGroupSnapshot(const std::shared_ptr<IStats> &stats_client);
void logCmaStatus(const std::shared_ptr<IStats> &stats_client);
std::vector<VendorAtomValue> genPixelMmMetricsPerHour();
std::vector<VendorAtomValue> genPixelMmMetricsPerDay();
+ bool readMmProcessUsageByOomGroup(std::vector<OomGroupMemUsage> *ogusage);
+ std::vector<VendorAtomValue> genMmProcessUsageByOomGroupSnapshotAtom(
+ const OomGroupMemUsage &data);
+ std::vector<VendorAtomValue> readAndGenGcmaPerHour();
+ std::vector<VendorAtomValue> readAndGenGcmaPerDay();
virtual ~MmMetricsReporter() {}
private:
@@ -119,8 +165,12 @@
kPsiNumAllUploadTotalMetrics + kPsiNumAllUploadAvgMetrics;
bool checkKernelMMMetricSupport();
+ bool checkKernelOomUsageSupport();
+ bool checkKernelGcmaSupport();
bool MmMetricsSupported() { return ker_mm_metrics_support_; }
+ bool OomUsageSupoorted() { return ker_oom_usage_support_; }
+ bool GcmaSupported() { return ker_gcma_support_; }
bool ReadFileToUint(const std::string &path, uint64_t *val);
bool reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
@@ -167,7 +217,12 @@
int cma_name_offset, const std::vector<MmMetricsInfo> &metrics_info,
std::map<std::string, std::map<std::string, uint64_t>> *all_prev_cma_stat);
+ std::optional<OomGroupMemUsage> parseMmProcessUsageByOomGroupLine(const std::string &line);
+ bool readMmProcessUsageByOomGroupFile(const std::string &path,
+ std::vector<OomGroupMemUsage> *ogusage, int32_t *m_uid);
+
// test code could override this to inject test data
+ // though named 'Sysfs', it can be applied to proc fs
virtual std::string getSysfsPath(const std::string &path) { return path; }
const char *const kVmstatPath;
@@ -179,6 +234,28 @@
const char *const kPixelStatMm;
const char *const kMeminfoPath;
const char *const kProcStatPath;
+ const char *const kProcVendorMmUsageByOom;
+ const char *const kGcmaBasePath;
+
+ // GCMA hourly metrics
+ const char *const kGcmaCached = "cached";
+
+ // GCMA hourly 1/2
+ const char *const kGcmaHourlySimpleKnobs[4] = {
+ "discarded",
+ "evicted",
+ "loaded",
+ "stored",
+ };
+
+ // GCMA hourly 2/2
+ const char *const kGcmaHourlyHistogramKnobs[4] = {
+ "latency_low",
+ "latency_mid",
+ "latency_high",
+ "latency_extreme_high",
+ };
+
// Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
// store everything in the values array at the index of the field number
// -2.
@@ -202,7 +279,10 @@
int prev_kcompactd_pid_ = -1;
uint64_t prev_kswapd_stime_ = 0;
uint64_t prev_kcompactd_stime_ = 0;
+ int32_t oom_usage_uid_ = 0;
bool ker_mm_metrics_support_;
+ bool ker_oom_usage_support_;
+ bool ker_gcma_support_;
};
} // namespace pixel
diff --git a/pixelstats/include/pixelstats/StatsHelper.h b/pixelstats/include/pixelstats/StatsHelper.h
index 70fd4e3..db345c1 100644
--- a/pixelstats/include/pixelstats/StatsHelper.h
+++ b/pixelstats/include/pixelstats/StatsHelper.h
@@ -42,7 +42,7 @@
enum ReportEventFormat {
FormatAddrWithVal,
FormatIgnoreAddr,
- FormatNoAddr,
+ FormatOnlyVal,
};
void reportSpeakerImpedance(const std::shared_ptr<IStats> &stats_client,
@@ -70,11 +70,8 @@
const PixelAtoms::VendorUsbDataSessionEvent &usb_session);
void readLogbuffer(const std::string &buf_path, int num_fields, uint16_t code,
enum ReportEventFormat format, unsigned int last_check_time,
- std::vector<std::vector<uint16_t>> &events);
+ std::vector<std::vector<uint32_t>> &events);
-void readLogbuffer(const std::string &buf_path, int num_fields, const char *code,
- enum ReportEventFormat format, unsigned int last_check_time,
- std::vector<std::vector<uint16_t>> &events);
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h
index 42a4b6f..15d50dd 100644
--- a/pixelstats/include/pixelstats/SysfsCollector.h
+++ b/pixelstats/include/pixelstats/SysfsCollector.h
@@ -75,6 +75,8 @@
const std::vector<std::string> ThermalStatsPaths;
const std::vector<std::string> DisplayStatsPaths;
const std::vector<std::string> DisplayPortStatsPaths;
+ const std::vector<std::string> DisplayPortDSCStatsPaths;
+ const std::vector<std::string> DisplayPortMaxResolutionStatsPaths;
const std::vector<std::string> HDCPStatsPaths;
const char *const CCARatePath;
const std::vector<std::pair<std::string, std::string>> TempResidencyAndResetPaths;
@@ -137,6 +139,8 @@
void logMitigationDurationCounts(const std::shared_ptr<IStats> &stats_client);
void logDisplayStats(const std::shared_ptr<IStats> &stats_client);
void logDisplayPortStats(const std::shared_ptr<IStats> &stats_client);
+ void logDisplayPortDSCStats(const std::shared_ptr<IStats> &stats_client);
+ void logDisplayPortMaxResolutionStats(const std::shared_ptr<IStats> &stats_client);
void logHDCPStats(const std::shared_ptr<IStats> &stats_client);
void logVendorAudioPdmStatsReported(const std::shared_ptr<IStats> &stats_client);
@@ -199,6 +203,8 @@
const char *const kWifiPcieLinkStatsPath;
const std::vector<std::string> kDisplayStatsPaths;
const std::vector<std::string> kDisplayPortStatsPaths;
+ const std::vector<std::string> kDisplayPortDSCStatsPaths;
+ const std::vector<std::string> kDisplayPortMaxResolutionStatsPaths;
const std::vector<std::string> kHDCPStatsPaths;
const char *const kPDMStatePath;
const char *const kWavesPath;
diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h
index 3768218..a9e87cb 100644
--- a/pixelstats/include/pixelstats/UeventListener.h
+++ b/pixelstats/include/pixelstats/UeventListener.h
@@ -164,9 +164,11 @@
GpuEvent_GpuEventInfo_MALI_PMODE_ENTRY_FAILURE},
{"GPU_PAGE_FAULT",
PixelAtoms::GpuEvent::GpuEventInfo::GpuEvent_GpuEventInfo_MALI_GPU_PAGE_FAULT},
- {"MMU_AS_ACTIVE_STUCK",
+ {"MMU_AS_ACTIVE_STUCK", PixelAtoms::GpuEvent::GpuEventInfo::
+ GpuEvent_GpuEventInfo_MALI_MMU_AS_ACTIVE_STUCK},
+ {"TRACE_BUF_INVALID_SLOT",
PixelAtoms::GpuEvent::GpuEventInfo::
- GpuEvent_GpuEventInfo_MALI_MMU_AS_ACTIVE_STUCK}};
+ GpuEvent_GpuEventInfo_MALI_TRACE_BUF_INVALID_SLOT}};
const std::unordered_map<std::string,
PixelAtoms::ThermalSensorAbnormalityDetected::AbnormalityType>
diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto
index 21bfbbf..eb30e5d 100644
--- a/pixelstats/pixelatoms.proto
+++ b/pixelstats/pixelatoms.proto
@@ -25,7 +25,7 @@
option java_outer_classname = "PixelAtoms";
import "frameworks/proto_logging/stats/atom_field_options.proto";
-import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
+import "frameworks/proto_logging/stats/enums/app_shared/app_enums.proto";
/*
* Please note that the following features are not currently supported by
@@ -57,7 +57,7 @@
PixelMmMetricsPerDay pixel_mm_metrics_per_day = 105016;
F2fsCompressionInfo f2fs_compression_info = 105017;
VendorChargeCycles vendor_charge_cycles = 105018; // moved from atoms.proto
- VendorHardwareFailed vendor_hardware_failed = 105019; // moved from atoms.proto
+ VendorHardwareFailed vendor_hardware_failed = 105019 [(android.os.statsd.module) = "pixelaudio"]; // moved from atoms.proto
VendorSlowIo vendor_slow_io = 105020; // moved from atoms.proto
VendorSpeechDspStat vendor_speech_dsp_stat = 105021; // moved from atoms.proto
VendorPhysicalDropDetected vendor_physical_drop_detected =
@@ -74,7 +74,7 @@
CitadelVersion citadel_version = 100018; // moved from vendor proprietary
CitadelEvent citadel_event = 100019; // moved from vendor proprietary
- VendorSpeakerStatsReported vendor_speaker_stats_reported = 105030;
+ VendorSpeakerStatsReported vendor_speaker_stats_reported = 105030 [(android.os.statsd.module) = "pixelaudio"];
ChreHalNanoappLoadFailed chre_hal_nanoapp_load_failed =
105031 [(android.os.statsd.module) = "chre"];
@@ -91,7 +91,7 @@
BatteryHealthUsage battery_health_usage = 105038;
F2fsSmartIdleMaintEnabledStateChanged f2fs_smart_idle_maint_enabled_state_changed = 105039;
BlockStatsReported block_stats_reported = 105040;
- VendorAudioHardwareStatsReported vendor_audio_hardware_stats_reported = 105041;
+ VendorAudioHardwareStatsReported vendor_audio_hardware_stats_reported = 105041 [(android.os.statsd.module) = "pixelaudio"];
ThermalDfsStats thermal_dfs_stats = 105042;
VendorLongIRQStatsReported vendor_long_irq_stats_reported = 105043;
@@ -101,22 +101,22 @@
PcieLinkStatsReported pcie_link_stats = 105047;
VendorSensorCoolingDeviceStats vendor_sensor_cooling_device_stats = 105048;
- VibratorPlaycountReported vibrator_playcount_reported = 105049;
- VibratorLatencyReported vibrator_latency_reported = 105050;
- VibratorErrorsReported vibrator_errors_reported = 105051;
+ VibratorPlaycountReported vibrator_playcount_reported = 105049 [(android.os.statsd.module) = "vibrator"];
+ VibratorLatencyReported vibrator_latency_reported = 105050 [(android.os.statsd.module) = "vibrator"];
+ VibratorErrorsReported vibrator_errors_reported = 105051 [(android.os.statsd.module) = "vibrator"];
F2fsAtomicWriteInfo f2fs_atomic_write_info = 105052;
PartitionsUsedSpaceReported partition_used_space_reported = 105053;
PowerMitigationDurationCounts mitigation_duration = 105054; // moved from atoms.proto
DisplayPanelErrorStats display_panel_error_stats = 105055;
VendorAudioPdmStatsReported vendor_audio_pdm_stats_reported = 105056;
- VendorAudioThirdPartyEffectStatsReported vendor_audio_third_party_effect_stats_reported = 105057;
+ VendorAudioThirdPartyEffectStatsReported vendor_audio_third_party_effect_stats_reported = 105057 [(android.os.statsd.module) = "pixelaudio"];
VendorAudioAdaptedInfoStatsReported vendor_audio_adapted_info_stats_reported = 105058;
GpuEvent gpu_event = 105059;
- VendorAudioPcmStatsReported vendor_audio_pcm_stats_reported = 105060;
+ VendorAudioPcmStatsReported vendor_audio_pcm_stats_reported = 105060 [(android.os.statsd.module) = "pixelaudio"];
VendorUsbDataSessionEvent vendor_usb_data_session_event = 105061;
ThermalSensorAbnormalityDetected thermal_sensor_abnormality_detected = 105062;
VendorAudioOffloadedEffectStatsReported vendor_audio_offloaded_effect_stats_reported = 105063;
- VendorAudioBtMediaStatsReported vendor_audio_bt_media_stats_reported = 105064;
+ VendorAudioBtMediaStatsReported vendor_audio_bt_media_stats_reported = 105064 [(android.os.statsd.module) = "pixelaudio"];
PixelImpulseUsageReported pixel_impulse_usage_reported = 105065;
DisplayPortErrorStats display_port_error_stats = 105066;
HDCPAuthTypeStats hdcp_auth_type_stats = 105067;
@@ -129,6 +129,16 @@
BatteryTimeToFullStatsReported battery_time_to_full_stats_reported = 105074;
VendorAudioDirectUsbAccessUsageStats vendor_audio_direct_usb_access_usage_stats = 105075 [(android.os.statsd.module) = "pixelaudio"];
VendorAudioUsbConfigStats vendor_audio_usb_config_stats = 105076 [(android.os.statsd.module) = "pixelaudio"];
+ GpuFrozenAppsMemoryPerUid gpu_frozen_apps_memory_per_uid = 105078;
+ RepairModeEntered repair_mode_entered = 105079;
+ RepairModeExited repair_mode_exited = 105080;
+ RepairModeLowStorageReported repair_mode_low_storage_reported = 105081;
+ RepairModeErrorReported repair_mode_error_reported = 105082;
+ DisplayPortDSCSupportCountStatsReported display_port_dsc_support_stats = 105083;
+ DisplayPortMaxResolutionCountStatsReported display_port_max_resolution_stats = 105084;
+ VendorAudioDspRecordUsageStatsReported vendor_audio_dsp_record_usage_stats_reported = 105085 [(android.os.statsd.module) = "pixelaudio"];
+ VendorAudioUsbConnectionState vendor_audio_usb_connection_state = 105086 [(android.os.statsd.module) = "pixelaudio"];
+ VendorAudioSpeakerPowerStatsReported vendor_audio_speaker_power_stats_reported = 105087 [(android.os.statsd.module) = "pixelaudio"];
}
// AOSP atom ID range ends at 109999
reserved 109997; // reserved for VtsVendorAtomJavaTest test atom
@@ -802,7 +812,14 @@
FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5;
DEGRADE = 6;
}
- optional int32 failure_code = 4;
+ optional HardwareErrorCode failure_code = 4;
+
+ enum EventType {
+ UNKNOWN_EVENT = 0;
+ VOICE_CALL = 1;
+ VOIP_CALL = 2;
+ }
+ optional EventType event_type = 5;
}
/**
@@ -1378,6 +1395,15 @@
/* cca_enable: UI enable & algorithm is inactive (C2 or C4) */
optional int32 cca_enable_count_per_day = 8;
+
+ /* version: version of the data. */
+ optional int32 version = 9;
+
+ /* duration: duration in second of the voice/voip call. */
+ optional int32 duration_second = 10;
+
+ /* band: band value. */
+ optional int32 band = 11;
}
/**
@@ -1887,6 +1913,7 @@
MALI_PMODE_ENTRY_FAILURE = 21;
MALI_GPU_PAGE_FAULT = 22;
MALI_MMU_AS_ACTIVE_STUCK = 23;
+ MALI_TRACE_BUF_INVALID_SLOT = 24;
}
/* Vendor reverse domain name (expecting "com.google.pixel"). */
@@ -2196,6 +2223,8 @@
INTERNAL_CLIENT_LISTENER_ADD = 10004;
INTERNAL_CLIENT_LISTENER_REMOVE = 10005;
INTERNAL_WAIT = 10006;
+ INTERNAL_COROUTINE_ENQUEUE = 10007;
+ INTERNAL_COROUTINE_RUN = 10008;
}
/* Invoked API name */
optional ApiName api_name = 4;
@@ -2203,6 +2232,9 @@
enum Tag {
TAG_UNKNOWN = 0;
TAG_TEMPERATURE_READ_DELAY = 1;
+ TAG_SKIN_TEMPERATURE = 2;
+ TAG_BUSINESS_SCOPE = 3;
+ TAG_NON_BUSINESS_SCOPE = 4;
}
/* Tag for debugging purpose */
optional Tag tag = 5;
@@ -2260,6 +2292,12 @@
*/
/* Used when state_source == STATE_SOURCE_UID_IMPORTANCE */
optional android.app.Importance uid_importance_cut_point = 15;
+
+ /* Expected value for temperature delta in Celsius */
+ optional float expected_temperature_celsius = 16;
+
+ /* Actual value for temperature delta in Celsius */
+ optional float actual_temperature_celsius = 17;
}
/**
@@ -2579,3 +2617,377 @@
/* Duration in second */
optional int32 duration_second = 7;
};
+
+/* GPU memory allocation information for frozen apps */
+message GpuFrozenAppsMemoryPerUid {
+ /* Vendor reverse domain name (expecting "com.google.pixel"). */
+ optional string reverse_domain_name = 1;
+
+ /* UID of the frozen app. */
+ optional int32 uid = 2 [(android.os.statsd.is_uid) = true];
+
+ /* Total amount of GPU memory allocated by this app, in kilobytes. */
+ optional int64 gpu_memory_kb = 3;
+}
+
+/**
+ * Logs for repair mode enter
+ * Logged from:
+ * vendor/google/apps/RepairMode/
+ *
+ * Estimated Logging Rate:
+ * Peak: 5 times in 1 min | Avg: 3 times per device per year
+ */
+ message RepairModeEntered {
+ // Vendor reverse domain name (expecting "com.google.pixel").
+ optional string reverse_domain_name = 1;
+ // free storage size on device when entering repair mode in megabyte
+ optional int64 storage_size_mb = 2;
+}
+
+/**
+ * Logs for repair mode exit
+ * Logged from:
+ * vendor/google/apps/RepairMode/
+ *
+ * Estimated Logging Rate:
+ * Peak: 5 times in 1 min | Avg: 3 times per device per year
+ */
+message RepairModeExited {
+ // Vendor reverse domain name (expecting "com.google.pixel").
+ optional string reverse_domain_name = 1;
+ // free storage size on device when exiting repair mode in megabyte
+ optional int64 storage_size_mb = 2;
+ // whether diagnostic tool is executed during repair mode
+ // false if diagnostic tool is never run
+ // true if diagnostic is run once or more
+ optional bool is_diagnostic_run = 3;
+
+ // how user auth/verify the credential to exit repair mode
+ enum ExitMethod {
+ UNSPECIFIED = 0;
+ // auth by google account
+ GAUTH = 1;
+ // auth by screen lock on the device
+ SCREEN_LOCK = 2;
+ }
+ // method for auth when exiting repair mode
+ optional ExitMethod exit_method = 4;
+}
+
+/**
+ * Logs when a user cannot enter repair mode due to insufficient storage
+ * Logged from:
+ * vendor/google/apps/RepairMode/
+ *
+ * Estimated Logging Rate:
+ * Peak: 1 time in 5 mins | Avg: 20 times per device per year
+ */
+message RepairModeLowStorageReported {
+ // Vendor reverse domain name (expecting "com.google.pixel").
+ optional string reverse_domain_name = 1;
+ // free storage size on the device in megabyte
+ optional int64 storage_size_mb = 2;
+}
+
+/**
+ * Logs programmatic error that prevent users from entering repair mode
+ * Logged from:
+ * vendor/google/apps/RepairMode/
+ *
+ * Estimated Logging Rate:
+ * Peak: 1 time in 3 mins | Avg: 2 times per device per year
+ */
+message RepairModeErrorReported {
+ // Vendor reverse domain name (expecting "com.google.pixel").
+ optional string reverse_domain_name = 1;
+
+ // Error type that prevent user from entering repair mode
+ enum ErrorType {
+ UNSPECIFIED = 0;
+ // Dynamic system failed to install image
+ INSTALLED_FAILED = 1;
+ // Failed to enable Dynamic system
+ ENABLE_DYN_FAILED = 2;
+ // Failed to reboot
+ REBOOT_FAILED = 3;
+ }
+
+ optional ErrorType error_type = 2;
+}
+
+/*
+ * Log if a device is plugged into a display that
+ * supports forward error correction (FEC) and
+ * display stream compression (DSC)
+ */
+message DisplayPortDSCSupportCountStatsReported{
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* Counts of connections where FEC/DSC is
+ * supported or not
+ */
+ optional int32 fec_dsc_supported = 2;
+ optional int32 fec_dsc_not_supported = 3;
+}
+
+/*
+* A message containing the use counts of various maximum
+* resolutions the displays plugged into the phone use.
+*/
+message DisplayPortMaxResolutionCountStatsReported{
+
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ /* Other Resolutions that don't fit into the current list */
+ optional int32 max_res_other = 2;
+
+ /* Predefined Resolutions */
+ optional int32 max_res_1366_768 = 3;
+ optional int32 max_res_1440_900 = 4;
+ optional int32 max_res_1600_900 = 5;
+ optional int32 max_res_1920_1080 = 6;
+ optional int32 max_res_2560_1080 = 7;
+ optional int32 max_res_2560_1440 = 8;
+ optional int32 max_res_3440_1440 = 9;
+ optional int32 max_res_3840_2160 = 10;
+ optional int32 max_res_5120_2880 = 11;
+ optional int32 max_res_7680_4320 = 12;
+}
+
+/*
+ * A message containing recording usage event.
+ * Logged from:
+ * vendor/google/whitechapel/audio/hal/aidl/audio/metric/suez_data_adapter/statsd_suez_data_adapter.cc
+ *
+ * Estimated Logging Rate: Any time during audio recording that screen_orientation / audio device / use case changes.
+ * It will be aggregated in a count and value metric to keep the resource usage low.
+ */
+message VendorAudioDspRecordUsageStatsReported {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ enum Type {
+ UNKNOWN = 0;
+ UC_AUDIO_RECORD = 1;
+ UC_LOW_LATENCY_AUDIO_RECORD = 2;
+ UC_MMAP_RECORD = 3;
+ IN_HANDSET_MIC = 4;
+ IN_HANDSET_DUAL_MIC = 5;
+ IN_HANDSET_TRIPLE_MIC = 6;
+ IN_CAMCORDER_LANDSCAPE = 7;
+ IN_CAMCORDER_INVERT_LANDSCAPE = 8;
+ IN_CAMCORDER_PORTRAIT = 9;
+ IN_CAMCORDER_SELFIE_LANDSCAPE = 10;
+ IN_CAMCORDER_SELFIE_INVERT_LANDSCAPE = 11;
+ IN_CAMCORDER_SELFIE_PORTRAIT = 12;
+ IN_CAMCORDER_MIC = 13;
+ IN_CAMCORDER_TIRPLE_MIC = 14;
+ CUSTOM_IN_PCM1 = 15;
+ CUSTOM_IN_PCM2 = 16;
+ CUSTOM_IN_PCM3 = 17;
+ CUSTOM_IN_PCM4 = 18;
+ CUSTOM_IN_PCM5 = 19;
+ }
+
+ /* Audio Device Interface. */
+ enum AudioDeviceInterface {
+ UNKNOWN_DEVICE_INTERFACE = 0;
+
+ // Built-in speakers
+ SPEAKER = 1;
+ SPEAKER_EARPIECE = 2;
+ SPEAKER_SAFE = 3;
+
+ // Built-in microphones
+ MICROPHONES = 4;
+ BACK_MICROPHONES = 5;
+ // internal used microphones
+ ULTRASOUND_MICROPHONES = 6;
+ SOUND_TRIGGER_MICROPHONES = 7;
+
+ // BT SCO
+ BLUETOOTH_SCO_DEFAULT = 8;
+ BLUETOOTH_SCO_HEADSET = 9;
+ BLUETOOTH_SCO_CAR_KIT = 10;
+ BLUETOOTH_SCO_HEADSET_MICROPHONES = 11;
+
+ // BT A2DP
+ BLUETOOTH_A2DP_DEVICE = 12;
+ BLUETOOTH_A2DP_SPEAKER = 13;
+ BLUETOOTH_A2DP_HEADPHONE = 14;
+
+ // BT low energy (BLE)
+ BLUETOOTH_LOW_ENERGY_SPEAKER = 15;
+ BLUETOOTH_LOW_ENERGY_HEADSET = 16;
+ BLUETOOTH_LOW_ENERGY_BROADCAST = 17;
+ BLUETOOTH_LOW_ENERGY_HEADSET_MICROPHONES = 18;
+
+ // USB
+ USB_DEVICE = 19;
+ USB_HEADSET = 20;
+ USB_DOCK = 21;
+ USB_DEVICE_MICROPHONES = 22;
+ USB_HEADSET_MICROPHONES = 23;
+ USB_DOCK_MICROPHONES = 24;
+
+ // HDMI
+ HDMI_DEVICE = 25;
+
+ // Telephony
+ TELEPHONY_TX = 26;
+ TELEPHONY_RX = 27;
+ IN_CALL_CAPTURE_SOURCE0 = 28;
+ IN_CALL_CAPTURE_SOURCE1 = 29;
+ IN_CALL_CAPTURE_SOURCE2 = 30;
+
+ // Null sink and source
+ NULL_SOURCE = 31;
+ NULL_SINK = 32;
+
+ // Echo reference
+ ECHO_REFERENCE_DEVICE_INTERFACE = 33;
+ }
+
+ /* Audio Use Case. */
+ enum UseCase {
+ UNKNOWN_VENDOR_AUDIO_USECASE = 0;
+ // playback use cases
+ PRIMARY_PLAYBACK = 1;
+ RAW_PLAYBACK = 2;
+ DEEP_BUFFER_PLAYBACK = 3;
+ COMPRESS_OFFLOAD_PLAYBACK = 4;
+ MMAP_PLAYBACK = 5;
+ HIFI_PLAYBACK = 6;
+ VOIP_PLAYBACK = 7;
+ TELEPHONY_PLAYBACK = 8;
+ IN_CALL_PLAYBACK = 9;
+ SPATIALIZER_PLAYBACK = 10;
+ ULTRASOUND_PLAYBACK = 11;
+ HAPTIC_PLAYBACK = 12;
+ SPATIALIZER_OFFLOAD_PLAYBACK = 13;
+ // capture use cases
+ PRIMARY_CAPTURE = 14;
+ FAST_CAPTURE = 15;
+ HIFI_CAPTURE = 16;
+ MMAP_CAPTURE = 17;
+ VOIP_CAPTURE = 18;
+ VOIP_GSENET_CAPTURE = 19;
+ ULTRASOUND_CAPTURE = 20;
+ TELEPHONY_CAPTURE = 21;
+ IN_CALL_CAPTURE = 22;
+ SOUND_TRIGGER_CAPTURE = 23;
+ SOUND_TRIGGER_TAP_CAPTURE = 24;
+ HOTWORD_LOOKBACK_CAPTURE = 25;
+ ECHO_REFERENCE_CAPTURE = 26;
+
+ // voice call use case
+ VOICE_CALL_DOWNLINK = 27;
+ VOICE_CALL_UPLINK = 28;
+ }
+
+ /* Audio source with the original enum value. */
+ enum AudioSource {
+ DEFAULT = 0;
+ MIC = 1;
+ VOICE_UPLINK = 2;
+ VOICE_DOWNLINK = 3;
+ VOICE_CALL = 4;
+ CAMCORDER = 5;
+ VOICE_RECOGNITION = 6;
+ VOICE_COMMUNICATION = 7;
+ REMOTE_SUBMIX = 8;
+ UNPROCESSED = 9;
+ VOICE_PERFORMANCE = 10;
+ ECHO_REFERENCE = 1997;
+ FM_TUNER = 1998;
+ HOTWORD = 1999;
+ ULTRASOUND = 2000;
+ };
+
+ enum CameraType {
+ UNKNOWN_CAMERA_TYPE = 0;
+ FRONT_CAMERA = 1;
+ BACK_CAMERA = 2;
+ }
+
+ /* Type of Backend used in recording */
+ optional Type type = 2 [deprecated = true];
+
+ /* Duration in second */
+ optional int32 duration_second = 3;
+
+ optional AudioSource audio_source = 4;
+
+ /* Device interface used */
+ optional AudioDeviceInterface audio_device_interface = 5;
+
+ /* Usecase used */
+ optional UseCase vendor_audio_use_case = 6;
+
+ /* Camera Type */
+ optional CameraType camera_type = 7;
+
+ /* Screen orientation used. */
+ optional int32 screen_orientation = 8;
+
+ /* True if this atom represent the beginning of recording. If usecase/interfaces/orientation
+ * changes mid-recording, new atom will be uploaded but this value will be false.
+ */
+ optional bool is_beginning_of_recording = 9;
+};
+
+/*
+ * A message containing USB audio connection error event.
+ * Logged from:
+ * vendor/google/whitechapel/audio/hal/aidl/audio/metric/suez_data_adapter/statsd_suez_data_adapter.cc
+ *
+ * Estimated Logging Rate: Very low, around once a month per user.
+ */
+message VendorAudioUsbConnectionState {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+
+ enum ConnectionState {
+ UNKNOWN_CONNECTION_STATE = 0;
+ FAILED_TO_READ_CARD_ID_PCM_ID = 1;
+ FAILED_TO_READ_USB_ID = 2;
+ FAILED_TO_READ_BUS_ID_DEVICE_ID = 3;
+ FAILED_TO_ADD_NEW_DEVICE = 4;
+ FAILED_TO_PARSE_USB_CAPABILITY = 5;
+ FAILED_TO_PARSE_USB_CAPABILITY_IS_EMPTY = 6;
+ FAILED_TO_ADD_NEW_DEVICE_CAPABILITY = 7;
+ FAILED_TO_ADD_ALREADY_CONNECTED_PORT_ID = 8;
+ CONNECTION_SUCCESS = 9;
+ }
+
+ enum DisconnectionState {
+ UNKNOWN_DISCONNECTION_STATE = 0;
+ FAILED_PORT_ID_NOT_CONNECTED = 1;
+ DISCONNECTION_SUCCESS = 2;
+ }
+
+ /* Connection State. UNKNOWN_CONNECTION_STATE in disconnection event. */
+ optional ConnectionState connection_error = 2;
+
+ /* Disconnection State. UNKNOWN_DISCONNECTION_STATE in connection event. */
+ optional DisconnectionState disconnection_error = 3;
+};
+
+/*
+ * Logs the Audio Speaker Power information stats.
+ * Logged from:
+ * vendor/google/whitechapel/audio/hal/aidl/audio/metric/suez_data_adapter/statsd_suez_data_adapter.cc
+ *
+ * Estimated Logging Rate: Once per audio playback through speaker.
+ */
+message VendorAudioSpeakerPowerStatsReported {
+ /* Vendor reverse domain name */
+ optional string reverse_domain_name = 1;
+ /* The average power of the speaker. i-th value represent i-th speaker. There are at most 4 speakers. */
+ repeated float average_power = 2;
+ /* Duration in second that speaker is using the average power. i-th value represent i-th speaker. There are at most 4 speakers. */
+ repeated int32 duration_second = 3;
+}
diff --git a/pixelstats/test/mm/MmMetricsGoldenAtomFieldTypes.h b/pixelstats/test/mm/MmMetricsGoldenAtomFieldTypes.h
index 549e117..8e281f6 100644
--- a/pixelstats/test/mm/MmMetricsGoldenAtomFieldTypes.h
+++ b/pixelstats/test/mm/MmMetricsGoldenAtomFieldTypes.h
@@ -156,6 +156,37 @@
longValue, // optional int64 cpu_io_wait_time_cs = 63;
longValue, // optional int64 kswapd_pageout_run = 64;
};
+
+const int MmMetricsOomGroupMemUsage_field_types[]{
+ intValue, // metric_id
+ intValue, // oom_group
+ longValue, // nr_task
+ longValue, // file_rss_kb
+ longValue, // anon_rss_kb
+ longValue, // pgtable_kb
+ longValue, // swap_ents_kb
+ longValue, // shmem_rss_kb
+};
+
+const int MmMetricsGcmaPerHour_field_types[]{
+ longValue, // GCMA.cached
+};
+
+const int MmMetricsGcmaPerDaySimple_field_types[]{
+ longValue, // GCMA.disarded
+ longValue, // GCMA.evicted
+ longValue, // GCMA.loaded
+ longValue, // GCMA.stored
+ repeatedLongValue, // GCMA repeated type (array of histograms)
+};
+
+const int MmMetricsGcmaPerDayHistogram_field_types[]{
+ longValue, // GCMA.latency_low
+ longValue, // GCMA.latency_mid
+ longValue, // GCMA.latency_high
+ longValue, // GCMA.latency_extreme_high
+};
+
} // namespace mm_metrics_atom_field_test_golden_results
} // namespace pixel
diff --git a/pixelstats/test/mm/MmMetricsGoldenResults.h b/pixelstats/test/mm/MmMetricsGoldenResults.h
index 9bcf300..d7e192c 100644
--- a/pixelstats/test/mm/MmMetricsGoldenResults.h
+++ b/pixelstats/test/mm/MmMetricsGoldenResults.h
@@ -177,6 +177,70 @@
1126601,
// clang-format on
};
+
+const uint64_t MmMetricsOomGroupMemUsage_golden[2][23][7]{
+ // clang-format off
+ {
+ {0, 0, 102, 103, 104, 105, 106},
+ {1, 201, 0, 203, 204, 205, 206},
+ {2, 301, 302, 0, 304, 305, 306},
+ {3, 401, 402, 403, 0, 405, 406},
+ {4, 501, 502, 503, 504, 0, 506},
+ {5, 601, 602, 603, 604, 605, 0},
+ {6, 701, 702, 703, 704, 705, 706},
+ {7, 801, 802, 803, 804, 805, 806},
+ {8, 901, 902, 903, 904, 905, 906},
+ {9, 1001, 1002, 1003, 1004, 1005, 1006},
+ {10, 1101, 1102, 1103, 1104, 1105, 1106},
+ {11, 1201, 1202, 1203, 1204, 1205, 1206},
+ {12, 1301, 1302, 1303, 1304, 1305, 1306},
+ {13, 1401, 1402, 1403, 1404, 1405, 1406},
+ {14, 1501, 1502, 1503, 1504, 1505, 1506},
+ {15, 1601, 1602, 1603, 1604, 1605, 1606},
+ {16, 1701, 1702, 1703, 1704, 1705, 1706},
+ {17, 1801, 1802, 1803, 1804, 1805, 1806},
+ {18, 1901, 1902, 1903, 1904, 1905, 1906},
+ {19, 2001, 2002, 2003, 2004, 2005, 2006},
+ {20, 2101, 2102, 2103, 2104, 2105, 2106},
+ {21, 2201, 2202, 2203, 2204, 2205, 2206},
+ {22, 2301, 2302, 2303, 2304, 2305, 2306},
+ },
+ {
+ {0, 3101, 3102, 3103, 3104, 3105, 3106},
+ {1, 3201, 3202, 3203, 3204, 3205, 3206},
+ {2, 3301, 3302, 3303, 3304, 3305, 3306},
+ {3, 3401, 3402, 3403, 3404, 3405, 3406},
+ {4, 3501, 3502, 3503, 3504, 3505, 3506},
+ {5, 3601, 3602, 3603, 3604, 3605, 3606},
+ {6, 3701, 3702, 3703, 3704, 3705, 3706},
+ {7, 3801, 3802, 3803, 3804, 3805, 3806},
+ {8, 3901, 3902, 3903, 3904, 3905, 3906},
+ {9, 4001, 4002, 4003, 4004, 4005, 4006},
+ {10, 4101, 4102, 4103, 4104, 4105, 4106},
+ {11, 4201, 4202, 4203, 4204, 4205, 4206},
+ {12, 4301, 4302, 4303, 4304, 4305, 4306},
+ {13, 4401, 4402, 4403, 4404, 4405, 4406},
+ {14, 4501, 4502, 4503, 4504, 4505, 4506},
+ {15, 4601, 4602, 4603, 4604, 4605, 4606},
+ {16, 4701, 4702, 4703, 4704, 4705, 4706},
+ {17, 4801, 4802, 4803, 4804, 4805, 4806},
+ {18, 4901, 4902, 4903, 4904, 4905, 4906},
+ {19, 5001, 5002, 5003, 5004, 5005, 5006},
+ {20, 5101, 5102, 5103, 5104, 5105, 5106},
+ {21, 5201, 5202, 5203, 5204, 5205, 5206},
+ {22, 5301, 5302, 5303, 5304, 5305, 5306},
+ }
+ // clang-format on
+};
+
+const uint64_t MmMetricsGcmaPerHour_golden[1] = {
+ 13,
+};
+
+const uint64_t MmMetricsGcmaPerDaySimple_golden[4] = {1, 2, 3, 4};
+
+const uint64_t MmMetricsGcmaPerDayHistogram_golden[4] = {5, 6, 7, 8};
+
} // namespace mm_metrics_reporter_test_golden_result
} // namespace pixel
diff --git a/pixelstats/test/mm/MmMetricsReporterTest.cpp b/pixelstats/test/mm/MmMetricsReporterTest.cpp
index c8d2f14..b5adb15 100644
--- a/pixelstats/test/mm/MmMetricsReporterTest.cpp
+++ b/pixelstats/test/mm/MmMetricsReporterTest.cpp
@@ -16,6 +16,8 @@
#include <gtest/gtest.h>
#include <pixelstats/MmMetricsReporter.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "MmMetricsGoldenAtomFieldTypes.h"
#include "MmMetricsGoldenResults.h"
@@ -31,8 +33,17 @@
namespace google {
namespace pixel {
+using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerDayHistogram_field_types;
+using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerDaySimple_field_types;
+using mm_metrics_atom_field_test_golden_results::MmMetricsGcmaPerHour_field_types;
+using mm_metrics_atom_field_test_golden_results::MmMetricsOomGroupMemUsage_field_types;
using mm_metrics_atom_field_test_golden_results::PixelMmMetricsPerDay_field_types;
using mm_metrics_atom_field_test_golden_results::PixelMmMetricsPerHour_field_types;
+
+using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerDayHistogram_golden;
+using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerDaySimple_golden;
+using mm_metrics_reporter_test_golden_result::MmMetricsGcmaPerHour_golden;
+using mm_metrics_reporter_test_golden_result::MmMetricsOomGroupMemUsage_golden;
using mm_metrics_reporter_test_golden_result::PixelMmMetricsPerDay_golden;
using mm_metrics_reporter_test_golden_result::PixelMmMetricsPerHour_golden;
@@ -132,6 +143,235 @@
}
}
+TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageSuccess) {
+ constexpr int kNumTests = 2;
+ MockMmMetricsReporter mreport;
+ const std::string data_path[kNumTests] = {
+ std::string(data_base_path) + "/test_data_0",
+ std::string(data_base_path) + "/test_data_1",
+ };
+ std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
+ int32_t og_metric_uid[kNumTests];
+ auto &golden = MmMetricsOomGroupMemUsage_golden;
+ auto &gold_ftype = MmMetricsOomGroupMemUsage_field_types;
+
+ constexpr int kNumFields = ARRAY_SIZE(MmMetricsOomGroupMemUsage_field_types);
+ constexpr int kNumLines = ARRAY_SIZE(golden[0]);
+
+ ASSERT_LT(kNumLines, 100);
+
+ // Check testcase consistency (if fail, the test case itself has some bug)
+ ASSERT_EQ(ARRAY_SIZE(golden), kNumTests);
+ ASSERT_EQ(ARRAY_SIZE(golden[1]), kNumLines);
+ ASSERT_EQ(ARRAY_SIZE(MmMetricsOomGroupMemUsage_field_types), kNumFields);
+
+ for (int i = 0; i < kNumTests; i++) {
+ for (int j = 0; j < kNumLines; j++) {
+ // golden result does not have UID field, which is date/time based unique ID.
+ ASSERT_EQ(ARRAY_SIZE(golden[i][j]), kNumFields - 1);
+ }
+ }
+
+ for (int test_iteration = 0; test_iteration < kNumTests; ++test_iteration) {
+ // setup
+ mreport.setBasePath(data_path[test_iteration]);
+
+ // --- start test ---
+ ASSERT_TRUE(mreport.readMmProcessUsageByOomGroup(&ogusage));
+ ASSERT_EQ(ogusage.size(), kNumLines);
+
+ int line = 0;
+ for (const auto &u : ogusage) {
+ std::vector<VendorAtomValue> values =
+ mreport.genMmProcessUsageByOomGroupSnapshotAtom(u);
+ int32_t &uid = og_metric_uid[test_iteration];
+
+ // check size
+ ASSERT_EQ(values.size(), kNumFields)
+ << "Size mismatch: test# " << test_iteration << " line " << line;
+
+ if (line == 0) {
+ uid = getVendorAtomIntValue(values[0]);
+ } else {
+ // check UID
+ EXPECT_EQ(getVendorAtomIntValue(values[0]), uid)
+ << "value mismatch: test# " << test_iteration << " line " << line
+ << " field 0";
+ }
+
+ for (int field = 1; field < kNumFields; ++field) {
+ // check types
+ EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_ftype[field])
+ << "type mismatch: test# " << test_iteration << " line " << line
+ << " field " << field;
+
+ if (static_cast<int>(values[field].getTag()) != gold_ftype[field])
+ continue; // no checking values when the type is already wrong.
+
+ // check values
+ EXPECT_EQ(getVendorAtomIntValue(values[field]),
+ golden[test_iteration][line][field - 1])
+ << "value mismatch: test# " << test_iteration << " line " << line
+ << " field " << field;
+ }
+ line++;
+ }
+ // --- end test ---
+ }
+
+ // metric_uid must be unique
+ EXPECT_NE(og_metric_uid[0], og_metric_uid[1]);
+}
+
+TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageFailFileNotFound) {
+ constexpr int kNumTests = 2;
+ MockMmMetricsReporter mreport;
+ const std::string data_path = std::string(data_base_path) + "/nonexisting_dir";
+ std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
+ int32_t uid;
+
+ // setup
+ mreport.setBasePath(data_path);
+
+ // --- start test ---
+ ASSERT_FALSE(mreport.readMmProcessUsageByOomGroup(&ogusage));
+ ASSERT_EQ(ogusage.size(), 0);
+}
+
+static bool file_exists(const char *const path) {
+ struct stat sbuf;
+
+ return (stat(path, &sbuf) == 0);
+}
+
+TEST(MmMetricsReporterTest, MmMetricsOomGroupMemUsageMultipleFailCases) {
+ constexpr int kNumTests = 8;
+ MockMmMetricsReporter mreport;
+ const std::string data_path[kNumTests] = {
+ std::string(data_base_path) + "/test_data_oom_usage_fail/1",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/2",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/3",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/4",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/5",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/6",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/7",
+ std::string(data_base_path) + "/test_data_oom_usage_fail/8",
+ };
+ const char *file = "oom_mm_usage";
+ std::vector<MmMetricsReporter::OomGroupMemUsage> ogusage;
+
+ for (int test_iteration = 0; test_iteration < kNumTests; ++test_iteration) {
+ // setup
+ mreport.setBasePath(data_path[test_iteration]);
+
+ // check file exist, otherwise it is testing "file not found" rather than the desired test
+ ASSERT_TRUE(file_exists((data_path[test_iteration] + "/" + file).c_str()));
+
+ // --- start test ---
+ ASSERT_FALSE(mreport.readMmProcessUsageByOomGroup(&ogusage))
+ << "Iteration " << test_iteration << ": test fail.";
+ ASSERT_EQ(ogusage.size(), 0) << "Iteration " << test_iteration << ": test fail.";
+ }
+}
+
+TEST(MmMetricsReporterTest, MmMetricsGcmaPerHourSuccess) {
+ MockMmMetricsReporter mreport;
+ const std::string data_path = std::string(data_base_path) + "/test_data_0";
+ auto &golden = MmMetricsGcmaPerHour_golden;
+ auto &gold_ftype = MmMetricsGcmaPerHour_field_types;
+
+ constexpr int kNumFields = ARRAY_SIZE(gold_ftype);
+ constexpr int kNumLines = ARRAY_SIZE(golden);
+
+ // Check testcase consistency (if fail, the test case itself has some bug)
+ ASSERT_EQ(kNumFields, kNumLines);
+
+ // setup
+ mreport.setBasePath(data_path);
+
+ // --- start test ---
+ std::vector<VendorAtomValue> values = mreport.readAndGenGcmaPerHour();
+
+ // check size
+ ASSERT_EQ(values.size(), kNumLines);
+
+ for (int field = 0; field < kNumFields; ++field) {
+ // check type
+ EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_ftype[field])
+ << "type mismatch @ field #" << field;
+
+ if (static_cast<int>(values[field].getTag()) != gold_ftype[field])
+ continue; // no checking the value when the type is wrong.
+
+ // check value
+ EXPECT_EQ(getVendorAtomIntValue(values[field]), golden[field])
+ << "value mismatch @ field #" << field;
+ }
+}
+
+TEST(MmMetricsReporterTest, MmMetricsGcmaPerDaySuccess) {
+ MockMmMetricsReporter mreport;
+ const std::string data_path = std::string(data_base_path) + "/test_data_0";
+ auto &golden_simple = MmMetricsGcmaPerDaySimple_golden;
+ auto &golden_histogram = MmMetricsGcmaPerDayHistogram_golden;
+
+ auto &gold_simple_ftype = MmMetricsGcmaPerDaySimple_field_types;
+ auto &gold_histogram_ftype = MmMetricsGcmaPerDayHistogram_field_types;
+
+ constexpr int kNumSimpleValues = 4;
+ constexpr int kNumHistogramValues = 4;
+ // total field num in atom values need to count the histogram array as one.
+ constexpr int kNumAtomValues = kNumSimpleValues + 1;
+
+ // Check testcase consistency (if fail, the test case itself has some bug)
+ ASSERT_EQ(ARRAY_SIZE(golden_simple), kNumSimpleValues);
+ ASSERT_EQ(ARRAY_SIZE(golden_histogram), kNumHistogramValues);
+ ASSERT_EQ(ARRAY_SIZE(gold_simple_ftype), kNumSimpleValues + 1); // count the last array type
+ ASSERT_EQ(ARRAY_SIZE(gold_histogram_ftype), kNumHistogramValues);
+
+ // setup
+ mreport.setBasePath(data_path);
+
+ // --- start test ---
+ std::vector<VendorAtomValue> values = mreport.readAndGenGcmaPerDay();
+
+ /*
+ * check size +1:
+ * Histogram in the form of a vector in the last element of 'Simple' value array.
+ */
+ ASSERT_EQ(values.size(), kNumAtomValues);
+
+ // check 'simple' values
+ for (int field = 0; field < kNumSimpleValues; ++field) {
+ // check type
+ EXPECT_EQ(static_cast<int>(values[field].getTag()), gold_simple_ftype[field])
+ << "type mismatch @ field #" << field;
+
+ if (static_cast<int>(values[field].getTag()) != gold_simple_ftype[field])
+ continue; // no checking the value when the type is wrong.
+
+ if (field == kNumAtomValues - 1)
+ continue; // same as break. The last one is an array, compare type only here.
+
+ EXPECT_EQ(getVendorAtomIntValue(values[field]), golden_simple[field])
+ << "value mismatch @ field #" << field;
+ }
+
+ // check array validity
+ auto &arrAtomValue = values[kNumAtomValues - 1];
+ const std::optional<std::vector<int64_t>> &repeatedLongValue =
+ arrAtomValue.get<VendorAtomValue::repeatedLongValue>();
+ ASSERT_TRUE(repeatedLongValue.has_value());
+
+ // check array size
+ ASSERT_EQ(repeatedLongValue.value().size(), kNumHistogramValues);
+
+ // check array values
+ for (int field = 0; field < kNumHistogramValues; ++field) {
+ EXPECT_EQ(repeatedLongValue.value()[field], golden_histogram[field]);
+ }
+}
+
} // namespace pixel
} // namespace google
} // namespace hardware
diff --git a/pixelstats/test/mm/MockMmMetricsReporter.h b/pixelstats/test/mm/MockMmMetricsReporter.h
index bdf67c1..25bddf3 100644
--- a/pixelstats/test/mm/MockMmMetricsReporter.h
+++ b/pixelstats/test/mm/MockMmMetricsReporter.h
@@ -82,10 +82,30 @@
{"/proc/pressure/memory", "psi_memory"},
{"kswapd0", "kswapd0_stat"},
{"kcompactd0", "kcompactd0_stat"},
+ {"/proc/vendor_mm/memory_usage_by_oom_score", "oom_mm_usage"},
+ {"/sys/kernel/vendor_mm/gcma/cached", "gcma_cached"},
+ {"/sys/kernel/vendor_mm/gcma/discarded", "gcma_discarded"},
+ {"/sys/kernel/vendor_mm/gcma/evicted", "gcma_evicted"},
+ {"/sys/kernel/vendor_mm/gcma/loaded", "gcma_loaded"},
+ {"/sys/kernel/vendor_mm/gcma/stored", "gcma_stored"},
+ {"/sys/kernel/vendor_mm/gcma/latency_low", "gcma_latency_low"},
+ {"/sys/kernel/vendor_mm/gcma/latency_mid", "gcma_latency_mid"},
+ {"/sys/kernel/vendor_mm/gcma/latency_high", "gcma_latency_high"},
+ {"/sys/kernel/vendor_mm/gcma/latency_extreme_high", "gcma_latency_extreme_high"},
};
virtual std::string getSysfsPath(const std::string &path) {
- return base_path_ + "/" + mock_path_map.at(path);
+ std::string ret(base_path_ + '/');
+ if (mock_path_map.find(path) == mock_path_map.end()) {
+ /*
+ * This mapped file won't exist in the test directory,
+ * so this effectively emulates a 'file-not-found' condition
+ * for testing the failed cases.
+ */
+ return ret + "not_found";
+ } else {
+ return ret + mock_path_map.at(path);
+ }
}
virtual std::string getProcessStatPath(const std::string &name, int *prev_pid) {
diff --git a/pixelstats/test/mm/VendorAtomIntValueUtil.h b/pixelstats/test/mm/VendorAtomIntValueUtil.h
index 1275112..6063df8 100644
--- a/pixelstats/test/mm/VendorAtomIntValueUtil.h
+++ b/pixelstats/test/mm/VendorAtomIntValueUtil.h
@@ -23,6 +23,7 @@
constexpr int longValue = static_cast<int>(VendorAtomValue::longValue);
constexpr int floatValue = static_cast<int>(VendorAtomValue::floatValue);
constexpr int stringValue = static_cast<int>(VendorAtomValue::stringValue);
+constexpr int repeatedLongValue = static_cast<int>(VendorAtomValue::repeatedLongValue);
static int64_t getVendorAtomIntValue(const VendorAtomValue &v) {
switch (v.getTag()) {
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_cached b/pixelstats/test/mm/data/test_data_0/gcma_cached
new file mode 100644
index 0000000..b1bd38b
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_cached
@@ -0,0 +1 @@
+13
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_discarded b/pixelstats/test/mm/data/test_data_0/gcma_discarded
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_discarded
@@ -0,0 +1 @@
+1
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_evicted b/pixelstats/test/mm/data/test_data_0/gcma_evicted
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_evicted
@@ -0,0 +1 @@
+2
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_latency_extreme_high b/pixelstats/test/mm/data/test_data_0/gcma_latency_extreme_high
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_latency_extreme_high
@@ -0,0 +1 @@
+8
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_latency_high b/pixelstats/test/mm/data/test_data_0/gcma_latency_high
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_latency_high
@@ -0,0 +1 @@
+7
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_latency_low b/pixelstats/test/mm/data/test_data_0/gcma_latency_low
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_latency_low
@@ -0,0 +1 @@
+5
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_latency_mid b/pixelstats/test/mm/data/test_data_0/gcma_latency_mid
new file mode 100644
index 0000000..1e8b314
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_latency_mid
@@ -0,0 +1 @@
+6
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_loaded b/pixelstats/test/mm/data/test_data_0/gcma_loaded
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_loaded
@@ -0,0 +1 @@
+3
diff --git a/pixelstats/test/mm/data/test_data_0/gcma_stored b/pixelstats/test/mm/data/test_data_0/gcma_stored
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/gcma_stored
@@ -0,0 +1 @@
+4
diff --git a/pixelstats/test/mm/data/test_data_0/oom_mm_usage b/pixelstats/test/mm/data/test_data_0/oom_mm_usage
new file mode 100644
index 0000000..2f72f52
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_0/oom_mm_usage
@@ -0,0 +1,25 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_1/oom_mm_usage b/pixelstats/test/mm/data/test_data_1/oom_mm_usage
new file mode 100644
index 0000000..f204e96
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_1/oom_mm_usage
@@ -0,0 +1,25 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 3101 3102 3103 3104 3105 3106
+[901,950] 3201 3202 3203 3204 3205 3206
+[851,900] 3301 3302 3303 3304 3305 3306
+[801,850] 3401 3402 3403 3404 3405 3406
+[751,800] 3501 3502 3503 3504 3505 3506
+[701,750] 3601 3602 3603 3604 3605 3606
+[651,700] 3701 3702 3703 3704 3705 3706
+[601,650] 3801 3802 3803 3804 3805 3806
+[551,600] 3901 3902 3903 3904 3905 3906
+[501,550] 4001 4002 4003 4004 4005 4006
+[451,500] 4101 4102 4103 4104 4105 4106
+[401,450] 4201 4202 4203 4204 4205 4206
+[351,400] 4301 4302 4303 4304 4305 4306
+[301,350] 4401 4402 4403 4404 4405 4406
+[251,300] 4501 4502 4503 4504 4505 4506
+[201,250] 4601 4602 4603 4604 4605 4606
+[200,200] 4701 4702 4703 4704 4705 4706
+[151,199] 4801 4802 4803 4804 4805 4806
+[101,150] 4901 4902 4903 4904 4905 4906
+[51,100] 5001 5002 5003 5004 5005 5006
+[1,50] 5101 5102 5103 5104 5105 5106
+[0,0] 5201 5202 5203 5204 5205 5206
+[-1000,-1] 5301 5302 5303 5304 5305 5306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/1/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/1/oom_mm_usage
new file mode 100644
index 0000000..332757c
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/1/oom_mm_usage
@@ -0,0 +1,26 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+# error line below: insufficient number of tokens
+[801,850] 401 402 403 0 405
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/2/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/2/oom_mm_usage
new file mode 100644
index 0000000..501f911
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/2/oom_mm_usage
@@ -0,0 +1,26 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+# Error line: No such range "[225,431]"
+[225,431] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/3/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/3/oom_mm_usage
new file mode 100644
index 0000000..41be842
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/3/oom_mm_usage
@@ -0,0 +1,26 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+# Error line: negative number
+[801,850] 401 402 403 -3 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/4/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/4/oom_mm_usage
new file mode 100644
index 0000000..3ae807b
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/4/oom_mm_usage
@@ -0,0 +1,26 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+# Error line: not an integer: "y04"
+[651,700] 701 702 703 y04 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/5/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/5/oom_mm_usage
new file mode 100644
index 0000000..ab36e00
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/5/oom_mm_usage
@@ -0,0 +1,26 @@
+# oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+# Error line: not an integer: floating point 1003.25"
+[501,550] 1001 1002 1003.25 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/6/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/6/oom_mm_usage
new file mode 100644
index 0000000..40a9dae
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/6/oom_mm_usage
@@ -0,0 +1,25 @@
+# 23y03 not an integer: oom_group <nr_task > <file_rss_kb> <anon_rss_kb> <pgtable_kb> <swap_ents_kb> <shmem_rss_kb>,
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003.25 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 23y03 2304 2305 2306
+
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/7/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/7/oom_mm_usage
new file mode 100644
index 0000000..6435693
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/7/oom_mm_usage
@@ -0,0 +1,28 @@
+# Error file: trailing extra groups
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[651,700] 701 702 703 704 705 706
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
diff --git a/pixelstats/test/mm/data/test_data_oom_usage_fail/8/oom_mm_usage b/pixelstats/test/mm/data/test_data_oom_usage_fail/8/oom_mm_usage
new file mode 100644
index 0000000..98f38e1
--- /dev/null
+++ b/pixelstats/test/mm/data/test_data_oom_usage_fail/8/oom_mm_usage
@@ -0,0 +1,26 @@
+# Groups not in descending order error: [351,400] -> [651,700]
+[951,1000] 0 102 103 104 105 106
+[901,950] 201 0 203 204 205 206
+[851,900] 301 302 0 304 305 306
+[801,850] 401 402 403 0 405 406
+[751,800] 501 502 503 504 0 506
+[701,750] 601 602 603 604 605 0
+[601,650] 801 802 803 804 805 806
+[551,600] 901 902 903 904 905 906
+[501,550] 1001 1002 1003 1004 1005 1006
+[451,500] 1101 1102 1103 1104 1105 1106
+[401,450] 1201 1202 1203 1204 1205 1206
+[351,400] 1301 1302 1303 1304 1305 1306
+[651,700] 701 702 703 704 705 706
+[301,350] 1401 1402 1403 1404 1405 1406
+[251,300] 1501 1502 1503 1504 1505 1506
+[201,250] 1601 1602 1603 1604 1605 1606
+[200,200] 1701 1702 1703 1704 1705 1706
+[151,199] 1801 1802 1803 1804 1805 1806
+[101,150] 1901 1902 1903 1904 1905 1906
+[51,100] 2001 2002 2003 2004 2005 2006
+[1,50] 2101 2102 2103 2104 2105 2106
+[0,0] 2201 2202 2203 2204 2205 2206
+[-1000,-1] 2301 2302 2303 2304 2305 2306
+
+
diff --git a/power-libperfmgr/aidl/AdpfTypes.h b/power-libperfmgr/aidl/AdpfTypes.h
index 8f5a018..4db4894 100644
--- a/power-libperfmgr/aidl/AdpfTypes.h
+++ b/power-libperfmgr/aidl/AdpfTypes.h
@@ -47,6 +47,24 @@
enum class AdpfErrorCode : int32_t { ERR_OK = 0, ERR_BAD_STATE = -1, ERR_BAD_ARG = -2 };
+enum class SessionJankyLevel : int32_t {
+ /**
+ * Small number of jank frames in the monitoring window.
+ * No extra heuristic boost will be applied.
+ */
+ LIGHT = 0,
+ /**
+ * Moderate number of jank frames in the monitoring window.
+ * Heuristic boost applied.
+ */
+ MODERATE,
+ /**
+ * Significant number of jank frames in the monitoring window.
+ * Heuristic boost applied.
+ */
+ SEVERE,
+};
+
enum class AdpfVoteType : int32_t {
CPU_VOTE_DEFAULT = 0,
CPU_LOAD_UP,
diff --git a/power-libperfmgr/aidl/AppDescriptorTrace.h b/power-libperfmgr/aidl/AppDescriptorTrace.h
index d2d1236..d9af140 100644
--- a/power-libperfmgr/aidl/AppDescriptorTrace.h
+++ b/power-libperfmgr/aidl/AppDescriptorTrace.h
@@ -60,13 +60,19 @@
trace_is_first_frame = StringPrintf("adpf.%s-%s", idString.c_str(), "is_first_frame");
// traces for heuristic boost
trace_avg_duration = StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.avgDuration");
- trace_heuristic_boost_active =
- StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.isActive");
+ trace_hboost_janky_level =
+ StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.jankyLevel");
trace_low_frame_rate =
StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.isLowFrameRate");
trace_max_duration = StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.maxDuration");
trace_missed_cycles =
StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.numOfMissedCycles");
+ trace_uclamp_min_ceiling =
+ StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.uclampMinCeiling");
+ trace_uclamp_min_floor =
+ StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.uclampMinFloor");
+ trace_hboost_pid_pu = StringPrintf("adpf.%s-%s", idString.c_str(), "hboost.uclampPidPu");
+
for (size_t i = 0; i < trace_modes.size(); ++i) {
trace_modes[i] = StringPrintf(
"adpf.%s-%s_mode", idString.c_str(),
@@ -100,10 +106,14 @@
std::string trace_is_first_frame;
// traces for heuristic boost
std::string trace_avg_duration;
- std::string trace_heuristic_boost_active;
+ std::string trace_hboost_janky_level;
+ std::string trace_hboost_pid_pu;
std::string trace_low_frame_rate;
std::string trace_max_duration;
std::string trace_missed_cycles;
+ std::string trace_uclamp_min_ceiling;
+ std::string trace_uclamp_min_floor;
+
std::array<std::string, enum_size<aidl::android::hardware::power::SessionMode>()> trace_modes;
std::array<std::string, static_cast<int32_t>(AdpfVoteType::VOTE_TYPE_SIZE)> trace_votes;
std::string trace_cpu_duration;
diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp
index 1a66d70..6ede0fb 100644
--- a/power-libperfmgr/aidl/Power.cpp
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -92,8 +92,7 @@
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
- if (HintManager::GetInstance()->GetAdpfProfile() &&
- HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+ if (HintManager::GetInstance()->IsAdpfSupported()) {
PowerSessionManager<>::getInstance()->updateHintMode(toString(type), enabled);
}
switch (type) {
@@ -141,6 +140,15 @@
mVRModeOn = false;
}
break;
+ case Mode::AUTOMOTIVE_PROJECTION:
+ mDisplayLowPower->SetAAMode(enabled);
+ if (enabled) {
+ HintManager::GetInstance()->DoHint("AUTOMOTIVE_PROJECTION");
+ } else {
+ HintManager::GetInstance()->EndHint("AUTOMOTIVE_PROJECTION");
+ HintManager::GetInstance()->EndHint("DISPLAY_IDLE_AA");
+ }
+ break;
case Mode::LAUNCH:
if (mVRModeOn || mSustainedPerfModeOn) {
break;
@@ -219,10 +227,6 @@
ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
- if (HintManager::GetInstance()->GetAdpfProfile() &&
- HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
- PowerSessionManager<>::getInstance()->updateHintBoost(toString(type), durationMs);
- }
switch (type) {
case Boost::INTERACTION:
if (mVRModeOn || mSustainedPerfModeOn) {
@@ -316,7 +320,7 @@
}
ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) {
- *outNanoseconds = HintManager::GetInstance()->GetAdpfProfile()
+ *outNanoseconds = HintManager::GetInstance()->IsAdpfSupported()
? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs
: 0;
if (*outNanoseconds <= 0) {
@@ -329,8 +333,7 @@
ndk::ScopedAStatus Power::createHintSessionWithConfig(
int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNanos,
SessionTag tag, SessionConfig *config, std::shared_ptr<IPowerHintSession> *_aidl_return) {
- if (!HintManager::GetInstance()->GetAdpfProfile() ||
- HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs <= 0) {
+ if (!HintManager::GetInstance()->IsAdpfSupported()) {
*_aidl_return = nullptr;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp
index ac72f41..fce358f 100644
--- a/power-libperfmgr/aidl/PowerExt.cpp
+++ b/power-libperfmgr/aidl/PowerExt.cpp
@@ -47,11 +47,18 @@
} else {
HintManager::GetInstance()->EndHint(mode);
}
- if (HintManager::GetInstance()->GetAdpfProfile() &&
- HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+ if (HintManager::GetInstance()->IsAdpfSupported()) {
PowerSessionManager<>::getInstance()->updateHintMode(mode, enabled);
}
+ if (mode == "DISPLAY_IDLE" && mDisplayLowPower->IsAAModeOn()) {
+ if (enabled) {
+ HintManager::GetInstance()->DoHint("DISPLAY_IDLE_AA");
+ } else {
+ HintManager::GetInstance()->EndHint("DISPLAY_IDLE_AA");
+ }
+ }
+
return ndk::ScopedAStatus::ok();
}
@@ -68,10 +75,6 @@
ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
- if (HintManager::GetInstance()->GetAdpfProfile() &&
- HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
- PowerSessionManager<>::getInstance()->updateHintBoost(boost, durationMs);
- }
if (durationMs > 0) {
HintManager::GetInstance()->DoHint(boost, std::chrono::milliseconds(durationMs));
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index 91f23b5..e6f0731 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -23,7 +23,6 @@
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <perfmgr/AdpfConfig.h>
#include <private/android_filesystem_config.h>
#include <sys/syscall.h>
#include <time.h>
@@ -44,6 +43,7 @@
using ::android::base::StringPrintf;
using ::android::perfmgr::AdpfConfig;
+using ::android::perfmgr::HintManager;
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
@@ -62,7 +62,7 @@
template <class HintManagerT, class PowerSessionManagerT>
int64_t PowerHintSession<HintManagerT, PowerSessionManagerT>::convertWorkDurationToBoostByPid(
const std::vector<WorkDuration> &actualDurations) {
- std::shared_ptr<AdpfConfig> adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ std::shared_ptr<AdpfConfig> adpfConfig = getAdpfProfile();
const nanoseconds &targetDuration = mDescriptor->targetNs;
int64_t &integral_error = mDescriptor->integral_error;
int64_t &previous_error = mDescriptor->previous_error;
@@ -104,9 +104,20 @@
auto pid_pu_active = adpfConfig->mPidPu;
if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
- pid_pu_active = mHeuristicBoostActive
- ? adpfConfig->mPidPu * adpfConfig->mHBoostPidPuFactor.value()
- : adpfConfig->mPidPu;
+ auto hboostPidPu = std::min(adpfConfig->mHBoostSevereJankPidPu.value(), adpfConfig->mPidPu);
+ if (mJankyLevel == SessionJankyLevel::MODERATE) {
+ double JankyFactor =
+ mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
+ ? 0.0
+ : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
+ 1.0 /
+ (adpfConfig->mHBoostSevereJankThreshold.value() -
+ adpfConfig->mHBoostModerateJankThreshold.value());
+ pid_pu_active = adpfConfig->mPidPu + JankyFactor * (hboostPidPu - adpfConfig->mPidPu);
+ } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
+ pid_pu_active = hboostPidPu;
+ }
+ ATRACE_INT(mAppDescriptorTrace->trace_hboost_pid_pu.c_str(), pid_pu_active * 100);
}
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : pid_pu_active) *
err_sum / (length - p_start));
@@ -133,31 +144,30 @@
SessionTag tag)
: mPSManager(PowerSessionManagerT::getInstance()),
mSessionId(++sSessionIDCounter),
- mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64, tgid, uid, mSessionId)),
+ mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64 "-%s", tgid, uid, mSessionId,
+ toString(tag).c_str())),
mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag,
std::chrono::nanoseconds(durationNs))),
mAppDescriptorTrace(std::make_shared<AppDescriptorTrace>(mIdString)),
mTag(tag),
- mSessionRecords(
- HintManagerT::GetInstance()->GetAdpfProfile()->mHeuristicBoostOn.has_value() &&
- HintManagerT::GetInstance()
- ->GetAdpfProfile()
- ->mHeuristicBoostOn.value()
- ? std::make_unique<SessionRecords>(HintManagerT::GetInstance()
- ->GetAdpfProfile()
- ->mMaxRecordsNum.value(),
- HintManagerT::GetInstance()
- ->GetAdpfProfile()
- ->mJankCheckTimeFactor.value())
- : nullptr) {
+ mAdpfProfile(HintManager::GetInstance()->GetAdpfProfile(toString(mTag))),
+ mOnAdpfUpdate(
+ [this](const std::shared_ptr<AdpfConfig> config) { this->setAdpfProfile(config); }),
+ mSessionRecords(getAdpfProfile()->mHeuristicBoostOn.has_value() &&
+ getAdpfProfile()->mHeuristicBoostOn.value()
+ ? std::make_unique<SessionRecords>(
+ getAdpfProfile()->mMaxRecordsNum.value(),
+ getAdpfProfile()->mJankCheckTimeFactor.value())
+ : nullptr) {
ATRACE_CALL();
ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), mDescriptor->is_active.load());
+ HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mTag), &mOnAdpfUpdate);
mLastUpdatedTime = std::chrono::steady_clock::now();
mPSManager->addPowerSession(mIdString, mDescriptor, mAppDescriptorTrace, threadIds);
// init boost
- auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ auto adpfConfig = getAdpfProfile();
mPSManager->voteSet(
mSessionId, AdpfVoteType::CPU_LOAD_RESET, adpfConfig->mUclampMinLoadReset, kUclampMax,
std::chrono::steady_clock::now(),
@@ -189,7 +199,7 @@
int pidControlVariable, bool updateVote) {
mDescriptor->pidControlVariable = pidControlVariable;
if (updateVote) {
- auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ auto adpfConfig = getAdpfProfile();
mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, pidControlVariable,
kUclampMax, std::chrono::steady_clock::now(),
std::max(duration_cast<nanoseconds>(mDescriptor->targetNs *
@@ -265,6 +275,7 @@
// Remove the session from PowerSessionManager first to avoid racing.
mPSManager->removePowerSession(mSessionId);
mDescriptor->is_active.store(false);
+ HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mTag), &mOnAdpfUpdate);
ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
return ndk::ScopedAStatus::ok();
}
@@ -281,8 +292,7 @@
ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- targetDurationNanos =
- targetDurationNanos * HintManagerT::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
+ targetDurationNanos = targetDurationNanos * getAdpfProfile()->mTargetTimeFactor;
mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos);
mPSManager->updateTargetWorkDuration(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT,
@@ -293,14 +303,42 @@
}
template <class HintManagerT, class PowerSessionManagerT>
-bool PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() {
+SessionJankyLevel PowerHintSession<HintManagerT, PowerSessionManagerT>::updateSessionJankState(
+ SessionJankyLevel oldState, int32_t numOfJankFrames, double durationVariance,
+ bool isLowFPS) {
+ SessionJankyLevel newState = SessionJankyLevel::LIGHT;
+ if (isLowFPS) {
+ newState = SessionJankyLevel::LIGHT;
+ return newState;
+ }
+
+ auto adpfConfig = getAdpfProfile();
+ if (numOfJankFrames < adpfConfig->mHBoostModerateJankThreshold.value()) {
+ if (oldState == SessionJankyLevel::LIGHT ||
+ durationVariance < adpfConfig->mHBoostOffMaxAvgDurRatio.value()) {
+ newState = SessionJankyLevel::LIGHT;
+ } else {
+ newState = SessionJankyLevel::MODERATE;
+ }
+ } else if (numOfJankFrames < adpfConfig->mHBoostSevereJankThreshold.value()) {
+ newState = SessionJankyLevel::MODERATE;
+ } else {
+ newState = SessionJankyLevel::SEVERE;
+ }
+
+ return newState;
+}
+
+template <class HintManagerT, class PowerSessionManagerT>
+void PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() {
auto maxDurationUs = mSessionRecords->getMaxDuration(); // micro seconds
auto avgDurationUs = mSessionRecords->getAvgDuration(); // micro seconds
auto numOfReportedDurations = mSessionRecords->getNumOfRecords();
- auto numOfMissedCycles = mSessionRecords->getNumOfMissedCycles();
+ auto numOfJankFrames = mSessionRecords->getNumOfMissedCycles();
if (!maxDurationUs.has_value() || !avgDurationUs.has_value()) {
- return false;
+ // No history data stored
+ return;
}
double maxToAvgRatio;
@@ -310,26 +348,18 @@
maxToAvgRatio = maxDurationUs.value() / avgDurationUs.value();
}
- auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ auto isLowFPS =
+ mSessionRecords->isLowFrameRate(getAdpfProfile()->mLowFrameRateThreshold.value());
- if (mSessionRecords->isLowFrameRate(adpfConfig->mLowFrameRateThreshold.value())) {
- // Turn off the boost when the FPS drops to a low value,
- // since usually this is because of ui changing to low rate scenarios.
- // Extra boost is not needed in these scenarios.
- mHeuristicBoostActive = false;
- } else if (numOfMissedCycles >= adpfConfig->mHBoostOnMissedCycles.value()) {
- mHeuristicBoostActive = true;
- } else if (numOfMissedCycles <= adpfConfig->mHBoostOffMissedCycles.value() &&
- maxToAvgRatio < adpfConfig->mHBoostOffMaxAvgRatio.value()) {
- mHeuristicBoostActive = false;
- }
- ATRACE_INT(mAppDescriptorTrace->trace_heuristic_boost_active.c_str(), mHeuristicBoostActive);
- ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), numOfMissedCycles);
+ mJankyLevel = updateSessionJankState(mJankyLevel, numOfJankFrames, maxToAvgRatio, isLowFPS);
+ mJankyFrameNum = numOfJankFrames;
+
+ ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(),
+ static_cast<int32_t>(mJankyLevel));
+ ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum);
ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), avgDurationUs.value());
ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), maxDurationUs.value());
- ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(),
- mSessionRecords->isLowFrameRate(adpfConfig->mLowFrameRateThreshold.value()));
- return mHeuristicBoostActive;
+ ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), isLowFPS);
}
template <class HintManagerT, class PowerSessionManagerT>
@@ -352,8 +382,7 @@
ALOGE("Error: shouldn't report duration during pause state.");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
-
- auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ auto adpfConfig = getAdpfProfile();
mDescriptor->update_count++;
bool isFirstFrame = isTimeout();
ATRACE_INT(mAppDescriptorTrace->trace_batch_size.c_str(), actualDurations.size());
@@ -370,10 +399,6 @@
mLastUpdatedTime = std::chrono::steady_clock::now();
if (isFirstFrame) {
- if (isAppSession()) {
- tryToSendPowerHint("ADPF_FIRST_FRAME");
- }
-
mPSManager->updateUniversalBoostMode();
}
@@ -384,23 +409,54 @@
return ndk::ScopedAStatus::ok();
}
- if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
+ bool hboostEnabled =
+ adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value();
+
+ if (hboostEnabled) {
mSessionRecords->addReportedDurations(actualDurations, mDescriptor->targetNs.count());
+ mPSManager->updateHboostStatistics(mSessionId, mJankyLevel, actualDurations.size());
updateHeuristicBoost();
}
int64_t output = convertWorkDurationToBoostByPid(actualDurations);
// Apply to all the threads in the group
+ auto uclampMinFloor = adpfConfig->mUclampMinLow;
auto uclampMinCeiling = adpfConfig->mUclampMinHigh;
- if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
- uclampMinCeiling = mHeuristicBoostActive ? adpfConfig->mHBoostUclampMin.value()
- : adpfConfig->mUclampMinHigh;
+ if (hboostEnabled) {
+ auto hboostMinUclampMinFloor = std::max(
+ adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().first);
+ auto hboostMaxUclampMinFloor = std::max(
+ adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().second);
+ auto hboostMinUclampMinCeiling = std::max(
+ adpfConfig->mUclampMinHigh, adpfConfig->mHBoostUclampMinCeilingRange.value().first);
+ auto hboostMaxUclampMinCeiling =
+ std::max(adpfConfig->mUclampMinHigh,
+ adpfConfig->mHBoostUclampMinCeilingRange.value().second);
+ if (mJankyLevel == SessionJankyLevel::MODERATE) {
+ double JankyFactor =
+ mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
+ ? 0.0
+ : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
+ 1.0 /
+ (adpfConfig->mHBoostSevereJankThreshold.value() -
+ adpfConfig->mHBoostModerateJankThreshold.value());
+ uclampMinFloor = hboostMinUclampMinFloor +
+ (hboostMaxUclampMinFloor - hboostMinUclampMinFloor) * JankyFactor;
+ uclampMinCeiling =
+ hboostMinUclampMinCeiling +
+ (hboostMaxUclampMinCeiling - hboostMinUclampMinCeiling) * JankyFactor;
+ } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
+ uclampMinFloor = hboostMaxUclampMinFloor;
+ uclampMinCeiling = hboostMaxUclampMinCeiling;
+ }
+ ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_ceiling.c_str(), uclampMinCeiling);
+ ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_floor.c_str(), uclampMinFloor);
}
int next_min = std::min(static_cast<int>(uclampMinCeiling),
mDescriptor->pidControlVariable + static_cast<int>(output));
- next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
+ next_min = std::max(static_cast<int>(uclampMinFloor), next_min);
updatePidControlVariable(next_min);
@@ -432,65 +488,69 @@
template <class HintManagerT, class PowerSessionManagerT>
ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::sendHint(
SessionHint hint) {
- std::scoped_lock lock{mPowerHintSessionLock};
- if (mSessionClosed) {
- ALOGE("Error: session is dead");
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- if (mDescriptor->targetNs.count() == 0LL) {
- ALOGE("Expect to call updateTargetWorkDuration() first.");
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
+ {
+ std::scoped_lock lock{mPowerHintSessionLock};
+ if (mSessionClosed) {
+ ALOGE("Error: session is dead");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mDescriptor->targetNs.count() == 0LL) {
+ ALOGE("Expect to call updateTargetWorkDuration() first.");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto adpfConfig = getAdpfProfile();
- switch (hint) {
- case SessionHint::CPU_LOAD_UP:
- updatePidControlVariable(mDescriptor->pidControlVariable);
- mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP, adpfConfig->mUclampMinLoadUp,
- kUclampMax, std::chrono::steady_clock::now(),
- mDescriptor->targetNs * 2);
- break;
- case SessionHint::CPU_LOAD_DOWN:
- updatePidControlVariable(adpfConfig->mUclampMinLow);
- break;
- case SessionHint::CPU_LOAD_RESET:
- updatePidControlVariable(
- std::max(adpfConfig->mUclampMinInit,
- static_cast<uint32_t>(mDescriptor->pidControlVariable)),
- false);
- mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET,
- adpfConfig->mUclampMinLoadReset, kUclampMax,
- std::chrono::steady_clock::now(),
- duration_cast<nanoseconds>(mDescriptor->targetNs *
- adpfConfig->mStaleTimeFactor / 2.0));
- break;
- case SessionHint::CPU_LOAD_RESUME:
- mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME,
- mDescriptor->pidControlVariable, kUclampMax,
- std::chrono::steady_clock::now(),
- duration_cast<nanoseconds>(mDescriptor->targetNs *
- adpfConfig->mStaleTimeFactor / 2.0));
- break;
- case SessionHint::POWER_EFFICIENCY:
- setMode(SessionMode::POWER_EFFICIENCY, true);
- break;
- case SessionHint::GPU_LOAD_UP:
- mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP,
- Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom),
- std::chrono::steady_clock::now(), mDescriptor->targetNs);
- break;
- case SessionHint::GPU_LOAD_DOWN:
- // TODO(kevindubois): add impl
- break;
- case SessionHint::GPU_LOAD_RESET:
- // TODO(kevindubois): add impl
- break;
- default:
- ALOGE("Error: hint is invalid");
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ switch (hint) {
+ case SessionHint::CPU_LOAD_UP:
+ updatePidControlVariable(mDescriptor->pidControlVariable);
+ mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP,
+ adpfConfig->mUclampMinLoadUp, kUclampMax,
+ std::chrono::steady_clock::now(), mDescriptor->targetNs * 2);
+ break;
+ case SessionHint::CPU_LOAD_DOWN:
+ updatePidControlVariable(adpfConfig->mUclampMinLow);
+ break;
+ case SessionHint::CPU_LOAD_RESET:
+ updatePidControlVariable(
+ std::max(adpfConfig->mUclampMinInit,
+ static_cast<uint32_t>(mDescriptor->pidControlVariable)),
+ false);
+ mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET,
+ adpfConfig->mUclampMinLoadReset, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs *
+ adpfConfig->mStaleTimeFactor / 2.0));
+ break;
+ case SessionHint::CPU_LOAD_RESUME:
+ mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME,
+ mDescriptor->pidControlVariable, kUclampMax,
+ std::chrono::steady_clock::now(),
+ duration_cast<nanoseconds>(mDescriptor->targetNs *
+ adpfConfig->mStaleTimeFactor / 2.0));
+ break;
+ case SessionHint::POWER_EFFICIENCY:
+ setMode(SessionMode::POWER_EFFICIENCY, true);
+ break;
+ case SessionHint::GPU_LOAD_UP:
+ mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP,
+ Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom),
+ std::chrono::steady_clock::now(), mDescriptor->targetNs);
+ break;
+ case SessionHint::GPU_LOAD_DOWN:
+ // TODO(kevindubois): add impl
+ break;
+ case SessionHint::GPU_LOAD_RESET:
+ // TODO(kevindubois): add impl
+ break;
+ default:
+ ALOGE("Error: hint is invalid");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ mLastUpdatedTime = std::chrono::steady_clock::now();
}
+ // Don't hold a lock (mPowerHintSession) while DoHint will try to take another
+ // lock(NodeLooperThread).
tryToSendPowerHint(toString(hint));
- mLastUpdatedTime = std::chrono::steady_clock::now();
return ndk::ScopedAStatus::ok();
}
@@ -533,7 +593,7 @@
mDescriptor->thread_ids = threadIds;
mPSManager->setThreadsFromPowerSession(mSessionId, threadIds);
// init boost
- updatePidControlVariable(HintManagerT::GetInstance()->GetAdpfProfile()->mUclampMinInit);
+ updatePidControlVariable(getAdpfProfile()->mUclampMinInit);
return ndk::ScopedAStatus::ok();
}
@@ -549,6 +609,23 @@
return mTag;
}
+template <class HintManagerT, class PowerSessionManagerT>
+const std::shared_ptr<AdpfConfig>
+PowerHintSession<HintManagerT, PowerSessionManagerT>::getAdpfProfile() const {
+ if (!mAdpfProfile) {
+ return HintManager::GetInstance()->GetAdpfProfile(toString(mTag));
+ }
+ return mAdpfProfile;
+}
+
+template <class HintManagerT, class PowerSessionManagerT>
+void PowerHintSession<HintManagerT, PowerSessionManagerT>::setAdpfProfile(
+ const std::shared_ptr<AdpfConfig> profile) {
+ // Must prevent profile from being changed in a binder call duration.
+ std::scoped_lock lock{mPowerHintSessionLock};
+ mAdpfProfile = profile;
+}
+
std::string AppHintDesc::toString() const {
std::string out = StringPrintf("session %" PRId64 "\n", sessionId);
out.append(
@@ -563,9 +640,8 @@
auto now = std::chrono::steady_clock::now();
time_point<steady_clock> staleTime =
mLastUpdatedTime +
- nanoseconds(static_cast<int64_t>(
- mDescriptor->targetNs.count() *
- HintManagerT::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
+ nanoseconds(static_cast<int64_t>(mDescriptor->targetNs.count() *
+ getAdpfProfile()->mStaleTimeFactor));
return now >= staleTime;
}
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
index 54127db..da73c13 100644
--- a/power-libperfmgr/aidl/PowerHintSession.h
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -40,6 +40,7 @@
using ::android::Message;
using ::android::MessageHandler;
using ::android::sp;
+using ::android::perfmgr::AdpfConfig;
using std::chrono::milliseconds;
using std::chrono::nanoseconds;
using std::chrono::steady_clock;
@@ -69,6 +70,7 @@
void dumpToStream(std::ostream &stream);
SessionTag getSessionTag() const;
+ void setAdpfProfile(const std::shared_ptr<AdpfConfig> profile);
private:
// In practice this lock should almost never get contested, but it's necessary for FMQ
@@ -76,12 +78,16 @@
bool isTimeout() REQUIRES(mPowerHintSessionLock);
// Is hint session for a user application
bool isAppSession() REQUIRES(mPowerHintSessionLock);
- void tryToSendPowerHint(std::string hint) REQUIRES(mPowerHintSessionLock);
+ void tryToSendPowerHint(std::string hint);
void updatePidControlVariable(int pidControlVariable, bool updateVote = true)
REQUIRES(mPowerHintSessionLock);
int64_t convertWorkDurationToBoostByPid(const std::vector<WorkDuration> &actualDurations)
REQUIRES(mPowerHintSessionLock);
- bool updateHeuristicBoost() REQUIRES(mPowerHintSessionLock);
+ SessionJankyLevel updateSessionJankState(SessionJankyLevel oldState, int32_t numOfJankFrames,
+ double durationVariance, bool isLowFPS)
+ REQUIRES(mPowerHintSessionLock);
+ void updateHeuristicBoost() REQUIRES(mPowerHintSessionLock);
+ const std::shared_ptr<AdpfConfig> getAdpfProfile() const;
// Data
PowerSessionManagerT *mPSManager;
@@ -94,14 +100,17 @@
time_point<steady_clock> mLastUpdatedTime GUARDED_BY(mPowerHintSessionLock);
bool mSessionClosed GUARDED_BY(mPowerHintSessionLock) = false;
// Are cpu load change related hints are supported
- std::unordered_map<std::string, std::optional<bool>> mSupportedHints
- GUARDED_BY(mPowerHintSessionLock);
+ std::unordered_map<std::string, std::optional<bool>> mSupportedHints;
// Use the value of the last enum in enum_range +1 as array size
std::array<bool, enum_size<SessionMode>()> mModes GUARDED_BY(mPowerHintSessionLock){};
// Tag labeling what kind of session this is
const SessionTag mTag;
+ std::shared_ptr<AdpfConfig> mAdpfProfile;
+ std::function<void(const std::shared_ptr<AdpfConfig>)> mOnAdpfUpdate;
std::unique_ptr<SessionRecords> mSessionRecords GUARDED_BY(mPowerHintSessionLock) = nullptr;
bool mHeuristicBoostActive GUARDED_BY(mPowerHintSessionLock){false};
+ SessionJankyLevel mJankyLevel GUARDED_BY(mPowerHintSessionLock){SessionJankyLevel::LIGHT};
+ uint32_t mJankyFrameNum GUARDED_BY(mPowerHintSessionLock){0};
};
} // namespace pixel
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
index f386db5..2ee4565 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.cpp
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -20,7 +20,6 @@
#include "PowerSessionManager.h"
#include <android-base/file.h>
-#include <android-base/stringprintf.h>
#include <log/log.h>
#include <perfmgr/HintManager.h>
#include <private/android_filesystem_config.h>
@@ -78,33 +77,13 @@
}
} // namespace
+// TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
template <class HintManagerT>
void PowerSessionManager<HintManagerT>::updateHintMode(const std::string &mode, bool enabled) {
- if (enabled && mode.compare(0, 8, "REFRESH_") == 0) {
- if (mode.compare("REFRESH_120FPS") == 0) {
- mDisplayRefreshRate = 120;
- } else if (mode.compare("REFRESH_90FPS") == 0) {
- mDisplayRefreshRate = 90;
- } else if (mode.compare("REFRESH_60FPS") == 0) {
- mDisplayRefreshRate = 60;
- }
+ ALOGD("%s %s:%b", __func__, mode.c_str(), enabled);
+ if (enabled && HintManager::GetInstance()->GetAdpfProfileFromDoHint()) {
+ HintManager::GetInstance()->SetAdpfProfileFromDoHint(mode);
}
- if (HintManager::GetInstance()->GetAdpfProfile()) {
- HintManager::GetInstance()->SetAdpfProfile(mode);
- }
-}
-
-template <class HintManagerT>
-void PowerSessionManager<HintManagerT>::updateHintBoost(const std::string &boost,
- int32_t durationMs) {
- ATRACE_CALL();
- ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
- durationMs);
-}
-
-template <class HintManagerT>
-int PowerSessionManager<HintManagerT>::getDisplayRefreshRate() {
- return mDisplayRefreshRate;
}
template <class HintManagerT>
@@ -603,6 +582,30 @@
mSessionMap.clear();
}
+template <class HintManagerT>
+void PowerSessionManager<HintManagerT>::updateHboostStatistics(int64_t sessionId,
+ SessionJankyLevel jankyLevel,
+ int32_t numOfFrames) {
+ std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
+ auto sessValPtr = mSessionTaskMap.findSession(sessionId);
+ if (nullptr == sessValPtr) {
+ return;
+ }
+ switch (jankyLevel) {
+ case SessionJankyLevel::LIGHT:
+ sessValPtr->hBoostModeDist.lightModeFrames += numOfFrames;
+ break;
+ case SessionJankyLevel::MODERATE:
+ sessValPtr->hBoostModeDist.moderateModeFrames += numOfFrames;
+ break;
+ case SessionJankyLevel::SEVERE:
+ sessValPtr->hBoostModeDist.severeModeFrames += numOfFrames;
+ break;
+ default:
+ ALOGW("Unknown janky level during updateHboostStatistics");
+ }
+}
+
template class PowerSessionManager<>;
template class PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>;
diff --git a/power-libperfmgr/aidl/PowerSessionManager.h b/power-libperfmgr/aidl/PowerSessionManager.h
index 4827d9a..e71ed2e 100644
--- a/power-libperfmgr/aidl/PowerSessionManager.h
+++ b/power-libperfmgr/aidl/PowerSessionManager.h
@@ -46,8 +46,6 @@
// Update the current hint info
void updateHintMode(const std::string &mode, bool enabled);
- void updateHintBoost(const std::string &boost, int32_t durationMs);
- int getDisplayRefreshRate();
// Add and remove power hint session
void addPowerSession(const std::string &idString,
const std::shared_ptr<AppHintDesc> &sessionDescriptor,
@@ -78,6 +76,9 @@
void setPreferPowerEfficiency(int64_t sessionId, bool enabled);
+ void updateHboostStatistics(int64_t sessionId, SessionJankyLevel jankyLevel,
+ int32_t numOfFrames);
+
// Singleton
static PowerSessionManager *getInstance() {
static PowerSessionManager instance{};
@@ -98,8 +99,6 @@
void enableSystemTopAppBoost();
const std::string kDisableBoostHintName;
- int mDisplayRefreshRate;
-
// Rewrite specific
mutable std::mutex mSessionTaskMapMutex;
SessionTaskMap mSessionTaskMap;
@@ -129,7 +128,6 @@
PowerSessionManager()
: kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
"ADPF_DISABLE_TA_BOOST")),
- mDisplayRefreshRate(60),
mPriorityQueueWorkerPool(new PriorityQueueWorkerPool(1, "adpf_handler")),
mEventSessionTimeoutWorker([&](auto e) { handleEvent(e); }, mPriorityQueueWorkerPool),
mGpuCapacityNode(createGpuCapacityNode()) {}
diff --git a/power-libperfmgr/aidl/SessionValueEntry.cpp b/power-libperfmgr/aidl/SessionValueEntry.cpp
index f250d93..cff5bf6 100644
--- a/power-libperfmgr/aidl/SessionValueEntry.cpp
+++ b/power-libperfmgr/aidl/SessionValueEntry.cpp
@@ -37,6 +37,16 @@
os << ", votes nullptr";
}
os << ", " << isActive;
+ auto totalFrames = hBoostModeDist.lightModeFrames + hBoostModeDist.moderateModeFrames +
+ hBoostModeDist.severeModeFrames;
+ os << ", HBoost:"
+ << (totalFrames <= 0 ? 0 : (hBoostModeDist.lightModeFrames * 10000 / totalFrames / 100.0))
+ << "%-"
+ << (totalFrames <= 0 ? 0 : (hBoostModeDist.moderateModeFrames * 10000 / totalFrames / 100.0))
+ << "%-"
+ << (totalFrames <= 0 ? 0 : (hBoostModeDist.severeModeFrames * 10000 / totalFrames / 100.0))
+ << "%-" << totalFrames << ", ";
+
return os;
}
diff --git a/power-libperfmgr/aidl/SessionValueEntry.h b/power-libperfmgr/aidl/SessionValueEntry.h
index e3cd046..3ccade8 100644
--- a/power-libperfmgr/aidl/SessionValueEntry.h
+++ b/power-libperfmgr/aidl/SessionValueEntry.h
@@ -28,6 +28,13 @@
namespace impl {
namespace pixel {
+// Record the heuristic boost mode distribution among the frames
+struct HeurBoostStatistics {
+ int64_t lightModeFrames{0};
+ int64_t moderateModeFrames{0};
+ int64_t severeModeFrames{0};
+};
+
// Per-power-session values (equivalent to original PowerHintSession)
// Responsible for maintaining the state of the power session via attributes
// Primarily this means actual uclamp value and whether session is active
@@ -44,6 +51,7 @@
std::shared_ptr<Votes> votes;
std::shared_ptr<AppDescriptorTrace> sessionTrace;
bool isPowerEfficient{false};
+ HeurBoostStatistics hBoostModeDist;
// Write info about power session to ostream for logging and debugging
std::ostream &dump(std::ostream &os) const;
diff --git a/power-libperfmgr/aidl/service.cpp b/power-libperfmgr/aidl/service.cpp
index caf7b7d..f1e94c0 100644
--- a/power-libperfmgr/aidl/service.cpp
+++ b/power-libperfmgr/aidl/service.cpp
@@ -39,6 +39,7 @@
int main() {
android::base::SetDefaultTag(LOG_TAG);
+ android::base::SetMinimumLogSeverity(android::base::INFO);
// Parse config but do not start the looper
HintManager *hm = HintManager::GetInstance();
if (!hm) {
diff --git a/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp b/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
index a161bc7..8e32ad9 100644
--- a/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
+++ b/power-libperfmgr/aidl/tests/PowerHintSessionTest.cpp
@@ -325,6 +325,44 @@
sess2->close();
}
+TEST_F(PowerHintSessionMockedTest, updateSessionJankState) {
+ // Low FPS
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::SEVERE, 8, 5.0, true));
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::MODERATE, 8, 5.0, true));
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::LIGHT, 8, 5.0, true));
+ // Light number of jank frames, and high workload duration variance.
+ ASSERT_EQ(SessionJankyLevel::MODERATE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::SEVERE, 1, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::MODERATE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::MODERATE, 1, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::LIGHT, 1, 5.0, false));
+ // Light number of jank frames, and low workload duration variance.
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::SEVERE, 1, 1.0, false));
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::MODERATE, 1, 1.0, false));
+ ASSERT_EQ(SessionJankyLevel::LIGHT,
+ mHintSession->updateSessionJankState(SessionJankyLevel::LIGHT, 1, 1.0, false));
+ // Moderate number of jank frames
+ ASSERT_EQ(SessionJankyLevel::MODERATE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::SEVERE, 4, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::MODERATE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::MODERATE, 4, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::MODERATE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::LIGHT, 4, 5.0, false));
+ // Significant number of jank frames
+ ASSERT_EQ(SessionJankyLevel::SEVERE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::SEVERE, 9, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::SEVERE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::MODERATE, 9, 5.0, false));
+ ASSERT_EQ(SessionJankyLevel::SEVERE,
+ mHintSession->updateSessionJankState(SessionJankyLevel::LIGHT, 9, 5.0, false));
+}
+
} // namespace pixel
} // namespace impl
} // namespace power
diff --git a/power-libperfmgr/aidl/tests/TestHelper.cpp b/power-libperfmgr/aidl/tests/TestHelper.cpp
index b7c7295..b07b94d 100644
--- a/power-libperfmgr/aidl/tests/TestHelper.cpp
+++ b/power-libperfmgr/aidl/tests/TestHelper.cpp
@@ -19,41 +19,43 @@
namespace aidl::google::hardware::power::impl::pixel {
::android::perfmgr::AdpfConfig makeMockConfig() {
- return ::android::perfmgr::AdpfConfig("REFRESH_60FPS", /* Name */
- true, /* PID_On */
- 2.0, /* PID_Po */
- 1.0, /* PID_Pu */
- 0.0, /* PID_I */
- 200, /* PID_I_Init */
- 512, /* PID_I_High */
- -30, /* PID_I_Low */
- 500.0, /* PID_Do */
- 0.0, /* PID_Du */
- true, /* UclampMin_On */
- 162, /* UclampMin_Init */
- 480, /* UclampMin_High */
- 2, /* UclampMin_Low */
- 1, /* SamplingWindow_P */
- 0, /* SamplingWindow_I */
- 1, /* SamplingWindow_D */
- 166666660, /* ReportingRateLimitNs */
- 1.0, /* TargetTimeFactor */
- 15.0, /* StaleTimeFactor */
- true, /* GpuBoost */
- 25000, /* GpuCapacityBoostMax */
- 0, /* GpuCapacityLoadUpHeadroom */
- true, /* HeuristicBoost_On */
- 8, /* HBoostOnMissedCycles */
- 4.0, /* HBoostOffMaxAvgRatio */
- 5, /* HBoostOffMissedCycles */
- 0.5, /* HBoostPidPuFactor */
- 722, /* HBoostUclampMin */
- 1.2, /* JankCheckTimeFactor */
- 25, /* LowFrameRateThreshold */
- 300, /* MaxRecordsNum */
- 480, /* UclampMin_LoadUp */
- 480, /* UclampMin_LoadReset */
- 500, /* UclampMax_EfficientBase */
- 200); /* UclampMax_EfficientOffset */
+ return ::android::perfmgr::AdpfConfig(
+ "REFRESH_60FPS", /* Name */
+ true, /* PID_On */
+ 2.0, /* PID_Po */
+ 1.0, /* PID_Pu */
+ 0.0, /* PID_I */
+ 200, /* PID_I_Init */
+ 512, /* PID_I_High */
+ -30, /* PID_I_Low */
+ 500.0, /* PID_Do */
+ 0.0, /* PID_Du */
+ true, /* UclampMin_On */
+ 162, /* UclampMin_Init */
+ 480, /* UclampMin_High */
+ 2, /* UclampMin_Low */
+ 1, /* SamplingWindow_P */
+ 0, /* SamplingWindow_I */
+ 1, /* SamplingWindow_D */
+ 166666660, /* ReportingRateLimitNs */
+ 1.0, /* TargetTimeFactor */
+ 15.0, /* StaleTimeFactor */
+ true, /* GpuBoost */
+ 25000, /* GpuCapacityBoostMax */
+ 0, /* GpuCapacityLoadUpHeadroom */
+ true, /* HeuristicBoost_On */
+ 2, /* HBoostModerateJankThreshold */
+ 4.0, /* HBoostOffMaxAvgDurRatio */
+ 0.5, /* HBoostSevereJankPidPu */
+ 8, /* HBoostSevereJankThreshold */
+ std::make_pair(480, 800), /* HBoostUclampMinCeilingRange */
+ std::make_pair(200, 400), /* HBoostUclampMinFloorRange */
+ 1.2, /* JankCheckTimeFactor */
+ 25, /* LowFrameRateThreshold */
+ 300, /* MaxRecordsNum */
+ 480, /* UclampMin_LoadUp */
+ 480, /* UclampMin_LoadReset */
+ 500, /* UclampMax_EfficientBase */
+ 200); /* UclampMax_EfficientOffset */
}
} // namespace aidl::google::hardware::power::impl::pixel
diff --git a/power-libperfmgr/aidl/tests/mocks/MockPowerSessionManager.h b/power-libperfmgr/aidl/tests/mocks/MockPowerSessionManager.h
index cf0183e..3092611 100644
--- a/power-libperfmgr/aidl/tests/mocks/MockPowerSessionManager.h
+++ b/power-libperfmgr/aidl/tests/mocks/MockPowerSessionManager.h
@@ -68,6 +68,9 @@
MOCK_METHOD(void, unregisterSession, (int64_t sessionId), ());
MOCK_METHOD(void, clear, (), ());
MOCK_METHOD(std::shared_ptr<void>, getSession, (int64_t sessionId), ());
+ MOCK_METHOD(void, updateHboostStatistics,
+ (int64_t sessionId, impl::pixel::SessionJankyLevel jankyLevel, int32_t numOfFrames),
+ ());
static testing::NiceMock<MockPowerSessionManager> *getInstance() {
static testing::NiceMock<MockPowerSessionManager> instance{};
diff --git a/power-libperfmgr/disp-power/DisplayLowPower.cpp b/power-libperfmgr/disp-power/DisplayLowPower.cpp
index f2da574..81744be 100644
--- a/power-libperfmgr/disp-power/DisplayLowPower.cpp
+++ b/power-libperfmgr/disp-power/DisplayLowPower.cpp
@@ -31,7 +31,7 @@
namespace impl {
namespace pixel {
-DisplayLowPower::DisplayLowPower() : mFossStatus(false) {}
+DisplayLowPower::DisplayLowPower() : mFossStatus(false), mAAModeOn(false) {}
void DisplayLowPower::Init() {
ConnectPpsDaemon();
@@ -79,6 +79,13 @@
}
}
+void DisplayLowPower::SetAAMode(bool enable) {
+ mAAModeOn = enable;
+}
+bool DisplayLowPower::IsAAModeOn() {
+ return mAAModeOn;
+}
+
} // namespace pixel
} // namespace impl
} // namespace power
diff --git a/power-libperfmgr/disp-power/DisplayLowPower.h b/power-libperfmgr/disp-power/DisplayLowPower.h
index 53eb6c9..64a7dcf 100644
--- a/power-libperfmgr/disp-power/DisplayLowPower.h
+++ b/power-libperfmgr/disp-power/DisplayLowPower.h
@@ -33,6 +33,8 @@
~DisplayLowPower() {}
void Init();
void SetDisplayLowPower(bool enable);
+ void SetAAMode(bool enable);
+ bool IsAAModeOn();
private:
void ConnectPpsDaemon();
@@ -41,6 +43,7 @@
::android::base::unique_fd mPpsSocket;
bool mFossStatus;
+ std::atomic<bool> mAAModeOn;
};
} // namespace pixel
diff --git a/power-libperfmgr/libperfmgr/AdpfConfig.cc b/power-libperfmgr/libperfmgr/AdpfConfig.cc
index 946673d..b04100f 100644
--- a/power-libperfmgr/libperfmgr/AdpfConfig.cc
+++ b/power-libperfmgr/libperfmgr/AdpfConfig.cc
@@ -66,11 +66,14 @@
dump_buf << "mGpuCapacityLoadUpHeadroom: " << mGpuCapacityLoadUpHeadroom << "\n";
if (mHeuristicBoostOn.has_value()) {
dump_buf << "HeuristicBoost_On: " << mHeuristicBoostOn.value() << "\n";
- dump_buf << "HBoostOnMissedCycles: " << mHBoostOnMissedCycles.value() << "\n";
- dump_buf << "HBoostOffMaxAvgRatio: " << mHBoostOffMaxAvgRatio.value() << "\n";
- dump_buf << "HBoostOffMissedCycles: " << mHBoostOffMissedCycles.value() << "\n";
- dump_buf << "HBoostPidPuFactor: " << mHBoostPidPuFactor.value() << "\n";
- dump_buf << "HBoostUclampMin: " << mHBoostUclampMin.value() << "\n";
+ dump_buf << "HBoostModerateJankThreshold: " << mHBoostModerateJankThreshold.value() << "\n";
+ dump_buf << "HBoostOffMaxAvgDurRatio: " << mHBoostOffMaxAvgDurRatio.value() << "\n";
+ dump_buf << "HBoostSevereJankPidPu: " << mHBoostSevereJankPidPu.value() << "\n";
+ dump_buf << "HBoostSevereJankThreshold: " << mHBoostSevereJankThreshold.value() << "\n";
+ dump_buf << "HBoostUclampMinCeilingRange: [" << mHBoostUclampMinCeilingRange.value().first;
+ dump_buf << ", " << mHBoostUclampMinCeilingRange.value().second << "]\n";
+ dump_buf << "HBoostUclampMinFloorRange: [" << mHBoostUclampMinFloorRange.value().first;
+ dump_buf << ", " << mHBoostUclampMinFloorRange.value().second << "]\n";
dump_buf << "JankCheckTimeFactor: " << mJankCheckTimeFactor.value() << "\n";
dump_buf << "LowFrameRateThreshold: " << mLowFrameRateThreshold.value() << "\n";
dump_buf << "MaxRecordsNum: " << mMaxRecordsNum.value() << "\n";
diff --git a/power-libperfmgr/libperfmgr/Android.bp b/power-libperfmgr/libperfmgr/Android.bp
index 96c1064..fe6b695 100644
--- a/power-libperfmgr/libperfmgr/Android.bp
+++ b/power-libperfmgr/libperfmgr/Android.bp
@@ -59,6 +59,7 @@
"NodeLooperThread.cc",
"HintManager.cc",
"AdpfConfig.cc",
+ "EventNode.cc",
]
}
@@ -72,6 +73,7 @@
"tests/PropertyNodeTest.cc",
"tests/NodeLooperThreadTest.cc",
"tests/HintManagerTest.cc",
+ "tests/EventNodeTest.cc",
],
test_suites: ["device-tests"],
require_root: true,
diff --git a/power-libperfmgr/libperfmgr/EventNode.cc b/power-libperfmgr/libperfmgr/EventNode.cc
new file mode 100644
index 0000000..2a2d2e3
--- /dev/null
+++ b/power-libperfmgr/libperfmgr/EventNode.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
+#define LOG_TAG "libperfmgr"
+
+#include "perfmgr/EventNode.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace perfmgr {
+
+EventNode::EventNode(
+ std::string name, std::string node_path, std::vector<RequestGroup> req_sorted,
+ std::size_t default_val_index, bool reset_on_init,
+ std::function<void(const std::string &, const std::string &, const std::string &)>
+ update_callback)
+ : Node(std::move(name), std::move(node_path), std::move(req_sorted), default_val_index,
+ reset_on_init),
+ update_callback_(update_callback) {}
+
+std::chrono::milliseconds EventNode::Update(bool) {
+ std::size_t value_index = default_val_index_;
+ std::chrono::milliseconds expire_time = std::chrono::milliseconds::max();
+
+ // Find the highest outstanding request's expire time
+ for (std::size_t i = 0; i < req_sorted_.size(); i++) {
+ if (req_sorted_[i].GetExpireTime(&expire_time)) {
+ value_index = i;
+ break;
+ }
+ }
+
+ // Update node only if request index changes
+ if (value_index != current_val_index_ || reset_on_init_) {
+ const std::string &req_value = req_sorted_[value_index].GetRequestValue();
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT(("N:" + GetName()).c_str(), value_index);
+ const std::string tag =
+ GetName() + ":" + req_value + ":" + std::to_string(expire_time.count());
+ ATRACE_BEGIN(tag.c_str());
+ }
+ update_callback_(name_, node_path_, req_value);
+ current_val_index_ = value_index;
+ reset_on_init_ = false;
+ if (ATRACE_ENABLED()) {
+ ATRACE_END();
+ }
+ }
+ return expire_time;
+}
+
+void EventNode::DumpToFd(int fd) const {
+ const std::string &node_value = req_sorted_[current_val_index_].GetRequestValue();
+ std::string buf(android::base::StringPrintf(
+ "Node Name\t"
+ "Event Path\t"
+ "Current Index\t"
+ "Current Value\n"
+ "%s\t%s\t%zu\t%s\n",
+ name_.c_str(), node_path_.c_str(), current_val_index_, node_value.c_str()));
+ if (!android::base::WriteStringToFd(buf, fd)) {
+ LOG(ERROR) << "Failed to dump fd: " << fd;
+ }
+ for (std::size_t i = 0; i < req_sorted_.size(); i++) {
+ req_sorted_[i].DumpToFd(fd, android::base::StringPrintf("\t\tReq%zu:\t", i));
+ }
+}
+
+} // namespace perfmgr
+} // namespace android
diff --git a/power-libperfmgr/libperfmgr/FileNode.cc b/power-libperfmgr/libperfmgr/FileNode.cc
index 05f0746..8ccacbc 100644
--- a/power-libperfmgr/libperfmgr/FileNode.cc
+++ b/power-libperfmgr/libperfmgr/FileNode.cc
@@ -31,7 +31,8 @@
namespace perfmgr {
FileNode::FileNode(std::string name, std::string node_path, std::vector<RequestGroup> req_sorted,
- std::size_t default_val_index, bool reset_on_init, bool truncate, bool hold_fd, bool write_only)
+ std::size_t default_val_index, bool reset_on_init, bool truncate, bool hold_fd,
+ bool write_only)
: Node(std::move(name), std::move(node_path), std::move(req_sorted), default_val_index,
reset_on_init),
hold_fd_(hold_fd),
@@ -56,7 +57,9 @@
const std::string& req_value =
req_sorted_[value_index].GetRequestValue();
if (ATRACE_ENABLED()) {
- const std::string tag = GetName() + ":" + req_value;
+ ATRACE_INT(("N:" + GetName()).c_str(), value_index);
+ const std::string tag =
+ GetName() + ":" + req_value + ":" + std::to_string(expire_time.count());
ATRACE_BEGIN(tag.c_str());
}
android::base::Timer t;
diff --git a/power-libperfmgr/libperfmgr/HintManager.cc b/power-libperfmgr/libperfmgr/HintManager.cc
index 961fd4a..bc12cd9 100644
--- a/power-libperfmgr/libperfmgr/HintManager.cc
+++ b/power-libperfmgr/libperfmgr/HintManager.cc
@@ -30,7 +30,9 @@
#include <algorithm>
#include <set>
+#include <string>
+#include "perfmgr/EventNode.h"
#include "perfmgr/FileNode.h"
#include "perfmgr/PropertyNode.h"
@@ -43,10 +45,14 @@
std::chrono::steady_clock::time_point::max();
} // namespace
+using ::android::base::GetProperty;
+using ::android::base::StringPrintf;
+
constexpr char kPowerHalTruncateProp[] = "vendor.powerhal.truncate";
constexpr std::string_view kConfigDebugPathProperty("vendor.powerhal.config.debug");
constexpr std::string_view kConfigProperty("vendor.powerhal.config");
constexpr std::string_view kConfigDefaultFileName("powerhint.json");
+constexpr char kAdpfEventNodePath[] = "<AdpfConfig>:";
bool HintManager::ValidateHint(const std::string& hint_type) const {
if (nm_.get() == nullptr) {
@@ -95,8 +101,9 @@
std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
actions_.at(hint_type).status->stats.count.fetch_add(1);
auto now = std::chrono::steady_clock::now();
- ATRACE_INT(hint_type.c_str(), (timeout_ms == kMilliSecondZero) ? std::numeric_limits<int>::max()
- : timeout_ms.count());
+ ATRACE_INT(("H:" + hint_type).c_str(), (timeout_ms == kMilliSecondZero)
+ ? std::numeric_limits<int>::max()
+ : timeout_ms.count());
if (now > actions_.at(hint_type).status->end_time) {
actions_.at(hint_type).status->stats.duration_ms.fetch_add(
std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -113,7 +120,7 @@
std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
// Update HintStats if the hint ends earlier than expected end_time
auto now = std::chrono::steady_clock::now();
- ATRACE_INT(hint_type.c_str(), 0);
+ ATRACE_INT(("H:" + hint_type).c_str(), 0);
if (now < actions_.at(hint_type).status->end_time) {
actions_.at(hint_type).status->stats.duration_ms.fetch_add(
std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -250,9 +257,8 @@
std::sort(keys.begin(), keys.end());
for (const auto &ordered_key : keys) {
HintStats hint_stats(GetHintStats(ordered_key));
- hint_stats_string +=
- android::base::StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
- hint_stats.count, hint_stats.duration_ms);
+ hint_stats_string += StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
+ hint_stats.count, hint_stats.duration_ms);
}
if (!android::base::WriteStringToFd(hint_stats_string, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
@@ -263,16 +269,22 @@
}
// Dump current ADPF profile
- if (GetAdpfProfile()) {
- header = "========== Begin current adpf profile ==========\n";
+ if (IsAdpfSupported()) {
+ header = "========== ADPF Tag Profile begin ==========\n";
if (!android::base::WriteStringToFd(header, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
- GetAdpfProfile()->dumpToFd(fd);
- footer = "========== End current adpf profile ==========\n";
+ // TODO(jimmyshiu@/guibing@): Update it when fully switched to the tag based adpf profiles.
+ GetAdpfProfileFromDoHint()->dumpToFd(fd);
+ footer = "========== ADPF Tag Profile end ==========\n";
if (!android::base::WriteStringToFd(footer, fd)) {
LOG(ERROR) << "Failed to dump fd: " << fd;
}
+ } else {
+ header = "========== IsAdpfSupported: No ===========\n";
+ if (!android::base::WriteStringToFd(header, fd)) {
+ LOG(ERROR) << "Failed to dump fd: " << fd;
+ }
}
fsync(fd);
}
@@ -289,8 +301,7 @@
config_path = "/data/vendor/etc/";
LOG(WARNING) << "Pixel Power HAL AIDL Service is using debug config from: " << config_path;
}
- config_path.append(
- android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()));
+ config_path.append(GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()));
LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is starting with config: "
<< config_path;
@@ -344,6 +355,32 @@
std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc, nodes);
+ // Parse ADPF Event Node
+ std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> tag_adpfs;
+ LOG(VERBOSE) << "Parse ADPF Hint Event Table from all nodes.";
+ for (std::size_t i = 0; i < nodes.size(); ++i) {
+ const std::string &node_name = nodes[i]->GetName();
+ const std::string &node_path = nodes[i]->GetPath();
+ if (node_path.starts_with(kAdpfEventNodePath)) {
+ std::string tag = node_path.substr(strlen(kAdpfEventNodePath));
+ std::size_t index = nodes[i]->GetDefaultIndex();
+ std::string profile_name = nodes[i]->GetValues()[index];
+ for (std::size_t j = 0; j < adpfs.size(); ++j) {
+ if (adpfs[j]->mName == profile_name) {
+ tag_adpfs[tag] = adpfs[j];
+ LOG(INFO) << "[" << tag << ":" << node_name << "] set to '" << profile_name
+ << "'";
+ break;
+ }
+ }
+ if (!tag_adpfs[tag]) {
+ tag_adpfs[tag] = adpfs[0];
+ LOG(INFO) << "[" << tag << ":" << node_name << "] fallback to '" << adpfs[0]->mName
+ << "'";
+ }
+ }
+ }
+
if (actions.empty()) {
LOG(ERROR) << "Failed to parse Actions section from " << config_path;
return nullptr;
@@ -352,7 +389,8 @@
auto const gpu_sysfs_node = ParseGpuSysfsNode(json_doc);
sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
- sInstance = std::make_unique<HintManager>(std::move(nm), actions, adpfs, gpu_sysfs_node);
+ sInstance =
+ std::make_unique<HintManager>(std::move(nm), actions, adpfs, tag_adpfs, gpu_sysfs_node);
if (!HintManager::InitHintStatus(sInstance)) {
LOG(ERROR) << "Failed to initialize hint status";
@@ -368,8 +406,7 @@
return HintManager::GetInstance();
}
-std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
- const std::string& json_doc) {
+std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(const std::string &json_doc) {
// function starts
std::vector<std::unique_ptr<Node>> nodes_parsed;
std::set<std::string> nodes_name_parsed;
@@ -418,12 +455,16 @@
return nodes_parsed;
}
- bool is_file = true;
+ bool is_event_node = false;
+ bool is_file = false;
std::string node_type = nodes[i]["Type"].asString();
LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
if (node_type.empty()) {
+ is_file = true;
LOG(VERBOSE) << "Failed to read "
<< "Node[" << i << "]'s Type, set to 'File' as default";
+ } else if (node_type == "Event") {
+ is_event_node = true;
} else if (node_type == "File") {
is_file = true;
} else if (node_type == "Property") {
@@ -492,7 +533,15 @@
LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
<< reset << std::noboolalpha;
- if (is_file) {
+ if (is_event_node) {
+ auto update_callback = [](const std::string &name, const std::string &path,
+ const std::string &val) {
+ HintManager::GetInstance()->OnNodeUpdate(name, path, val);
+ };
+ nodes_parsed.emplace_back(std::make_unique<EventNode>(
+ name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
+ update_callback));
+ } else if (is_file) {
bool truncate = android::base::GetBoolProperty(kPowerHalTruncateProp, true);
if (nodes[i]["Truncate"].empty() || !nodes[i]["Truncate"].isBool()) {
LOG(INFO) << "Failed to read Node[" << i << "]'s Truncate, set to 'true'";
@@ -527,8 +576,7 @@
truncate, hold_fd, write_only));
} else {
nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
- name, path, values_parsed,
- static_cast<std::size_t>(default_index), reset));
+ name, path, values_parsed, static_cast<std::size_t>(default_index), reset));
}
}
LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
@@ -734,11 +782,12 @@
// heuristic boost configs
std::optional<bool> heuristicBoostOn;
- std::optional<uint32_t> hBoostOnMissedCycles;
- std::optional<double> hBoostOffMaxAvgRatio;
- std::optional<uint32_t> hBoostOffMissedCycles;
- std::optional<double> hBoostPidPuFactor;
- std::optional<uint32_t> hBoostUclampMin;
+ std::optional<uint32_t> hBoostModerateJankThreshold;
+ std::optional<double> hBoostOffMaxAvgDurRatio;
+ std::optional<double> hBoostSevereJankPidPu;
+ std::optional<uint32_t> hBoostSevereJankThreshold;
+ std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinCeilingRange;
+ std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinFloorRange;
std::optional<double> jankCheckTimeFactor;
std::optional<uint32_t> lowFrameRateThreshold;
std::optional<uint32_t> maxRecordsNum;
@@ -770,11 +819,10 @@
ADPF_PARSE(reportingRate, "ReportingRateLimitNs", UInt64);
ADPF_PARSE(targetTimeFactor, "TargetTimeFactor", Double);
ADPF_PARSE_OPTIONAL(heuristicBoostOn, "HeuristicBoost_On", Bool);
- ADPF_PARSE_OPTIONAL(hBoostOnMissedCycles, "HBoostOnMissedCycles", UInt);
- ADPF_PARSE_OPTIONAL(hBoostOffMaxAvgRatio, "HBoostOffMaxAvgRatio", Double);
- ADPF_PARSE_OPTIONAL(hBoostOffMissedCycles, "HBoostOffMissedCycles", UInt);
- ADPF_PARSE_OPTIONAL(hBoostPidPuFactor, "HBoostPidPuFactor", Double);
- ADPF_PARSE_OPTIONAL(hBoostUclampMin, "HBoostUclampMin", UInt);
+ ADPF_PARSE_OPTIONAL(hBoostModerateJankThreshold, "HBoostModerateJankThreshold", UInt);
+ ADPF_PARSE_OPTIONAL(hBoostOffMaxAvgDurRatio, "HBoostOffMaxAvgDurRatio", Double);
+ ADPF_PARSE_OPTIONAL(hBoostSevereJankPidPu, "HBoostSevereJankPidPu", Double);
+ ADPF_PARSE_OPTIONAL(hBoostSevereJankThreshold, "HBoostSevereJankThreshold", UInt);
ADPF_PARSE_OPTIONAL(jankCheckTimeFactor, "JankCheckTimeFactor", Double);
ADPF_PARSE_OPTIONAL(lowFrameRateThreshold, "LowFrameRateThreshold", UInt);
ADPF_PARSE_OPTIONAL(maxRecordsNum, "MaxRecordsNum", UInt);
@@ -793,12 +841,29 @@
gpuCapacityLoadUpHeadroom = adpfs[i]["GpuCapacityLoadUpHeadroom"].asUInt64();
}
+ if (!adpfs[i]["HBoostUclampMinCeilingRange"].empty()) {
+ Json::Value ceilRange = adpfs[i]["HBoostUclampMinCeilingRange"];
+ if (ceilRange.size() == 2 && ceilRange[0].isUInt() && ceilRange[1].isUInt()) {
+ hBoostUclampMinCeilingRange =
+ std::make_pair(ceilRange[0].asUInt(), ceilRange[1].asUInt());
+ }
+ }
+
+ if (!adpfs[i]["HBoostUclampMinFloorRange"].empty()) {
+ Json::Value floorRange = adpfs[i]["HBoostUclampMinFloorRange"];
+ if (floorRange.size() == 2 && floorRange[0].isUInt() && floorRange[1].isUInt()) {
+ hBoostUclampMinFloorRange =
+ std::make_pair(floorRange[0].asUInt(), floorRange[1].asUInt());
+ }
+ }
+
// Check all the heuristic configurations are there if heuristic boost is going to
// be used.
if (heuristicBoostOn.has_value()) {
- if (!hBoostOnMissedCycles.has_value() || !hBoostOffMaxAvgRatio.has_value() ||
- !hBoostOffMissedCycles.has_value() || !hBoostPidPuFactor.has_value() ||
- !hBoostUclampMin.has_value() || !jankCheckTimeFactor.has_value() ||
+ if (!hBoostModerateJankThreshold.has_value() || !hBoostOffMaxAvgDurRatio.has_value() ||
+ !hBoostSevereJankPidPu.has_value() || !hBoostSevereJankThreshold.has_value() ||
+ !hBoostUclampMinCeilingRange.has_value() ||
+ !hBoostUclampMinFloorRange.has_value() || !jankCheckTimeFactor.has_value() ||
!lowFrameRateThreshold.has_value() || !maxRecordsNum.has_value()) {
LOG(ERROR) << "Part of the heuristic boost configurations are missing!";
adpfs_parsed.clear();
@@ -824,31 +889,78 @@
pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
targetTimeFactor, staleTimeFactor, gpuBoost, gpuBoostCapacityMax,
- gpuCapacityLoadUpHeadroom, heuristicBoostOn, hBoostOnMissedCycles,
- hBoostOffMaxAvgRatio, hBoostOffMissedCycles, hBoostPidPuFactor, hBoostUclampMin,
- jankCheckTimeFactor, lowFrameRateThreshold, maxRecordsNum, uclampMinLoadUp.value(),
+ gpuCapacityLoadUpHeadroom, heuristicBoostOn, hBoostModerateJankThreshold,
+ hBoostOffMaxAvgDurRatio, hBoostSevereJankPidPu, hBoostSevereJankThreshold,
+ hBoostUclampMinCeilingRange, hBoostUclampMinFloorRange, jankCheckTimeFactor,
+ lowFrameRateThreshold, maxRecordsNum, uclampMinLoadUp.value(),
uclampMinLoadReset.value(), uclampMaxEfficientBase, uclampMaxEfficientOffset));
}
LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
return adpfs_parsed;
}
-std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfile() const {
+// TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
+std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfileFromDoHint() const {
if (adpfs_.empty())
return nullptr;
return adpfs_[adpf_index_];
}
-bool HintManager::SetAdpfProfile(const std::string &profile_name) {
+// TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
+bool HintManager::SetAdpfProfileFromDoHint(const std::string &profile_name) {
for (std::size_t i = 0; i < adpfs_.size(); ++i) {
if (adpfs_[i]->mName == profile_name) {
- adpf_index_ = i;
+ if (adpf_index_ != i) {
+ ATRACE_NAME(StringPrintf("%s %s:%s", __func__, adpfs_[adpf_index_]->mName.c_str(),
+ profile_name.c_str())
+ .c_str());
+ adpf_index_ = i;
+ }
return true;
}
}
return false;
}
+bool HintManager::IsAdpfSupported() const {
+ return !adpfs_.empty();
+}
+
+std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfile(const std::string &tag) const {
+ if (adpfs_.empty())
+ return nullptr;
+ if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
+ // TODO(jimmyshiu@): `return adpfs_[0]` once the GetAdpfProfileFromDoHint() retired.
+ return GetAdpfProfileFromDoHint();
+ }
+ return tag_profile_map_.at(tag);
+}
+
+bool HintManager::SetAdpfProfile(const std::string &tag, const std::string &profile) {
+ if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
+ LOG(WARNING) << "SetAdpfProfile('" << tag << "', " << profile << ") Invalidate Tag!!!";
+ return false;
+ }
+ if (tag_profile_map_[tag]->mName == profile) {
+ LOG(VERBOSE) << "SetAdpfProfile:(" << tag << ", " << profile << ") value not changed!";
+ return true;
+ }
+
+ bool updated = false;
+ for (std::size_t i = 0; i < adpfs_.size(); ++i) {
+ if (adpfs_[i]->mName == profile) {
+ LOG(DEBUG) << "SetAdpfProfile('" << tag << "', '" << profile << "') Done!";
+ tag_profile_map_[tag] = adpfs_[i];
+ updated = true;
+ break;
+ }
+ }
+ if (!updated) {
+ LOG(WARNING) << "SetAdpfProfile(" << tag << ") failed to find profile:'" << profile << "'";
+ }
+ return updated;
+}
+
bool HintManager::IsAdpfProfileSupported(const std::string &profile_name) const {
for (std::size_t i = 0; i < adpfs_.size(); ++i) {
if (adpfs_[i]->mName == profile_name) {
@@ -858,6 +970,43 @@
return false;
}
+void HintManager::OnNodeUpdate(const std::string &name,
+ __attribute__((unused)) const std::string &path,
+ const std::string &value) {
+ // Check if the node is to update ADPF.
+ if (path.starts_with(kAdpfEventNodePath)) {
+ std::string tag = path.substr(strlen(kAdpfEventNodePath));
+ bool updated = SetAdpfProfile(tag, value);
+ if (!updated) {
+ LOG(DEBUG) << "OnNodeUpdate:[" << name << "] failed to update '" << value << "'";
+ return;
+ }
+ auto &callback_list = tag_update_callback_list_[tag];
+ for (const auto &callback : callback_list) {
+ (*callback)(tag_profile_map_[tag]);
+ }
+ }
+}
+
+void HintManager::RegisterAdpfUpdateEvent(const std::string &tag, AdpfCallback *update_adpf_func) {
+ tag_update_callback_list_[tag].push_back(update_adpf_func);
+}
+
+void HintManager::UnregisterAdpfUpdateEvent(const std::string &tag,
+ AdpfCallback *update_adpf_func) {
+ auto &callback_list = tag_update_callback_list_[tag];
+ // Use std::find to locate the function object
+ auto it = std::find_if(
+ callback_list.begin(), callback_list.end(),
+ [update_adpf_func](const std::function<void(const std::shared_ptr<AdpfConfig>)> *func) {
+ return func == update_adpf_func;
+ });
+ if (it != callback_list.end()) {
+ // Erase the found function object
+ callback_list.erase(it);
+ }
+}
+
std::optional<std::string> HintManager::gpu_sysfs_config_path() const {
return gpu_sysfs_config_path_;
}
diff --git a/power-libperfmgr/libperfmgr/PropertyNode.cc b/power-libperfmgr/libperfmgr/PropertyNode.cc
index 2ed6d09..cb4d2ca 100644
--- a/power-libperfmgr/libperfmgr/PropertyNode.cc
+++ b/power-libperfmgr/libperfmgr/PropertyNode.cc
@@ -52,7 +52,9 @@
const std::string& req_value =
req_sorted_[value_index].GetRequestValue();
if (ATRACE_ENABLED()) {
- const std::string tag = GetName() + ":" + req_value;
+ ATRACE_INT(("N:" + GetName()).c_str(), value_index);
+ const std::string tag =
+ GetName() + ":" + req_value + ":" + std::to_string(expire_time.count());
ATRACE_BEGIN(tag.c_str());
}
if (!android::base::SetProperty(node_path_, req_value)) {
diff --git a/power-libperfmgr/libperfmgr/RequestGroup.cc b/power-libperfmgr/libperfmgr/RequestGroup.cc
index 4fedd33..8787329 100644
--- a/power-libperfmgr/libperfmgr/RequestGroup.cc
+++ b/power-libperfmgr/libperfmgr/RequestGroup.cc
@@ -47,9 +47,13 @@
}
bool RequestGroup::GetExpireTime(std::chrono::milliseconds* expire_time) {
- ReqTime now = std::chrono::steady_clock::now();
+
*expire_time = std::chrono::milliseconds::max();
+ if (request_map_.empty()) return false;
+
+ ReqTime now = std::chrono::steady_clock::now();
+
bool active = false;
for (auto it = request_map_.begin(); it != request_map_.end();) {
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
index c75e380..f4491ab 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/AdpfConfig.h
@@ -54,11 +54,12 @@
// Heuristic boost control
std::optional<bool> mHeuristicBoostOn;
- std::optional<uint32_t> mHBoostOnMissedCycles;
- std::optional<double> mHBoostOffMaxAvgRatio;
- std::optional<uint32_t> mHBoostOffMissedCycles;
- std::optional<double> mHBoostPidPuFactor;
- std::optional<uint32_t> mHBoostUclampMin;
+ std::optional<uint32_t> mHBoostModerateJankThreshold;
+ std::optional<double> mHBoostOffMaxAvgDurRatio;
+ std::optional<double> mHBoostSevereJankPidPu;
+ std::optional<uint32_t> mHBoostSevereJankThreshold;
+ std::optional<std::pair<uint32_t, uint32_t>> mHBoostUclampMinCeilingRange;
+ std::optional<std::pair<uint32_t, uint32_t>> mHBoostUclampMinFloorRange;
std::optional<double> mJankCheckTimeFactor;
std::optional<uint32_t> mLowFrameRateThreshold;
std::optional<uint32_t> mMaxRecordsNum;
@@ -82,10 +83,13 @@
uint64_t samplingWindowD, int64_t reportingRateLimitNs, double targetTimeFactor,
double staleTimeFactor, std::optional<bool> gpuBoostOn,
std::optional<uint64_t> gpuBoostCapacityMax, uint64_t gpuCapacityLoadUpHeadroom,
- std::optional<bool> heuristicBoostOn, std::optional<uint32_t> hBoostOnMissedCycles,
- std::optional<double> hBoostOffMaxAvgRatio,
- std::optional<uint32_t> hBoostOffMissedCycles,
- std::optional<double> hBoostPidPuFactor, std::optional<uint32_t> hBoostUclampMin,
+ std::optional<bool> heuristicBoostOn,
+ std::optional<uint32_t> hBoostModerateJankThreshold,
+ std::optional<double> hBoostOffMaxAvgDurRatio,
+ std::optional<double> hBoostSevereJankPidPu,
+ std::optional<uint32_t> hBoostSevereJankThreshold,
+ std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinCeilingRange,
+ std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinFloorRange,
std::optional<double> jankCheckTimeFactor,
std::optional<uint32_t> lowFrameRateThreshold, std::optional<uint32_t> maxRecordsNum,
uint32_t uclampMinLoadUp, uint32_t uclampMinLoadReset,
@@ -115,11 +119,12 @@
mGpuBoostCapacityMax(gpuBoostCapacityMax),
mGpuCapacityLoadUpHeadroom(gpuCapacityLoadUpHeadroom),
mHeuristicBoostOn(heuristicBoostOn),
- mHBoostOnMissedCycles(hBoostOnMissedCycles),
- mHBoostOffMaxAvgRatio(hBoostOffMaxAvgRatio),
- mHBoostOffMissedCycles(hBoostOffMissedCycles),
- mHBoostPidPuFactor(hBoostPidPuFactor),
- mHBoostUclampMin(hBoostUclampMin),
+ mHBoostModerateJankThreshold(hBoostModerateJankThreshold),
+ mHBoostOffMaxAvgDurRatio(hBoostOffMaxAvgDurRatio),
+ mHBoostSevereJankPidPu(hBoostSevereJankPidPu),
+ mHBoostSevereJankThreshold(hBoostSevereJankThreshold),
+ mHBoostUclampMinCeilingRange(hBoostUclampMinCeilingRange),
+ mHBoostUclampMinFloorRange(hBoostUclampMinFloorRange),
mJankCheckTimeFactor(jankCheckTimeFactor),
mLowFrameRateThreshold(lowFrameRateThreshold),
mMaxRecordsNum(maxRecordsNum),
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/EventNode.h b/power-libperfmgr/libperfmgr/include/perfmgr/EventNode.h
new file mode 100644
index 0000000..f1b97f1
--- /dev/null
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/EventNode.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef ANDROID_LIBPERFMGR_EVENTNODE_H_
+#define ANDROID_LIBPERFMGR_EVENTNODE_H_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "perfmgr/Node.h"
+
+namespace android {
+namespace perfmgr {
+
+// EventNode represents to handle events by callback function.
+class EventNode : public Node {
+ public:
+ EventNode(std::string name, std::string node_path, std::vector<RequestGroup> req_sorted,
+ std::size_t default_val_index, bool reset_on_init,
+ std::function<void(const std::string &, const std::string &, const std::string &)>
+ update_callback);
+
+ std::chrono::milliseconds Update(bool log_error) override;
+ void DumpToFd(int fd) const override;
+
+ private:
+ EventNode(const Node &other) = delete;
+ EventNode &operator=(Node const &) = delete;
+ const std::function<void(const std::string &name, const std::string &path,
+ const std::string &value)>
+ update_callback_;
+};
+
+} // namespace perfmgr
+} // namespace android
+
+#endif // ANDROID_LIBPERFMGR_EVENTNODE_H_
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
index 204f6f1..303818a 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
@@ -90,10 +90,12 @@
public:
HintManager(sp<NodeLooperThread> nm, const std::unordered_map<std::string, Hint> &actions,
const std::vector<std::shared_ptr<AdpfConfig>> &adpfs,
+ const std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> &tag_adpfs,
std::optional<std::string> gpu_sysfs_config_path)
: nm_(std::move(nm)),
actions_(actions),
adpfs_(adpfs),
+ tag_profile_map_(tag_adpfs),
adpf_index_(0),
gpu_sysfs_config_path_(gpu_sysfs_config_path) {}
~HintManager() {
@@ -123,13 +125,23 @@
// Query if given hint enabled.
bool IsHintEnabled(const std::string &hint_type) const;
- // set ADPF config by profile name.
- bool SetAdpfProfile(const std::string &profile_name);
+ // TODO(jimmyshiu@): Need to be removed once all powerhint.json up-to-date.
+ bool SetAdpfProfileFromDoHint(const std::string &profile_name);
+ std::shared_ptr<AdpfConfig> GetAdpfProfileFromDoHint() const;
+
+ bool SetAdpfProfile(const std::string &tag, const std::string &profile);
+
+ typedef std::function<void(std::shared_ptr<AdpfConfig>)> AdpfCallback;
+ void RegisterAdpfUpdateEvent(const std::string &tag, AdpfCallback *update_adpf_func);
+ void UnregisterAdpfUpdateEvent(const std::string &tag, AdpfCallback *update_adpf_func);
std::optional<std::string> gpu_sysfs_config_path() const;
// get current ADPF.
- std::shared_ptr<AdpfConfig> GetAdpfProfile() const;
+ std::shared_ptr<AdpfConfig> GetAdpfProfile(const std::string &node_name = "OTHER") const;
+
+ // Check if ADPF is supported.
+ bool IsAdpfSupported() const;
// Query if given AdpfProfile supported.
bool IsAdpfProfileSupported(const std::string &name) const;
@@ -153,8 +165,7 @@
static HintManager *GetInstance();
protected:
- static std::vector<std::unique_ptr<Node>> ParseNodes(
- const std::string& json_doc);
+ static std::vector<std::unique_ptr<Node>> ParseNodes(const std::string &json_doc);
static std::unordered_map<std::string, Hint> ParseActions(
const std::string &json_doc, const std::vector<std::unique_ptr<Node>> &nodes);
static std::vector<std::shared_ptr<AdpfConfig>> ParseAdpfConfigs(const std::string &json_doc);
@@ -176,10 +187,17 @@
sp<NodeLooperThread> nm_;
std::unordered_map<std::string, Hint> actions_;
std::vector<std::shared_ptr<AdpfConfig>> adpfs_;
+ // TODO(jimmyshiu@): Need to be removed once all powerhint.json up-to-date.
+ std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> tag_profile_map_;
uint32_t adpf_index_;
std::optional<std::string> gpu_sysfs_config_path_;
static std::unique_ptr<HintManager> sInstance;
+
+ // Hint Update Callback
+ void OnNodeUpdate(const std::string &name, const std::string &path, const std::string &value);
+ // set ADPF config by hint name.
+ std::unordered_map<std::string, std::vector<AdpfCallback *>> tag_update_callback_list_;
};
} // namespace perfmgr
diff --git a/power-libperfmgr/libperfmgr/tests/EventNodeTest.cc b/power-libperfmgr/libperfmgr/tests/EventNodeTest.cc
new file mode 100644
index 0000000..62ca90f
--- /dev/null
+++ b/power-libperfmgr/libperfmgr/tests/EventNodeTest.cc
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <thread>
+
+#include "perfmgr/EventNode.h"
+
+namespace android {
+namespace perfmgr {
+
+using std::literals::chrono_literals::operator""ms;
+
+constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
+constexpr auto kSLEEP_TOLERANCE_MS = 2ms;
+
+// Test init with no default value
+TEST(EventNodeTest, NoInitDefaultTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 1, false,
+ update_callback);
+ t.Update(false);
+ EXPECT_EQ(node_val, "uninitialize");
+}
+
+// Test init with default value
+TEST(EventNodeTest, InitDefaultTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 1, true,
+ update_callback);
+ t.Update(false);
+ EXPECT_EQ(node_val, "value1");
+ EventNode t2("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 0, true,
+ update_callback);
+ t2.Update(false);
+ EXPECT_EQ(node_val, "value0");
+}
+
+// Test DumpToFd
+TEST(EventNodeTest, DumpToFdTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 1, true,
+ update_callback);
+ t.Update(false);
+ t.Update(false);
+ TemporaryFile dumptf;
+ t.DumpToFd(dumptf.fd);
+ fsync(dumptf.fd);
+ std::string buf(android::base::StringPrintf(
+ "Node Name\t"
+ "Event Path\t"
+ "Current Index\t"
+ "Current Value\n"
+ "%s\t%s\t%zu\t%s\n",
+ "EventName", "<Event>:Node", static_cast<size_t>(1), "value1"));
+ std::string s;
+ EXPECT_TRUE(android::base::ReadFileToString(dumptf.path, &s)) << strerror(errno);
+ EXPECT_EQ(buf, s);
+}
+
+// Test GetValueIndex
+TEST(EventNodeTest, GetValueIndexTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 1, false,
+ update_callback);
+ std::size_t index = 0;
+ EXPECT_TRUE(t.GetValueIndex("value2", &index));
+ EXPECT_EQ(2u, index);
+ index = 1234;
+ EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index));
+ EXPECT_EQ(1234u, index);
+}
+
+// Test GetValues
+TEST(EventNodeTest, GetValuesTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 1, false,
+ update_callback);
+ std::vector values = t.GetValues();
+ EXPECT_EQ(3u, values.size());
+ EXPECT_EQ("value0", values[0]);
+ EXPECT_EQ("value1", values[1]);
+ EXPECT_EQ("value2", values[2]);
+}
+
+// Test get more properties
+TEST(EventNodeTest, GetPropertiesTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ std::string test_name = "TESTREQ_1";
+ std::string test_path = "TEST_PATH";
+ EventNode t(test_name, test_path, {}, 0, false, update_callback);
+ EXPECT_EQ(test_name, t.GetName());
+ EXPECT_EQ(test_path, t.GetPath());
+ EXPECT_EQ(0u, t.GetValues().size());
+ EXPECT_EQ(0u, t.GetDefaultIndex());
+ EXPECT_FALSE(t.GetResetOnInit());
+}
+
+// Test add request
+TEST(EventNodeTest, AddRequestTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {""}}, 2, true,
+ update_callback);
+ auto start = std::chrono::steady_clock::now();
+ EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
+ std::chrono::milliseconds expire_time = t.Update(true);
+ // Add request @ value1
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Add request @ value0 higher prio than value1
+ EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value0");
+ EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Let high prio request timeout, now only request @ value1 active
+ std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Let all requests timeout, now default value2
+ std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "");
+ EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
+}
+
+// Test remove request
+TEST(EventNodeTest, RemoveRequestTest) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 2, true,
+ update_callback);
+ auto start = std::chrono::steady_clock::now();
+ EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
+ std::chrono::milliseconds expire_time = t.Update(true);
+ // Add request @ value1
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Add request @ value0 higher prio than value1
+ EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value0");
+ EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Remove high prio request, now only request @ value1 active
+ t.RemoveRequest("LAUNCH");
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Remove request, now default value2
+ t.RemoveRequest("INTERACTION");
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value2");
+ EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
+}
+
+// Test add request
+TEST(EventNodeTest, AddRequestTestOverride) {
+ std::string node_val = "uninitialize";
+ auto update_callback = [&node_val](const std::string &, const std::string &,
+ const std::string &val) { node_val = val; };
+ EventNode t("EventName", "<Event>:Node", {{"value0"}, {"value1"}, {"value2"}}, 2, true,
+ update_callback);
+ auto start = std::chrono::steady_clock::now();
+ EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
+ std::chrono::milliseconds expire_time = t.Update(true);
+ // Add request @ value1
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Add request @ value0 higher prio than value1
+ EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value0");
+ EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Add request @ value0 shorter
+ EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms));
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value0");
+ EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Add request @ value0 longer
+ EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms));
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value0");
+ EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Remove high prio request, now only request @ value1 active
+ t.RemoveRequest("LAUNCH");
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value1");
+ EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), kTIMING_TOLERANCE_MS);
+ // Remove request, now default value2
+ t.RemoveRequest("INTERACTION");
+ expire_time = t.Update(true);
+ EXPECT_EQ(node_val, "value2");
+ EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
+}
+
+} // namespace perfmgr
+} // namespace android
diff --git a/power-libperfmgr/libperfmgr/tests/FileNodeTest.cc b/power-libperfmgr/libperfmgr/tests/FileNodeTest.cc
index 45d11e4..c3f269b 100644
--- a/power-libperfmgr/libperfmgr/tests/FileNodeTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/FileNodeTest.cc
@@ -10,7 +10,7 @@
* 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 specic language governing permissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
diff --git a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
index f91bd77..a3b8ca5 100644
--- a/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/HintManagerTest.cc
@@ -10,7 +10,7 @@
* 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 specic language governing permissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
@@ -148,11 +148,48 @@
"Type": "DoHint",
"Value": "LAUNCH"
}
+ ]
+}
+)";
+
+constexpr char kJSON_ADPF[] = R"(
+{
+ "Nodes": [
+ {
+ "Name": "OTHER",
+ "Path": "<AdpfConfig>:OTHER",
+ "Values": [
+ "ADPF_DEFAULT"
+ ],
+ "Type": "Event"
+ },
+ {
+ "Name": "SURFACEFLINGER",
+ "Path": "<AdpfConfig>:SURFACEFLINGER",
+ "Values": [
+ "ADPF_DEFAULT",
+ "ADPF_SF"
+ ],
+ "Type": "Event"
+ }
],
- "GpuSysfsPath" : "/sys/devices/platform/123.abc",
+ "Actions": [
+ {
+ "PowerHint": "SF_PLAYING",
+ "Node": "SURFACEFLINGER",
+ "Duration": 0,
+ "Value": "ADPF_SF"
+ },
+ {
+ "PowerHint": "SF_RESET",
+ "Node": "SURFACEFLINGER",
+ "Duration": 0,
+ "Value": "ADPF_DEFAULT"
+ }
+ ],
"AdpfConfig": [
{
- "Name": "REFRESH_120FPS",
+ "Name": "ADPF_DEFAULT",
"PID_On": true,
"PID_Po": 5.0,
"PID_Pu": 3.0,
@@ -172,25 +209,24 @@
"UclampMin_High": 384,
"UclampMin_Low": 0,
"ReportingRateLimitNs": 166666660,
- "EarlyBoost_On": false,
- "EarlyBoost_TimeFactor": 0.8,
"TargetTimeFactor": 1.0,
"StaleTimeFactor": 10.0,
"GpuBoost": true,
- "GpuCapacityBoostMax": 300000,
+ "GpuCapacityBoostMax": 325000,
"GpuCapacityLoadUpHeadroom": 1000,
"HeuristicBoost_On": true,
- "HBoostOnMissedCycles": 4,
- "HBoostOffMaxAvgRatio": 4.0,
- "HBoostOffMissedCycles": 2,
- "HBoostPidPuFactor": 0.5,
- "HBoostUclampMin": 800,
+ "HBoostModerateJankThreshold": 4,
+ "HBoostOffMaxAvgDurRatio": 4.0,
+ "HBoostSevereJankPidPu": 0.5,
+ "HBoostSevereJankThreshold": 2,
+ "HBoostUclampMinCeilingRange": [480, 800],
+ "HBoostUclampMinFloorRange": [200, 400],
"JankCheckTimeFactor": 1.2,
"LowFrameRateThreshold": 25,
"MaxRecordsNum": 50
},
{
- "Name": "REFRESH_60FPS",
+ "Name": "ADPF_SF",
"PID_On": false,
"PID_Po": 0,
"PID_Pu": 0,
@@ -205,15 +241,42 @@
"SamplingWindow_D": 0,
"UclampMin_On": true,
"UclampMin_Init": 200,
+ "UclampMin_LoadUp": 157,
+ "UclampMin_LoadReset": 157,
"UclampMin_High": 157,
"UclampMin_Low": 157,
"ReportingRateLimitNs": 83333330,
- "EarlyBoost_On": true,
- "EarlyBoost_TimeFactor": 1.2,
"TargetTimeFactor": 1.4,
"StaleTimeFactor": 5.0
+ },
+ {
+ "Name": "SF_VIDEO_30FPS",
+ "PID_On": true,
+ "PID_Po": 5.0,
+ "PID_Pu": 3.0,
+ "PID_I": 0.001,
+ "PID_I_Init": 200,
+ "PID_I_High": 512,
+ "PID_I_Low": -120,
+ "PID_Do": 500.0,
+ "PID_Du": 300.0,
+ "SamplingWindow_P": 0,
+ "SamplingWindow_I": 0,
+ "SamplingWindow_D": 0,
+ "UclampMin_On": true,
+ "UclampMin_Init": 200,
+ "UclampMin_LoadUp": 157,
+ "UclampMin_LoadReset": 157,
+ "UclampMin_High": 480,
+ "UclampMin_Low": 240,
+ "ReportingRateLimitNs": 83333330,
+ "TargetTimeFactor": 1.4,
+ "StaleTimeFactor": 5.0,
+ "GpuBoost": false,
+ "GpuCapacityBoostMax": 32500
}
- ]
+ ],
+ "GpuSysfsPath" : "/sys/devices/platform/123.abc"
}
)";
@@ -221,7 +284,7 @@
protected:
HintManagerTest()
: HintManager(nullptr, std::unordered_map<std::string, Hint>{},
- std::vector<std::shared_ptr<AdpfConfig>>(), {}) {
+ std::vector<std::shared_ptr<AdpfConfig>>(), tag_adpfs_, {}) {
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
prop_ = "vendor.pwhal.mode";
}
@@ -268,8 +331,7 @@
from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
- EXPECT_TRUE(android::base::SetProperty(prop_, ""))
- << "failed to clear property";
+ EXPECT_TRUE(android::base::SetProperty(prop_, "")) << "failed to clear property";
}
virtual void TearDown() {
@@ -277,6 +339,7 @@
nodes_.clear();
files_.clear();
nm_ = nullptr;
+ tag_adpfs_.clear();
}
sp<NodeLooperThread> nm_;
std::unordered_map<std::string, Hint> actions_;
@@ -284,6 +347,7 @@
std::vector<std::unique_ptr<TemporaryFile>> files_;
std::string json_doc_;
std::string prop_;
+ std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> tag_adpfs_;
};
static inline void _VerifyPropertyValue(const std::string& path,
@@ -308,7 +372,7 @@
// Test GetHints
TEST_F(HintManagerTest, GetHintsTest) {
- HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), {});
+ HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), tag_adpfs_, {});
EXPECT_TRUE(hm.Start());
std::vector<std::string> hints = hm.GetHints();
EXPECT_TRUE(hm.IsRunning());
@@ -321,7 +385,7 @@
TEST_F(HintManagerTest, GetHintStatsTest) {
auto hm =
std::make_unique<HintManager>(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(),
- std::optional<std::string>{});
+ tag_adpfs_, std::optional<std::string>{});
EXPECT_TRUE(InitHintStatus(hm));
EXPECT_TRUE(hm->Start());
HintStats launch_stats(hm->GetHintStats("LAUNCH"));
@@ -334,7 +398,7 @@
// Test initialization of default values
TEST_F(HintManagerTest, HintInitDefaultTest) {
- HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), {});
+ HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), tag_adpfs_, {});
EXPECT_TRUE(hm.Start());
std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
EXPECT_TRUE(hm.IsRunning());
@@ -345,7 +409,7 @@
// Test IsHintSupported
TEST_F(HintManagerTest, HintSupportedTest) {
- HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), {});
+ HintManager hm(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(), tag_adpfs_, {});
EXPECT_TRUE(hm.IsHintSupported("INTERACTION"));
EXPECT_TRUE(hm.IsHintSupported("LAUNCH"));
EXPECT_FALSE(hm.IsHintSupported("NO_SUCH_HINT"));
@@ -355,7 +419,7 @@
TEST_F(HintManagerTest, HintTest) {
auto hm =
std::make_unique<HintManager>(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(),
- std::optional<std::string>{});
+ tag_adpfs_, std::optional<std::string>{});
EXPECT_TRUE(InitHintStatus(hm));
EXPECT_TRUE(hm->Start());
EXPECT_TRUE(hm->IsRunning());
@@ -406,7 +470,7 @@
TEST_F(HintManagerTest, HintStatsTest) {
auto hm =
std::make_unique<HintManager>(nm_, actions_, std::vector<std::shared_ptr<AdpfConfig>>(),
- std::optional<std::string>{});
+ tag_adpfs_, std::optional<std::string>{});
EXPECT_TRUE(InitHintStatus(hm));
EXPECT_TRUE(hm->Start());
EXPECT_TRUE(hm->IsRunning());
@@ -448,8 +512,7 @@
// Test parsing nodes
TEST_F(HintManagerTest, ParseNodesTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(4u, nodes.size());
EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
@@ -482,8 +545,7 @@
std::string from = "CPUCluster0MinFreq";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -491,8 +553,7 @@
std::string from = "ModeProperty";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -501,8 +562,7 @@
std::string from = files_[0 + 2]->path;
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -511,8 +571,7 @@
std::string from = "1512000";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "1134000");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -521,8 +580,7 @@
std::string from = "HIGH";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "LOW");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -531,8 +589,7 @@
std::string from = "384000";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(0u, nodes.size());
}
@@ -541,8 +598,7 @@
std::string from = "LOW";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(4u, nodes.size());
EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
@@ -572,8 +628,7 @@
// Test parsing invalid json for nodes
TEST_F(HintManagerTest, ParseBadFileNodesTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes("invalid json");
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes("invalid json");
EXPECT_EQ(0u, nodes.size());
nodes = HintManager::ParseNodes(
"{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
@@ -583,8 +638,7 @@
// Test parsing actions
TEST_F(HintManagerTest, ParseActionsTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(7u, actions.size());
@@ -643,8 +697,7 @@
std::string from = R"("Node": "CPUCluster0MinFreq")";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(), R"("Node": "CPUCluster1MinFreq")");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
EXPECT_EQ(4u, nodes.size());
auto actions = HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(0u, actions.size());
@@ -663,8 +716,7 @@
// Test parsing invalid json for actions
TEST_F(HintManagerTest, ParseBadActionsTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
+ std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_);
auto actions = HintManager::ParseActions("invalid json", nodes);
EXPECT_EQ(0u, actions.size());
actions = HintManager::ParseActions(
@@ -786,10 +838,11 @@
// Test parsing AdpfConfig
TEST_F(HintManagerTest, ParseAdpfConfigsTest) {
- std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc_);
- EXPECT_EQ(2u, adpfs.size());
- EXPECT_EQ("REFRESH_120FPS", adpfs[0]->mName);
- EXPECT_EQ("REFRESH_60FPS", adpfs[1]->mName);
+ std::string json_doc = std::string(kJSON_ADPF);
+ std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
+ EXPECT_EQ(3u, adpfs.size());
+ EXPECT_EQ("ADPF_DEFAULT", adpfs[0]->mName);
+ EXPECT_EQ("ADPF_SF", adpfs[1]->mName);
EXPECT_TRUE(adpfs[0]->mPidOn);
EXPECT_FALSE(adpfs[1]->mPidOn);
EXPECT_EQ(5.0, adpfs[0]->mPidPo);
@@ -834,16 +887,20 @@
EXPECT_EQ(5.0, adpfs[1]->mStaleTimeFactor);
EXPECT_TRUE(adpfs[0]->mHeuristicBoostOn.value());
EXPECT_FALSE(adpfs[1]->mHeuristicBoostOn.has_value());
- EXPECT_EQ(4U, adpfs[0]->mHBoostOnMissedCycles.value());
- EXPECT_FALSE(adpfs[1]->mHBoostOnMissedCycles.has_value());
- EXPECT_EQ(4.0, adpfs[0]->mHBoostOffMaxAvgRatio.value());
- EXPECT_FALSE(adpfs[1]->mHBoostOffMaxAvgRatio.has_value());
- EXPECT_EQ(2U, adpfs[0]->mHBoostOffMissedCycles.value());
- EXPECT_FALSE(adpfs[1]->mHBoostOffMissedCycles.has_value());
- EXPECT_EQ(0.5, adpfs[0]->mHBoostPidPuFactor.value());
- EXPECT_FALSE(adpfs[1]->mHBoostPidPuFactor.has_value());
- EXPECT_EQ(800U, adpfs[0]->mHBoostUclampMin.value());
- EXPECT_FALSE(adpfs[1]->mHBoostUclampMin.has_value());
+ EXPECT_EQ(4U, adpfs[0]->mHBoostModerateJankThreshold.value());
+ EXPECT_FALSE(adpfs[1]->mHBoostModerateJankThreshold.has_value());
+ EXPECT_EQ(4.0, adpfs[0]->mHBoostOffMaxAvgDurRatio.value());
+ EXPECT_FALSE(adpfs[1]->mHBoostOffMaxAvgDurRatio.has_value());
+ EXPECT_EQ(0.5, adpfs[0]->mHBoostSevereJankPidPu.value());
+ EXPECT_FALSE(adpfs[1]->mHBoostSevereJankPidPu.has_value());
+ EXPECT_EQ(2U, adpfs[0]->mHBoostSevereJankThreshold.value());
+ EXPECT_FALSE(adpfs[1]->mHBoostSevereJankThreshold.has_value());
+ EXPECT_EQ(480U, adpfs[0]->mHBoostUclampMinCeilingRange.value().first);
+ EXPECT_EQ(800U, adpfs[0]->mHBoostUclampMinCeilingRange.value().second);
+ EXPECT_FALSE(adpfs[1]->mHBoostUclampMinCeilingRange.has_value());
+ EXPECT_EQ(200U, adpfs[0]->mHBoostUclampMinFloorRange.value().first);
+ EXPECT_EQ(400U, adpfs[0]->mHBoostUclampMinFloorRange.value().second);
+ EXPECT_FALSE(adpfs[1]->mHBoostUclampMinFloorRange.has_value());
EXPECT_EQ(1.2, adpfs[0]->mJankCheckTimeFactor.value());
EXPECT_FALSE(adpfs[1]->mJankCheckTimeFactor.has_value());
EXPECT_EQ(25U, adpfs[0]->mLowFrameRateThreshold.value());
@@ -854,77 +911,195 @@
// Test parsing adpf configs with duplicate name
TEST_F(HintManagerTest, ParseAdpfConfigsDuplicateNameTest) {
- std::string from = "REFRESH_120FPS";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "REFRESH_60FPS");
- std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc_);
+ std::string json_doc = std::string(kJSON_ADPF);
+ std::string from = "\"Name\": \"ADPF_DEFAULT\"";
+ size_t start_pos = json_doc.find(from);
+ json_doc.replace(start_pos, from.length(), "\"Name\": \"ADPF_SF\"");
+ std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
EXPECT_EQ(0u, adpfs.size());
}
// Test parsing adpf configs without PID_Po
TEST_F(HintManagerTest, ParseAdpfConfigsWithoutPIDPoTest) {
+ std::string json_doc = std::string(kJSON_ADPF);
std::string from = "\"PID_Po\": 0,";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc_);
+ size_t start_pos = json_doc.find(from);
+ json_doc.replace(start_pos, from.length(), "");
+ std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
EXPECT_EQ(0u, adpfs.size());
}
// Test parsing adpf configs with partially missing heuristic boost config
TEST_F(HintManagerTest, ParseAdpfConfigsWithBrokenHBoostConfig) {
- std::string from = "\"HBoostUclampMin\": 800,";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc_);
+ std::string json_doc = std::string(kJSON_ADPF);
+ std::string from = "\"JankCheckTimeFactor\": 1.2";
+ size_t start_pos = json_doc.find(from);
+ json_doc.replace(start_pos, from.length(), "");
+ std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
EXPECT_EQ(0u, adpfs.size());
}
// Test hint/cancel/expire with json config
TEST_F(HintManagerTest, GetFromJSONAdpfConfigTest) {
TemporaryFile json_file;
- ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path)) << strerror(errno);
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
EXPECT_NE(nullptr, hm);
EXPECT_TRUE(hm->Start());
EXPECT_TRUE(hm->IsRunning());
// Get default Adpf Profile
- EXPECT_EQ("REFRESH_120FPS", hm->GetAdpfProfile()->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile()->mName);
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
// Set specific Adpf Profile
- EXPECT_FALSE(hm->SetAdpfProfile("NoSuchProfile"));
- EXPECT_TRUE(hm->SetAdpfProfile("REFRESH_60FPS"));
- EXPECT_EQ("REFRESH_60FPS", hm->GetAdpfProfile()->mName);
- EXPECT_TRUE(hm->SetAdpfProfile("REFRESH_120FPS"));
- EXPECT_EQ("REFRESH_120FPS", hm->GetAdpfProfile()->mName);
+ EXPECT_FALSE(hm->SetAdpfProfile("OTHER", "NoSuchProfile"));
+ // Test SF_PLAYING
+ EXPECT_TRUE(hm->SetAdpfProfile("SURFACEFLINGER", "SF_VIDEO_30FPS"));
+ EXPECT_EQ("SF_VIDEO_30FPS", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
+ // Test SF_RESET
+ EXPECT_TRUE(hm->SetAdpfProfile("SURFACEFLINGER", "ADPF_SF"));
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
}
TEST_F(HintManagerTest, IsAdpfProfileSupported) {
TemporaryFile json_file;
- ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path)) << strerror(errno);
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
EXPECT_NE(nullptr, hm);
// Check if given AdpfProfile supported
EXPECT_FALSE(hm->IsAdpfProfileSupported("NoSuchProfile"));
- EXPECT_TRUE(hm->IsAdpfProfileSupported("REFRESH_60FPS"));
- EXPECT_TRUE(hm->IsAdpfProfileSupported("REFRESH_120FPS"));
+ EXPECT_TRUE(hm->IsAdpfProfileSupported("ADPF_DEFAULT"));
+ EXPECT_TRUE(hm->IsAdpfProfileSupported("ADPF_SF"));
+}
+
+TEST_F(HintManagerTest, IsAdpfSupported) {
+ TemporaryFile json_file;
+ // Use json with AdpfConfig
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_TRUE(hm->IsAdpfSupported());
+
+ // Use a json doc without AdpfConfig
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_RAW, json_file.path)) << strerror(errno);
+ hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_FALSE(hm->IsAdpfSupported());
+}
+
+TEST_F(HintManagerTest, GetAdpfProfile) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile()->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("OTHER")->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("HWUI")->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("APP")->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("GAME")->mName);
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("NoSuchTag")->mName);
+}
+
+TEST_F(HintManagerTest, SetAdpfProfile) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_TRUE(hm->SetAdpfProfile("OTHER", "ADPF_DEFAULT"));
+ EXPECT_FALSE(hm->SetAdpfProfile("OTHER", "NoSuchProfile"));
+ EXPECT_FALSE(hm->SetAdpfProfile("NoSuchTag", "ADPF_DEFAULT"));
+ EXPECT_TRUE(hm->SetAdpfProfile("OTHER", "ADPF_SF"));
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("ADPF_SF")->mName);
+ EXPECT_TRUE(hm->SetAdpfProfile("OTHER", "SF_VIDEO_30FPS"));
+ EXPECT_EQ("SF_VIDEO_30FPS", hm->GetAdpfProfile("OTHER")->mName);
+}
+
+TEST_F(HintManagerTest, DoHintForEventNode) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_TRUE(hm->Start());
+ EXPECT_TRUE(hm->IsRunning());
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
+ hm->DoHint("SF_RESET");
+ std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
+}
+
+TEST_F(HintManagerTest, RegisterAdpfUpdateEventAndUnregister) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ EXPECT_TRUE(hm->Start());
+ EXPECT_TRUE(hm->IsRunning());
+ int count = 0;
+ std::string name;
+ AdpfCallback callback = [&](std::shared_ptr<AdpfConfig> profile) {
+ count++;
+ name = profile->mName;
+ };
+ // the callback should be invoked by DoHint().
+ hm->RegisterAdpfUpdateEvent("SURFACEFLINGER", &callback);
+ hm->DoHint("SF_RESET");
+ std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
+ EXPECT_EQ(1, count);
+ EXPECT_EQ("ADPF_DEFAULT", name);
+
+ // Unregister and DoHint('SF_PLAYING'). the callback shouldn't be called.
+ hm->UnregisterAdpfUpdateEvent("SURFACEFLINGER", &callback);
+ hm->EndHint("SF_RESET");
+ hm->DoHint("SF_PLAYING");
+ std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("SURFACEFLINGER")->mName);
+ EXPECT_EQ(1, count);
+ EXPECT_EQ("ADPF_DEFAULT", name);
+}
+
+TEST_F(HintManagerTest, GetAdpfProfileFromDoHint) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ // Check the default profile is at index:0.
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfileFromDoHint()->mName);
+ // Make sure that SetAdpfProfile wouldn't impact GetAdpfProfileFromDoHint().
+ EXPECT_TRUE(hm->SetAdpfProfile("OTHER", "ADPF_SF"));
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfile("OTHER")->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfileFromDoHint()->mName);
+}
+
+TEST_F(HintManagerTest, SetAdpfProfileFromDoHint) {
+ TemporaryFile json_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
+ HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
+ EXPECT_NE(nullptr, hm);
+ // Check the default profile is at index:0.
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("OTHER")->mName);
+ // Make sure that SetAdpfProfileFromDoHint wouldn't impact GetAdpfProfile().
+ EXPECT_TRUE(hm->SetAdpfProfileFromDoHint("ADPF_SF"));
+ EXPECT_EQ("ADPF_SF", hm->GetAdpfProfileFromDoHint()->mName);
+ EXPECT_EQ("ADPF_DEFAULT", hm->GetAdpfProfile("OTHER")->mName);
}
TEST_F(HintManagerTest, GpuConfigSupport) {
TemporaryFile json_file;
- ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path)) << strerror(errno);
+ ASSERT_TRUE(android::base::WriteStringToFile(kJSON_ADPF, json_file.path)) << strerror(errno);
HintManager *hm = HintManager::GetFromJSON(json_file.path, false);
ASSERT_TRUE(hm);
EXPECT_THAT(hm->gpu_sysfs_config_path(), Optional(Eq("/sys/devices/platform/123.abc")));
- ASSERT_TRUE(hm->SetAdpfProfile("REFRESH_120FPS"));
+ ASSERT_TRUE(hm->SetAdpfProfile("OTHER", "ADPF_DEFAULT"));
auto profile = hm->GetAdpfProfile();
EXPECT_THAT(profile->mGpuBoostOn, Optional(true));
- EXPECT_THAT(profile->mGpuBoostCapacityMax, Optional(300000));
+ EXPECT_THAT(profile->mGpuBoostCapacityMax, Optional(325000));
EXPECT_EQ(profile->mGpuCapacityLoadUpHeadroom, 1000);
- ASSERT_TRUE(hm->SetAdpfProfile("REFRESH_60FPS"));
+ ASSERT_TRUE(hm->SetAdpfProfile("OTHER", "ADPF_SF"));
profile = hm->GetAdpfProfile();
EXPECT_FALSE(profile->mGpuBoostOn);
EXPECT_FALSE(profile->mGpuBoostCapacityMax);
diff --git a/power-libperfmgr/libperfmgr/tests/NodeLooperThreadTest.cc b/power-libperfmgr/libperfmgr/tests/NodeLooperThreadTest.cc
index c2aa82b..8deccd4 100644
--- a/power-libperfmgr/libperfmgr/tests/NodeLooperThreadTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/NodeLooperThreadTest.cc
@@ -10,7 +10,7 @@
* 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 specic language governing permissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
diff --git a/power-libperfmgr/libperfmgr/tests/PropertyNodeTest.cc b/power-libperfmgr/libperfmgr/tests/PropertyNodeTest.cc
index 1780503..5dd88a1 100644
--- a/power-libperfmgr/libperfmgr/tests/PropertyNodeTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/PropertyNodeTest.cc
@@ -10,7 +10,7 @@
* 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 specic language governing permissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
diff --git a/power-libperfmgr/libperfmgr/tests/RequestGroupTest.cc b/power-libperfmgr/libperfmgr/tests/RequestGroupTest.cc
index c23c821..47750d4 100644
--- a/power-libperfmgr/libperfmgr/tests/RequestGroupTest.cc
+++ b/power-libperfmgr/libperfmgr/tests/RequestGroupTest.cc
@@ -10,7 +10,7 @@
* 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 specic language governing permissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
diff --git a/powerstats/PowerStatsAidl.cpp b/powerstats/PowerStatsAidl.cpp
index 455b26c..bd7ba36 100644
--- a/powerstats/PowerStatsAidl.cpp
+++ b/powerstats/PowerStatsAidl.cpp
@@ -285,16 +285,16 @@
const char *dataFormatDelta = " %16s %18s %13" PRIu64 " ms (%14" PRId64 ") %15" PRIu64
" (%16" PRId64 ") %14" PRIu64 " ms (%14" PRId64 ")\n";
- // Construct maps to entity and state names
- std::unordered_map<int32_t, std::string> entityNames;
- std::unordered_map<int32_t, std::unordered_map<int32_t, std::string>> stateNames;
- getEntityStateNames(&entityNames, &stateNames);
-
oss << "\n============= PowerStats HAL 2.0 state residencies ==============\n";
std::vector<StateResidencyResult> results;
getStateResidency({}, &results);
+ // Construct maps to entity and state names
+ std::unordered_map<int32_t, std::string> entityNames;
+ std::unordered_map<int32_t, std::unordered_map<int32_t, std::string>> stateNames;
+ getEntityStateNames(&entityNames, &stateNames);
+
if (delta) {
static std::vector<StateResidencyResult> prevResults;
::android::base::boot_clock::time_point curTime = ::android::base::boot_clock::now();
diff --git a/radio/gril_carrier_nv_headers/inc/gril_carrier_nv.h b/radio/gril_carrier_nv_headers/inc/gril_carrier_nv.h
index 698d430..57b851f 100644
--- a/radio/gril_carrier_nv_headers/inc/gril_carrier_nv.h
+++ b/radio/gril_carrier_nv_headers/inc/gril_carrier_nv.h
@@ -93,6 +93,7 @@
GRIL_CARRIER_CSPIRE = 0x59,
GRIL_CARRIER_CBRS = 0x64,
GRIL_CARRIER_PLUS_PL = 0xB0,
+ GRIL_CARRIER_DNA_FI = 0xB2,
GRIL_CARRIER_CRICKET_5G = 0xE2,
GRIL_CARRIER_USCC_FI = 0xFB,
GRIL_CARRIER_SPRINT_FI = 0xFC,
diff --git a/recovery/Android.bp b/recovery/Android.bp
index d752ee5..79f4e38 100644
--- a/recovery/Android.bp
+++ b/recovery/Android.bp
@@ -47,5 +47,6 @@
shared_libs: [
"libbase",
"librecovery_ui",
+ "libboot_control_client",
],
}
diff --git a/recovery/recovery_watch_ui.cpp b/recovery/recovery_watch_ui.cpp
index 2fe0a46..b6e7275 100644
--- a/recovery/recovery_watch_ui.cpp
+++ b/recovery/recovery_watch_ui.cpp
@@ -14,7 +14,14 @@
* limitations under the License.
*/
+#include <BootControlClient.h>
+#include <android-base/endian.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <dlfcn.h>
+#include <misc_writer/misc_writer.h>
+#include <recovery_ui/device.h>
+#include <recovery_ui/wear_ui.h>
#include <stdint.h>
#include <string.h>
@@ -22,13 +29,6 @@
#include <string_view>
#include <vector>
-#include <android-base/endian.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <misc_writer/misc_writer.h>
-#include <recovery_ui/device.h>
-#include <recovery_ui/wear_ui.h>
-
namespace android {
namespace hardware {
namespace google {
@@ -67,6 +67,24 @@
public:
explicit PixelWatchDevice(::WearRecoveryUI* const ui) : ::Device(ui) {}
+ bool PreWipeData() override {
+ uint32_t currentSlot = 0;
+ const auto module = android::hal::BootControlClient::WaitForService();
+ if (module == nullptr) {
+ LOG(ERROR) << "Error getting bootctrl module, slot attributes not reset";
+ } else {
+ // Reset current slot attributes
+ currentSlot = module->GetCurrentSlot();
+ LOG(INFO) << "Slot attributes reset for slot " << currentSlot;
+ const auto result = module->SetActiveBootSlot(currentSlot);
+ if (!result.IsOk()) {
+ LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << currentSlot;
+ }
+ }
+
+ // Loogging errors is sufficient, we don't want to block Wipe Data on this.
+ return true;
+ }
/** Hook to wipe user data not stored in /data */
bool PostWipeData() override {
// Try to do everything but report a failure if anything wasn't successful
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index b71fd27..ef977b2 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -397,9 +397,14 @@
*dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " ";
}
*dump_buf << "]" << std::endl;
- *dump_buf << " K_i: [";
+ *dump_buf << " K_io: [";
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
- *dump_buf << name_info_pair.second.throttling_info->k_i[i] << " ";
+ *dump_buf << name_info_pair.second.throttling_info->k_io[i] << " ";
+ }
+ *dump_buf << "]" << std::endl;
+ *dump_buf << " K_iu: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ *dump_buf << name_info_pair.second.throttling_info->k_iu[i] << " ";
}
*dump_buf << "]" << std::endl;
*dump_buf << " K_d: [";
@@ -738,7 +743,7 @@
dump_buf << "getCurrentTemperatures:" << std::endl;
Temperature temp_2_0;
for (const auto &name_info_pair : map) {
- thermal_helper_->readTemperature(name_info_pair.first, &temp_2_0, nullptr, true);
+ thermal_helper_->readTemperature(name_info_pair.first, &temp_2_0, true);
dump_buf << " Type: " << toString(temp_2_0.type)
<< " Name: " << name_info_pair.first << " CurrentValue: " << temp_2_0.value
<< " ThrottlingStatus: " << toString(temp_2_0.throttlingStatus)
diff --git a/thermal/tests/mock_thermal_helper.h b/thermal/tests/mock_thermal_helper.h
index e5daa7f..9ba6e7f 100644
--- a/thermal/tests/mock_thermal_helper.h
+++ b/thermal/tests/mock_thermal_helper.h
@@ -38,9 +38,7 @@
MOCK_METHOD(bool, emulSeverity, (std::string_view, const int, const bool), (override));
MOCK_METHOD(bool, emulClear, (std::string_view), (override));
MOCK_METHOD(bool, isInitializedOk, (), (const, override));
- MOCK_METHOD(bool, readTemperature,
- (std::string_view, Temperature *out,
- (std::pair<ThrottlingSeverity, ThrottlingSeverity> *), const bool),
+ MOCK_METHOD(bool, readTemperature, (std::string_view, Temperature *out, const bool),
(override));
MOCK_METHOD(bool, readTemperatureThreshold, (std::string_view, TemperatureThreshold *),
(const, override));
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index 65d6f66..a7f878c 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -153,11 +153,15 @@
::android::base::GetBoolProperty(kThermalDisabledProperty.data(), false);
bool ret = true;
Json::Value config;
- if (!ParseThermalConfig(config_path, &config)) {
+ std::unordered_set<std::string> loaded_config_paths;
+ if (!ParseThermalConfig(config_path, &config, &loaded_config_paths)) {
LOG(ERROR) << "Failed to read JSON config";
ret = false;
}
+ const std::string &comment = config["Comment"].asString();
+ LOG(INFO) << "Comment: " << comment;
+
if (!ParseCoolingDevice(config, &cooling_device_info_map_)) {
LOG(ERROR) << "Failed to parse cooling device info config";
ret = false;
@@ -199,6 +203,7 @@
.prev_cold_severity = ThrottlingSeverity::NONE,
.last_update_time = boot_clock::time_point::min(),
.thermal_cached = {NAN, boot_clock::time_point::min()},
+ .pending_notification = false,
.override_status = {nullptr, false, false},
};
@@ -268,6 +273,20 @@
}
}
}
+
+ // Check if the severity reference sensor is valid
+ if (name_status_pair.second.severity_reference != "") {
+ if (sensor_info_map_.contains(name_status_pair.second.severity_reference)) {
+ sensor_info_map_[name_status_pair.second.severity_reference].is_watch = true;
+ LOG(INFO) << "Enable is_watch for " << name_status_pair.first
+ << "'s severity reference sensor: "
+ << name_status_pair.second.severity_reference;
+ } else {
+ LOG(ERROR) << name_status_pair.first << "'s severity reference sensor: "
+ << name_status_pair.second.severity_reference << " is invalid";
+ ret = false;
+ }
+ }
}
// Check predictor info config
if (name_status_pair.second.predictor_info != nullptr) {
@@ -362,15 +381,14 @@
std::string path =
::android::base::StringPrintf("%s/%s%d/%s", kThermalSensorsRoot.data(),
kSensorPrefix.data(), tz_id, kThermalNameFile.data());
- LOG(INFO) << "TZ Path: " << path;
if (!::android::base::ReadFileToString(path, &tz_type)) {
- LOG(ERROR) << "Failed to read sensor: " << tz_type;
+ LOG(ERROR) << "Failed to read sensor from: " << path;
return false;
}
// Strip the newline.
*type = ::android::base::Trim(tz_type);
- LOG(INFO) << "TZ type: " << *type;
+ LOG(INFO) << "TZ path: " << path << " type: " << *type;
return true;
}
@@ -499,10 +517,8 @@
return true;
}
-bool ThermalHelperImpl::readTemperature(
- std::string_view sensor_name, Temperature *out,
- std::pair<ThrottlingSeverity, ThrottlingSeverity> *throttling_status,
- const bool force_no_cache) {
+bool ThermalHelperImpl::readTemperature(std::string_view sensor_name, Temperature *out,
+ const bool force_no_cache) {
// Return fail if the thermal sensor cannot be read.
float temp = NAN;
std::map<std::string, float> sensor_log_map;
@@ -519,6 +535,7 @@
LOG(INFO) << "Sensor " << sensor_name.data() << " temperature is nan.";
return false;
}
+ const auto severity_reference = getSeverityReference(sensor_name.data());
const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
out->type = sensor_info.type;
@@ -527,45 +544,61 @@
std::pair<ThrottlingSeverity, ThrottlingSeverity> status =
std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE);
+
// Only update status if the thermal sensor is being monitored
- if (sensor_info.is_watch) {
- ThrottlingSeverity prev_hot_severity, prev_cold_severity;
- {
- // reader lock, readTemperature will be called in Binder call and the watcher thread.
- std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
- prev_hot_severity = sensor_status.prev_hot_severity;
- prev_cold_severity = sensor_status.prev_cold_severity;
- }
+ if (!sensor_info.is_watch) {
+ return true;
+ }
+ ThrottlingSeverity prev_hot_severity, prev_cold_severity;
+ {
+ std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ prev_hot_severity = sensor_status.prev_hot_severity;
+ prev_cold_severity = sensor_status.prev_cold_severity;
status = getSeverityFromThresholds(sensor_info.hot_thresholds, sensor_info.cold_thresholds,
sensor_info.hot_hysteresis, sensor_info.cold_hysteresis,
prev_hot_severity, prev_cold_severity, out->value);
- }
- if (throttling_status) {
- *throttling_status = status;
- }
-
- if (sensor_status.override_status.emul_temp != nullptr &&
- sensor_status.override_status.emul_temp->severity >= 0) {
- std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
- out->throttlingStatus =
- static_cast<ThrottlingSeverity>(sensor_status.override_status.emul_temp->severity);
- } else {
out->throttlingStatus =
static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
? status.first
: status.second;
- }
- if (sensor_info.is_watch) {
- std::ostringstream sensor_log;
- for (const auto &sensor_log_pair : sensor_log_map) {
- sensor_log << sensor_log_pair.first << ":" << sensor_log_pair.second << " ";
+
+ if (status.first != sensor_status.prev_hot_severity) {
+ sensor_status.prev_hot_severity = status.first;
}
- // Update sensor temperature time in state
- thermal_stats_helper_.updateSensorTempStatsBySeverity(sensor_name, out->throttlingStatus);
- LOG(INFO) << sensor_name.data() << ":" << out->value << " raw data: " << sensor_log.str();
+ if (status.second != sensor_status.prev_cold_severity) {
+ sensor_status.prev_cold_severity = status.second;
+ }
+
+ out->throttlingStatus = std::max(out->throttlingStatus, severity_reference);
+
+ if (sensor_status.override_status.emul_temp != nullptr &&
+ sensor_status.override_status.emul_temp->severity >= 0) {
+ out->throttlingStatus = static_cast<ThrottlingSeverity>(
+ sensor_status.override_status.emul_temp->severity);
+ }
+
+ if (sensor_status.severity != out->throttlingStatus) {
+ sensor_status.severity = out->throttlingStatus;
+ sensor_status.pending_notification = true;
+ }
}
+ std::ostringstream sensor_log;
+ for (const auto &sensor_log_pair : sensor_log_map) {
+ sensor_log << sensor_log_pair.first << ":" << sensor_log_pair.second << " ";
+ }
+ // Update sensor temperature time in state
+ thermal_stats_helper_.updateSensorTempStatsBySeverity(sensor_name, out->throttlingStatus);
+ if (out->throttlingStatus >= sensor_info.log_level) {
+ LOG(INFO) << sensor_name.data() << ":" << out->value << " raw data: " << sensor_log.str();
+ } else {
+ LOG(VERBOSE) << sensor_name.data() << ":" << out->value
+ << " raw data: " << sensor_log.str();
+ }
+ ATRACE_INT((sensor_name.data() + std::string("-severity")).c_str(),
+ static_cast<int>(out->throttlingStatus));
+
return true;
}
@@ -750,23 +783,31 @@
return false;
}
- std::string state2power_path = ::android::base::StringPrintf(
- "%s/%s", path.data(), kCoolingDeviceState2powerSuffix.data());
- std::string state2power_str;
- if (::android::base::ReadFileToString(state2power_path, &state2power_str)) {
- LOG(INFO) << "Cooling device " << cooling_device_info_pair.first
- << " use state2power read from sysfs";
- cooling_device_info_pair.second.state2power.clear();
+ // Get cooling device state2power table from sysfs if not defined in config
+ if (!cooling_device_info_pair.second.state2power.size()) {
+ std::string state2power_path = ::android::base::StringPrintf(
+ "%s/%s", path.data(), kCoolingDeviceState2powerSuffix.data());
+ std::string state2power_str;
+ if (::android::base::ReadFileToString(state2power_path, &state2power_str)) {
+ LOG(INFO) << "Cooling device " << cooling_device_info_pair.first
+ << " use State2power read from sysfs";
+ std::stringstream power(state2power_str);
+ unsigned int power_number;
+ while (power >> power_number) {
+ cooling_device_info_pair.second.state2power.push_back(
+ static_cast<float>(power_number));
+ }
+ }
+ }
- std::stringstream power(state2power_str);
- unsigned int power_number;
- int i = 0;
- while (power >> power_number) {
- cooling_device_info_pair.second.state2power.push_back(
- static_cast<float>(power_number));
- LOG(INFO) << "Cooling device " << cooling_device_info_pair.first << " state:" << i
- << " power: " << power_number;
- i++;
+ // Check if there's any wrong ordered state2power value to avoid cdev stuck issue
+ for (size_t i = 0; i < cooling_device_info_pair.second.state2power.size(); ++i) {
+ LOG(INFO) << "Cooling device " << cooling_device_info_pair.first << " state:" << i
+ << " power: " << cooling_device_info_pair.second.state2power[i];
+ if (i > 0 && cooling_device_info_pair.second.state2power[i] >
+ cooling_device_info_pair.second.state2power[i - 1]) {
+ LOG(ERROR) << "Higher power with higher state on cooling device "
+ << cooling_device_info_pair.first << "'s state" << i;
}
}
@@ -910,7 +951,7 @@
if (filterCallback && !name_info_pair.second.send_cb) {
continue;
}
- if (readTemperature(name_info_pair.first, &temp, nullptr, false)) {
+ if (readTemperature(name_info_pair.first, &temp, false)) {
ret.emplace_back(std::move(temp));
} else {
LOG(ERROR) << __func__
@@ -964,6 +1005,25 @@
return ret.size() > 0;
}
+ThrottlingSeverity ThermalHelperImpl::getSeverityReference(std::string_view sensor_name) {
+ if (!sensor_info_map_.contains(sensor_name.data())) {
+ return ThrottlingSeverity::NONE;
+ }
+ const std::string &severity_reference =
+ sensor_info_map_.at(sensor_name.data()).severity_reference;
+ if (severity_reference == "") {
+ return ThrottlingSeverity::NONE;
+ }
+
+ Temperature temp;
+ if (!readTemperature(severity_reference, &temp, false)) {
+ return ThrottlingSeverity::NONE;
+ }
+ LOG(VERBOSE) << sensor_name << "'s severity reference " << severity_reference
+ << " reading:" << toString(temp.throttlingStatus);
+ return temp.throttlingStatus;
+}
+
bool ThermalHelperImpl::readDataByType(std::string_view sensor_data, float *reading_value,
const SensorFusionType type, const bool force_no_cache,
std::map<std::string, float> *sensor_log_map) {
@@ -986,6 +1046,15 @@
case SensorFusionType::CONSTANT:
*reading_value = std::atof(sensor_data.data());
break;
+ case SensorFusionType::CDEV:
+ int max_state;
+ if (thermal_throttling_.getCdevMaxRequest(sensor_data.data(), &max_state)) {
+ *reading_value = max_state;
+ break;
+ } else {
+ return false;
+ }
+ break;
default:
break;
}
@@ -1322,21 +1391,30 @@
}
// This is called in the different thread context and will update sensor_status
-// uevent_sensors is the set of sensors which trigger uevent from thermal core driver.
+// uevent_sensors_map maps sensor which trigger uevent from thermal core driver to the temperature
+// read from uevent.
std::chrono::milliseconds ThermalHelperImpl::thermalWatcherCallbackFunc(
- const std::set<std::string> &uevent_sensors) {
+ const std::unordered_map<std::string, float> &uevent_sensor_map) {
std::vector<Temperature> temps;
std::vector<std::string> cooling_devices_to_update;
boot_clock::time_point now = boot_clock::now();
auto min_sleep_ms = std::chrono::milliseconds::max();
bool power_data_is_updated = false;
+ for (const auto &[sensor, temp] : uevent_sensor_map) {
+ if (!std::isnan(temp)) {
+ std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ sensor_status_map_[sensor].thermal_cached.temp = temp;
+ sensor_status_map_[sensor].thermal_cached.timestamp = now;
+ }
+ }
+
ATRACE_CALL();
+ // Go through all virtual and physical sensor and update if needed
for (auto &name_status_pair : sensor_status_map_) {
bool force_update = false;
bool force_no_cache = false;
Temperature temp;
- TemperatureThreshold threshold;
SensorStatus &sensor_status = name_status_pair.second;
const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first);
bool max_throttling = false;
@@ -1366,28 +1444,38 @@
}
}
}
- // Check if the sensor need to be updated
+ // Force update if it's first time we update temperature value after device boot
if (sensor_status.last_update_time == boot_clock::time_point::min()) {
force_update = true;
+
} else {
+ // Handle other update event
time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now - sensor_status.last_update_time);
- if (uevent_sensors.size()) {
+ // Update triggered from genlink or uevent
+ if (uevent_sensor_map.size()) {
+ // Checking virtual sensor
if (sensor_info.virtual_sensor_info != nullptr) {
for (size_t i = 0; i < sensor_info.virtual_sensor_info->trigger_sensors.size();
i++) {
- if (uevent_sensors.find(
+ if (uevent_sensor_map.find(
sensor_info.virtual_sensor_info->trigger_sensors[i]) !=
- uevent_sensors.end()) {
+ uevent_sensor_map.end()) {
force_update = true;
break;
}
}
- } else if (uevent_sensors.find(name_status_pair.first) != uevent_sensors.end()) {
+ } else if (uevent_sensor_map.find(name_status_pair.first) !=
+ uevent_sensor_map.end()) {
+ // Checking physical sensor
force_update = true;
- force_no_cache = true;
+ if (std::isnan(uevent_sensor_map.at(name_status_pair.first))) {
+ // Handle the case that uevent does not contain temperature
+ force_no_cache = true;
+ }
}
} else if (time_elapsed_ms > sleep_ms) {
+ // Update triggered from normal polling cylce
force_update = true;
}
}
@@ -1415,32 +1503,20 @@
}
std::pair<ThrottlingSeverity, ThrottlingSeverity> throttling_status;
- if (!readTemperature(name_status_pair.first, &temp, &throttling_status, force_no_cache)) {
+ if (!readTemperature(name_status_pair.first, &temp, force_no_cache)) {
LOG(ERROR) << __func__
<< ": error reading temperature for sensor: " << name_status_pair.first;
continue;
}
- if (!readTemperatureThreshold(name_status_pair.first, &threshold)) {
- LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
- << name_status_pair.first;
- continue;
- }
{
- // writer lock
std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
- if (throttling_status.first != sensor_status.prev_hot_severity) {
- sensor_status.prev_hot_severity = throttling_status.first;
- }
- if (throttling_status.second != sensor_status.prev_cold_severity) {
- sensor_status.prev_cold_severity = throttling_status.second;
- }
- if (temp.throttlingStatus != sensor_status.severity) {
+ if (sensor_status.pending_notification) {
temps.push_back(temp);
- sensor_status.severity = temp.throttlingStatus;
sleep_ms = (sensor_status.severity != ThrottlingSeverity::NONE)
? sensor_info.passive_delay
: sensor_info.polling_delay;
+ sensor_status.pending_notification = false;
}
}
@@ -1481,10 +1557,6 @@
sensor_status.last_update_time = now;
}
- if (!cooling_devices_to_update.empty()) {
- updateCoolingDevices(cooling_devices_to_update);
- }
-
if (!temps.empty()) {
for (const auto &t : temps) {
if (sensor_info_map_.at(t.name).send_cb && cb_) {
@@ -1497,6 +1569,10 @@
}
}
+ if (!cooling_devices_to_update.empty()) {
+ updateCoolingDevices(cooling_devices_to_update);
+ }
+
int count_failed_reporting = thermal_stats_helper_.reportStats();
if (count_failed_reporting != 0) {
LOG(ERROR) << "Failed to report " << count_failed_reporting << " thermal stats";
diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h
index 7c9d282..bd64505 100644
--- a/thermal/thermal-helper.h
+++ b/thermal/thermal-helper.h
@@ -72,6 +72,7 @@
ThrottlingSeverity prev_cold_severity;
boot_clock::time_point last_update_time;
ThermalSample thermal_cached;
+ bool pending_notification;
OverrideStatus override_status;
};
@@ -90,10 +91,8 @@
const bool max_throttling) = 0;
virtual bool emulClear(std::string_view target_sensor) = 0;
virtual bool isInitializedOk() const = 0;
- virtual bool readTemperature(
- std::string_view sensor_name, Temperature *out,
- std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
- const bool force_sysfs = false) = 0;
+ virtual bool readTemperature(std::string_view sensor_name, Temperature *out,
+ const bool force_sysfs = false) = 0;
virtual bool readTemperatureThreshold(std::string_view sensor_name,
TemperatureThreshold *out) const = 0;
virtual bool readCoolingDevice(std::string_view cooling_device, CoolingDevice *out) const = 0;
@@ -141,10 +140,8 @@
bool isInitializedOk() const override { return is_initialized_; }
// Read the temperature of a single sensor.
- bool readTemperature(
- std::string_view sensor_name, Temperature *out,
- std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
- const bool force_sysfs = false) override;
+ bool readTemperature(std::string_view sensor_name, Temperature *out,
+ const bool force_sysfs = false) override;
bool readTemperatureThreshold(std::string_view sensor_name,
TemperatureThreshold *out) const override;
@@ -205,7 +202,7 @@
void clearAllThrottling();
// For thermal_watcher_'s polling thread, return the sleep interval
std::chrono::milliseconds thermalWatcherCallbackFunc(
- const std::set<std::string> &uevent_sensors);
+ const std::unordered_map<std::string, float> &uevent_sensor_map);
// Return hot and cold severity status as std::pair
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
@@ -229,6 +226,8 @@
void maxCoolingRequestCheck(
std::unordered_map<std::string, BindedCdevInfo> *binded_cdev_info_map);
void checkUpdateSensorForEmul(std::string_view target_sensor, const bool max_throttling);
+ ThrottlingSeverity getSeverityReference(std::string_view sensor_name);
+
sp<ThermalWatcher> thermal_watcher_;
PowerFiles power_files_;
ThermalFiles thermal_sensors_;
diff --git a/thermal/utils/thermal_info.cpp b/thermal/utils/thermal_info.cpp
index 640da47..92ba07a 100644
--- a/thermal/utils/thermal_info.cpp
+++ b/thermal/utils/thermal_info.cpp
@@ -193,12 +193,14 @@
return stream << "ODPM";
case SensorFusionType::CONSTANT:
return stream << "CONSTANT";
+ case SensorFusionType::CDEV:
+ return stream << "CDEV";
default:
return stream << "UNDEFINED";
}
}
-bool ParseThermalConfig(std::string_view config_path, Json::Value *config) {
+bool LoadThermalConfig(std::string_view config_path, Json::Value *config) {
std::string json_doc;
if (!::android::base::ReadFileToString(config_path.data(), &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path;
@@ -214,6 +216,62 @@
return true;
}
+void MergeConfigEntries(Json::Value *config, Json::Value *sub_config,
+ std::string_view member_name) {
+ Json::Value &config_entries = (*config)[member_name.data()];
+ Json::Value &sub_config_entries = (*sub_config)[member_name.data()];
+ std::unordered_set<std::string> config_entries_set;
+
+ if (sub_config_entries.size() == 0) {
+ return;
+ }
+
+ for (Json::Value::ArrayIndex i = 0; i < config_entries.size(); i++) {
+ config_entries_set.insert(config_entries[i]["Name"].asString());
+ }
+
+ // Iterate through subconfig and add entries not found in main config
+ for (Json::Value::ArrayIndex i = 0; i < sub_config_entries.size(); ++i) {
+ if (config_entries_set.count(sub_config_entries[i]["Name"].asString()) == 0) {
+ config_entries.append(sub_config_entries[i]);
+ } else {
+ LOG(INFO) << "Base config entry " << sub_config_entries[i]["Name"].asString()
+ << " is overwritten in main config";
+ }
+ }
+}
+
+bool ParseThermalConfig(std::string_view config_path, Json::Value *config,
+ std::unordered_set<std::string> *loaded_config_paths) {
+ if (loaded_config_paths->count(config_path.data())) {
+ LOG(ERROR) << "Circular dependency detected in config " << config_path;
+ return false;
+ }
+
+ if (!LoadThermalConfig(config_path, config)) {
+ LOG(ERROR) << "Failed to read JSON config at " << config_path;
+ return false;
+ }
+
+ loaded_config_paths->insert(config_path.data());
+
+ Json::Value sub_configs_paths = (*config)["Include"];
+ for (Json::Value::ArrayIndex i = 0; i < sub_configs_paths.size(); ++i) {
+ const std::string sub_configs_path = "/vendor/etc/" + sub_configs_paths[i].asString();
+ Json::Value sub_config;
+
+ if (!ParseThermalConfig(sub_configs_path, &sub_config, loaded_config_paths)) {
+ return false;
+ }
+
+ MergeConfigEntries(config, &sub_config, "Sensors");
+ MergeConfigEntries(config, &sub_config, "CoolingDevices");
+ MergeConfigEntries(config, &sub_config, "PowerRails");
+ }
+
+ return true;
+}
+
bool ParseOffsetThresholds(const std::string_view name, const Json::Value &sensor,
std::vector<float> *offset_thresholds,
std::vector<float> *offset_values) {
@@ -322,6 +380,8 @@
linked_sensors_type.emplace_back(SensorFusionType::ODPM);
} else if (values[j].asString().compare("CONSTANT") == 0) {
linked_sensors_type.emplace_back(SensorFusionType::CONSTANT);
+ } else if (values[j].asString().compare("CDEV") == 0) {
+ linked_sensors_type.emplace_back(SensorFusionType::CDEV);
} else {
LOG(ERROR) << "Sensor[" << name << "] has invalid CombinationType settings "
<< values[j].asString();
@@ -833,8 +893,10 @@
k_po.fill(0.0);
std::array<float, kThrottlingSeverityCount> k_pu;
k_pu.fill(0.0);
- std::array<float, kThrottlingSeverityCount> k_i;
- k_i.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> k_io;
+ k_io.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> k_iu;
+ k_iu.fill(0.0);
std::array<float, kThrottlingSeverityCount> k_d;
k_d.fill(0.0);
std::array<float, kThrottlingSeverityCount> i_max;
@@ -869,13 +931,33 @@
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu";
return false;
}
- LOG(INFO) << "Start to parse"
- << " Sensor[" << name << "]'s K_I";
- if (sensor["PIDInfo"]["K_I"].empty() ||
- !getFloatFromJsonValues(sensor["PIDInfo"]["K_I"], &k_i, false, false)) {
- LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_I";
+ if (!sensor["PIDInfo"]["K_I"].empty()) {
+ if (!sensor["PIDInfo"]["K_Io"].empty() || !sensor["PIDInfo"]["K_Iu"].empty()) {
+ LOG(ERROR) << "Sensor[" << name << "]: K_Io or K_Iu cannot coexist with K_I";
+ return false;
+ }
+ LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_I";
+ if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_I"], &k_io, false, false) ||
+ !getFloatFromJsonValues(sensor["PIDInfo"]["K_I"], &k_iu, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_I";
+ return false;
+ }
+ } else if (!sensor["PIDInfo"]["K_Io"].empty() && !sensor["PIDInfo"]["K_Iu"].empty()) {
+ LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_Io";
+ if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_Io"], &k_io, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Io";
+ return false;
+ }
+ LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_Iu";
+ if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_Iu"], &k_iu, false, false)) {
+ LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Iu";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Sensor[" << name << "]: No K_I related settings";
return false;
}
+
LOG(INFO) << "Start to parse"
<< " Sensor[" << name << "]'s K_D";
if (sensor["PIDInfo"]["K_D"].empty() ||
@@ -939,9 +1021,10 @@
bool valid_pid_combination = false;
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
if (!std::isnan(s_power[j])) {
- if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_i[j]) ||
- std::isnan(k_d[j]) || std::isnan(i_max[j]) || std::isnan(max_alloc_power[j]) ||
- std::isnan(min_alloc_power[j]) || std::isnan(i_cutoff[j])) {
+ if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_io[j]) ||
+ std::isnan(k_iu[j]) || std::isnan(k_d[j]) || std::isnan(i_max[j]) ||
+ std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) ||
+ std::isnan(i_cutoff[j])) {
valid_pid_combination = false;
break;
} else {
@@ -1025,7 +1108,7 @@
}
excluded_power_info_map[power_rail] = power_weight;
}
- throttling_info->reset(new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power,
+ throttling_info->reset(new ThrottlingInfo{k_po, k_pu, k_io, k_iu, k_d, i_max, max_alloc_power,
min_alloc_power, s_power, i_cutoff, i_default,
i_default_pct, tran_cycle, excluded_power_info_map,
binded_cdev_info_map, profile_map});
@@ -1041,12 +1124,14 @@
LOG(INFO) << "Start reading ScalingAvailableFrequenciesPath from config";
for (Json::Value::ArrayIndex i = 0; i < cdevs.size(); ++i) {
- if (cdevs[i]["ScalingAvailableFrequenciesPath"].empty()) {
+ if (cdevs[i]["ScalingAvailableFrequenciesPath"].empty() ||
+ cdevs[i]["isDisabled"].asBool()) {
continue;
}
const std::string &path = cdevs[i]["ScalingAvailableFrequenciesPath"].asString();
const std::string &name = cdevs[i]["Name"].asString();
+
LOG(INFO) << "Cdev[" << name << "]'s scaling frequency path: " << path;
std::string scaling_frequency_str;
if (::android::base::ReadFileToString(path, &scaling_frequency_str)) {
@@ -1090,6 +1175,11 @@
return false;
}
+ if (sensors[i]["isDisabled"].asBool()) {
+ LOG(INFO) << "sensors[" << name << "] is disabled. Skipping parsing";
+ continue;
+ }
+
auto result = sensors_name_parsed.insert(name);
if (!result.second) {
LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
@@ -1134,6 +1224,17 @@
LOG(INFO) << "Sensor[" << name << "]'s Hidden: " << std::boolalpha << is_hidden
<< std::noboolalpha;
+ ThrottlingSeverity log_level = ThrottlingSeverity::NONE;
+ if (!sensors[i]["LogLevel"].empty()) {
+ const auto level = sensors[i]["LogLevel"].asInt();
+ if (level > static_cast<int>(ThrottlingSeverity::SHUTDOWN)) {
+ LOG(ERROR) << "Sensor[" << name << "]'s LogLevel is invalid";
+ } else {
+ log_level = static_cast<ThrottlingSeverity>(level);
+ }
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s LogLevel: " << toString(log_level);
+
std::array<float, kThrottlingSeverityCount> hot_thresholds;
hot_thresholds.fill(NAN);
std::array<float, kThrottlingSeverityCount> cold_thresholds;
@@ -1279,6 +1380,12 @@
LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path;
}
+ std::string severity_reference;
+ if (!sensors[i]["SeverityReference"].empty()) {
+ severity_reference = sensors[i]["SeverityReference"].asString();
+ LOG(INFO) << "Sensor[" << name << "]'s SeverityReference: " << temp_path;
+ }
+
float vr_threshold = NAN;
if (!sensors[i]["VrThreshold"].empty()) {
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
@@ -1371,6 +1478,7 @@
.hot_hysteresis = hot_hysteresis,
.cold_hysteresis = cold_hysteresis,
.temp_path = temp_path,
+ .severity_reference = severity_reference,
.vr_threshold = vr_threshold,
.multiplier = multiplier,
.polling_delay = polling_delay,
@@ -1381,6 +1489,7 @@
.send_powerhint = send_powerhint,
.is_watch = is_watch,
.is_hidden = is_hidden,
+ .log_level = log_level,
.virtual_sensor_info = std::move(virtual_sensor_info),
.throttling_info = std::move(throttling_info),
.predictor_info = std::move(predictor_info),
@@ -1407,6 +1516,11 @@
return false;
}
+ if (cooling_devices[i]["isDisabled"].asBool()) {
+ LOG(INFO) << "CoolingDevice[" << name << "] is disabled. Skipping parsing";
+ continue;
+ }
+
auto result = cooling_devices_name_parsed.insert(name.data());
if (!result.second) {
LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
@@ -1434,19 +1548,14 @@
std::vector<float> state2power;
Json::Value values = cooling_devices[i]["State2Power"];
if (values.size()) {
+ LOG(INFO) << "Cooling device " << name << " use State2power read from config";
state2power.reserve(values.size());
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
state2power.emplace_back(getFloatFromValue(values[j]));
- LOG(INFO) << "Cooling device[" << name << "]'s Power2State[" << j
- << "]: " << state2power[j];
- if (j > 0 && state2power[j] < state2power[j - 1]) {
- LOG(ERROR) << "Higher power with higher state on cooling device " << name
- << "'s state" << j;
- }
}
} else {
LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name
- << " does not support State2Power";
+ << " does not support State2Power in thermal config";
}
const std::string &power_rail = cooling_devices[i]["PowerRail"].asString();
diff --git a/thermal/utils/thermal_info.h b/thermal/utils/thermal_info.h
index ada97a3..dc1f6cb 100644
--- a/thermal/utils/thermal_info.h
+++ b/thermal/utils/thermal_info.h
@@ -127,6 +127,7 @@
SENSOR = 0,
ODPM,
CONSTANT,
+ CDEV,
};
std::ostream &operator<<(std::ostream &os, const SensorFusionType &sensor_fusion_type);
@@ -191,7 +192,8 @@
struct ThrottlingInfo {
ThrottlingArray k_po;
ThrottlingArray k_pu;
- ThrottlingArray k_i;
+ ThrottlingArray k_io;
+ ThrottlingArray k_iu;
ThrottlingArray k_d;
ThrottlingArray i_max;
ThrottlingArray max_alloc_power;
@@ -213,6 +215,7 @@
ThrottlingArray hot_hysteresis;
ThrottlingArray cold_hysteresis;
std::string temp_path;
+ std::string severity_reference;
float vr_threshold;
float multiplier;
std::chrono::milliseconds polling_delay;
@@ -225,6 +228,7 @@
bool send_powerhint;
bool is_watch;
bool is_hidden;
+ ThrottlingSeverity log_level;
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
std::shared_ptr<ThrottlingInfo> throttling_info;
std::unique_ptr<PredictorInfo> predictor_info;
@@ -244,7 +248,10 @@
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
};
-bool ParseThermalConfig(std::string_view config_path, Json::Value *config);
+bool LoadThermalConfig(std::string_view config_path, Json::Value *config);
+bool ParseThermalConfig(std::string_view config_path, Json::Value *config,
+ std::unordered_set<std::string> *loaded_config_paths);
+void MergeConfigEntries(Json::Value *config, Json::Value *sub_config, std::string_view member_name);
bool ParseSensorInfo(const Json::Value &config,
std::unordered_map<std::string, SensorInfo> *sensors_parsed);
bool ParseCoolingDevice(const Json::Value &config,
diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp
index f5f079b..1e011ec 100644
--- a/thermal/utils/thermal_throttling.cpp
+++ b/thermal/utils/thermal_throttling.cpp
@@ -237,7 +237,16 @@
}
if (err < sensor_info.throttling_info->i_cutoff[target_state]) {
- throttling_status.i_budget += err * sensor_info.throttling_info->k_i[target_state];
+ if (!(throttling_status.prev_power_budget <=
+ sensor_info.throttling_info->min_alloc_power[target_state] &&
+ err < 0) &&
+ !(throttling_status.prev_power_budget >=
+ sensor_info.throttling_info->max_alloc_power[target_state] &&
+ err > 0)) {
+ throttling_status.i_budget +=
+ err * (err < 0 ? sensor_info.throttling_info->k_io[target_state]
+ : sensor_info.throttling_info->k_iu[target_state]);
+ }
}
if (fabsf(throttling_status.i_budget) > sensor_info.throttling_info->i_max[target_state]) {
@@ -269,12 +278,10 @@
// Calculate power budget
power_budget = sensor_info.throttling_info->s_power[target_state] + p +
throttling_status.i_budget + d + compensation;
- if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) {
- power_budget = sensor_info.throttling_info->min_alloc_power[target_state];
- }
- if (power_budget > sensor_info.throttling_info->max_alloc_power[target_state]) {
- power_budget = sensor_info.throttling_info->max_alloc_power[target_state];
- }
+
+ power_budget =
+ std::clamp(power_budget, sensor_info.throttling_info->min_alloc_power[target_state],
+ sensor_info.throttling_info->max_alloc_power[target_state]);
if (target_changed) {
throttling_status.budget_transient = throttling_status.prev_power_budget - power_budget;
diff --git a/thermal/utils/thermal_watcher.cpp b/thermal/utils/thermal_watcher.cpp
index f8ca2c2..74bd416 100644
--- a/thermal/utils/thermal_watcher.cpp
+++ b/thermal/utils/thermal_watcher.cpp
@@ -44,6 +44,66 @@
namespace {
+using ::android::base::StringPrintf;
+
+constexpr static const char *const kNlAttributeStringMap[THERMAL_GENL_ATTR_MAX + 1] = {
+ [THERMAL_GENL_ATTR_TZ_ID] = "tz_id",
+ [THERMAL_GENL_ATTR_TZ_TEMP] = "tz_temp",
+ [THERMAL_GENL_ATTR_TZ_TRIP_ID] = "trip_id",
+ [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = "trip_type",
+ [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = "trip_temp",
+ [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = "trip_hyst",
+ [THERMAL_GENL_ATTR_TZ_NAME] = "tz_name",
+ [THERMAL_GENL_ATTR_CDEV_ID] = "cdev_id",
+ [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = "cdev_cur_state",
+ [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = "cdev_max_state",
+ [THERMAL_GENL_ATTR_CDEV_NAME] = "cdev_name",
+ [THERMAL_GENL_ATTR_GOV_NAME] = "gov_name",
+};
+
+static void setAndLogTzId(const struct nlattr *const attrs[THERMAL_GENL_ATTR_MAX + 1], int &tz_id,
+ std::string &out) {
+ if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
+ tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
+ out.append(StringPrintf(" %s=%d", kNlAttributeStringMap[THERMAL_GENL_ATTR_TZ_ID], tz_id));
+ }
+}
+
+static void setAndLogTzTemp(const struct nlattr *const attrs[THERMAL_GENL_ATTR_MAX + 1],
+ float &tz_temp, std::string &out) {
+ if (attrs[THERMAL_GENL_ATTR_TZ_TEMP]) {
+ tz_temp = static_cast<float>(nla_get_s32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]));
+ out.append(StringPrintf(" %s=%0.2f", kNlAttributeStringMap[THERMAL_GENL_ATTR_TZ_TEMP],
+ tz_temp));
+ }
+}
+
+static void log32Attribute(const struct nlattr *const attrs[THERMAL_GENL_ATTR_MAX + 1],
+ const thermal_genl_attr &attr_type, std::string &out) {
+ if (attrs[attr_type]) {
+ if (attr_type == THERMAL_GENL_ATTR_TZ_TEMP || attr_type == THERMAL_GENL_ATTR_TZ_TRIP_TEMP) {
+ out.append(StringPrintf(" %s=%d", kNlAttributeStringMap[attr_type],
+ nla_get_s32(attrs[attr_type])));
+ } else {
+ // id, hyst and state kind of attr_type will goes into this else
+ out.append(StringPrintf(" %s=%d", kNlAttributeStringMap[attr_type],
+ nla_get_u32(attrs[attr_type])));
+ }
+ }
+}
+
+static void log32AttributeList(const struct nlattr *const attrs[THERMAL_GENL_ATTR_MAX + 1],
+ const std::vector<thermal_genl_attr> &attr_types, std::string &out) {
+ for (const auto &attr_type : attr_types) log32Attribute(attrs, attr_type, out);
+}
+
+static void logStringAttribute(const struct nlattr *const attrs[THERMAL_GENL_ATTR_MAX + 1],
+ const thermal_genl_attr &attr_type, std::string &out) {
+ if (attrs[attr_type])
+ out.append(StringPrintf(" %s=%s", kNlAttributeStringMap[attr_type],
+ nla_get_string(attrs[attr_type])));
+}
+
static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
int *ret = reinterpret_cast<int *>(arg);
*ret = err->error;
@@ -190,151 +250,94 @@
struct nlmsghdr *nlh = nlmsg_hdr(n);
struct genlmsghdr *glh = genlmsg_hdr(nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
- int *tz_id = reinterpret_cast<int *>(arg);
+ std::pair<int, float> *tz_info = reinterpret_cast<std::pair<int, float> *>(arg);
+ int &tz_id = tz_info->first;
+ float &tz_temp = tz_info->second;
+ std::string out;
genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
- LOG(INFO) << "Thermal zone trip id: "
- << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
+ switch (glh->cmd) {
+ case THERMAL_GENL_EVENT_TZ_TRIP_UP:
+ out = "THERMAL_GENL_EVENT_TZ_TRIP_UP";
+ setAndLogTzId(attrs, tz_id, out);
+ setAndLogTzTemp(attrs, tz_temp, out);
+ log32Attribute(attrs, THERMAL_GENL_ATTR_TZ_TRIP_ID, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_TRIP_DOWN:
+ out = "THERMAL_GENL_EVENT_TZ_TRIP_DOWN";
+ setAndLogTzId(attrs, tz_id, out);
+ setAndLogTzTemp(attrs, tz_temp, out);
+ log32Attribute(attrs, THERMAL_GENL_ATTR_TZ_TRIP_ID, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_GOV_CHANGE:
+ out = "THERMAL_GENL_EVENT_TZ_GOV_CHANGE";
+ setAndLogTzId(attrs, tz_id, out);
+ logStringAttribute(attrs, THERMAL_GENL_ATTR_GOV_NAME, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_CREATE:
+ out = "THERMAL_GENL_EVENT_TZ_CREATE";
+ setAndLogTzId(attrs, tz_id, out);
+ logStringAttribute(attrs, THERMAL_GENL_ATTR_TZ_NAME, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_DELETE:
+ out = "THERMAL_GENL_EVENT_TZ_DELETE";
+ setAndLogTzId(attrs, tz_id, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_DISABLE:
+ out = "THERMAL_GENL_EVENT_TZ_DISABLE";
+ setAndLogTzId(attrs, tz_id, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_ENABLE:
+ out = "THERMAL_GENL_EVENT_TZ_ENABLE";
+ setAndLogTzId(attrs, tz_id, out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE:
+ out = "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE";
+ setAndLogTzId(attrs, tz_id, out);
+ log32AttributeList(attrs,
+ {THERMAL_GENL_ATTR_TZ_TRIP_ID, THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
+ THERMAL_GENL_ATTR_TZ_TRIP_TEMP, THERMAL_GENL_ATTR_TZ_TRIP_HYST},
+ out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_TRIP_ADD:
+ out = "THERMAL_GENL_EVENT_TZ_TRIP_ADD";
+ setAndLogTzId(attrs, tz_id, out);
+ log32AttributeList(attrs,
+ {THERMAL_GENL_ATTR_TZ_TRIP_ID, THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
+ THERMAL_GENL_ATTR_TZ_TRIP_TEMP, THERMAL_GENL_ATTR_TZ_TRIP_HYST},
+ out);
+ break;
+ case THERMAL_GENL_EVENT_TZ_TRIP_DELETE:
+ out = "THERMAL_GENL_EVENT_TZ_TRIP_DELETE";
+ setAndLogTzId(attrs, tz_id, out);
+ log32Attribute(attrs, THERMAL_GENL_ATTR_TZ_TRIP_ID, out);
+ break;
+ case THERMAL_GENL_EVENT_CDEV_STATE_UPDATE:
+ out = "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE:";
+ log32AttributeList(attrs, {THERMAL_GENL_ATTR_CDEV_ID, THERMAL_GENL_ATTR_CDEV_CUR_STATE},
+ out);
+ break;
+ case THERMAL_GENL_EVENT_CDEV_ADD:
+ out = "THERMAL_GENL_EVENT_CDEV_ADD";
+ log32Attribute(attrs, THERMAL_GENL_ATTR_CDEV_ID, out);
+ logStringAttribute(attrs, THERMAL_GENL_ATTR_CDEV_NAME, out);
+ log32Attribute(attrs, THERMAL_GENL_ATTR_CDEV_MAX_STATE, out);
+ break;
+ case THERMAL_GENL_EVENT_CDEV_DELETE:
+ out = "THERMAL_GENL_EVENT_CDEV_DELETE";
+ log32Attribute(attrs, THERMAL_GENL_ATTR_CDEV_ID, out);
+ break;
+ case THERMAL_GENL_SAMPLING_TEMP:
+ out = "THERMAL_GENL_SAMPLING_TEMP";
+ setAndLogTzId(attrs, tz_id, out);
+ log32Attribute(attrs, THERMAL_GENL_ATTR_TZ_TEMP, out);
+ break;
+ default:
+ LOG(ERROR) << "Unknown genlink event command: " << glh->cmd;
+ return 0;
}
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
- LOG(INFO) << "Thermal zone trip id: "
- << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_GOV_NAME])
- LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_NAME])
- LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
- LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
- LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
- LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
- LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID])
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
- LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE])
- LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP])
- LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]);
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST])
- LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])
- LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE";
- if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
- LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
- if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE])
- LOG(INFO) << "Cooling device current state: "
- << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) {
- LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD";
- if (attrs[THERMAL_GENL_ATTR_CDEV_NAME])
- LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]);
- if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
- LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
- if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE])
- LOG(INFO) << "Cooling device max state: "
- << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]);
- }
-
- if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) {
- LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE";
- if (attrs[THERMAL_GENL_ATTR_CDEV_ID])
- LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]);
- }
-
- if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) {
- LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP";
- if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
- LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
- }
- if (attrs[THERMAL_GENL_ATTR_TZ_TEMP])
- LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
- }
+ LOG(INFO) << out;
return 0;
}
@@ -412,7 +415,7 @@
}
return false;
}
-void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
+void ThermalWatcher::parseUevent(std::unordered_map<std::string, float> *sensor_map) {
bool thermal_event = false;
constexpr int kUeventMsgLen = 2048;
char msg[kUeventMsgLen + 2];
@@ -453,7 +456,7 @@
start_pos += 5;
std::string name = uevent.substr(start_pos);
if (monitored_sensors_.find(name) != monitored_sensors_.end()) {
- sensors_set->insert(name);
+ sensor_map->insert({name, NAN});
}
break;
}
@@ -466,8 +469,9 @@
// TODO(b/175367921): Consider for potentially adding more type of event in the function
// instead of just add the sensors to the list.
-void ThermalWatcher::parseGenlink(std::set<std::string> *sensors_set) {
- int err = 0, done = 0, tz_id = -1;
+void ThermalWatcher::parseGenlink(std::unordered_map<std::string, float> *sensor_map) {
+ int err = 0, done = 0;
+ std::pair<int, float> tz_info(-1, NAN);
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put);
@@ -475,19 +479,19 @@
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done);
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done);
nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done);
- nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id);
+ nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_info);
while (!done && !err) {
nl_recvmsgs(sk_thermal, cb.get());
- if (tz_id < 0) {
+ if (tz_info.first < 0) {
break;
}
std::string name;
- if (getThermalZoneTypeById(tz_id, &name) &&
+ if (getThermalZoneTypeById(tz_info.first, &name) &&
monitored_sensors_.find(name) != monitored_sensors_.end()) {
- sensors_set->insert(name);
+ sensor_map->insert({name, tz_info.second});
}
}
}
@@ -500,7 +504,7 @@
LOG(VERBOSE) << "ThermalWatcher polling...";
int fd;
- std::set<std::string> sensors;
+ std::unordered_map<std::string, float> sensors;
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
last_update_time_);
diff --git a/thermal/utils/thermal_watcher.h b/thermal/utils/thermal_watcher.h
index 8f8b398..6d1dbed 100644
--- a/thermal/utils/thermal_watcher.h
+++ b/thermal/utils/thermal_watcher.h
@@ -43,7 +43,8 @@
using ::android::base::boot_clock;
using ::android::base::unique_fd;
-using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>;
+using WatcherCallback = std::function<std::chrono::milliseconds(
+ const std::unordered_map<std::string, float> &uevent_sensor_map)>;
// A helper class for monitoring thermal files changes.
class ThermalWatcher : public ::android::Thread {
@@ -77,10 +78,10 @@
bool threadLoop() override;
// Parse uevent message
- void parseUevent(std::set<std::string> *sensor_name);
+ void parseUevent(std::unordered_map<std::string, float> *sensor_map);
// Parse thermal netlink message
- void parseGenlink(std::set<std::string> *sensor_name);
+ void parseGenlink(std::unordered_map<std::string, float> *sensor_map);
// Maps watcher filer descriptor to watched file path.
std::unordered_map<int, std::string> watch_to_file_path_map_;
diff --git a/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
index 2b70a3a..bb66e87 100644
--- a/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
+++ b/thermal/virtualtemp_estimator/virtualtemp_estimator.cpp
@@ -90,7 +90,7 @@
std::unique_lock<std::mutex> lock(tflite_instance_->tflite_methods.mutex);
if (!common_instance_->is_initialized) {
- LOG(ERROR) << "tflite_instance_ not initialized for " << tflite_instance_->model_path;
+ LOG(ERROR) << "tflite_instance_ not initialized for " << common_instance_->sensor_name;
return kVtEstimatorInitFailed;
}
@@ -262,7 +262,7 @@
return kVtEstimatorInitFailed;
}
- std::string model_path = data.model_path;
+ std::string_view sensor_name = common_instance_->sensor_name;
size_t num_linked_sensors = common_instance_->num_linked_sensors;
bool use_prev_samples = data.use_prev_samples;
size_t prev_samples_order = data.prev_samples_order;
@@ -271,17 +271,16 @@
std::unique_lock<std::mutex> lock(tflite_instance_->tflite_methods.mutex);
- if (model_path.empty()) {
- LOG(ERROR) << "Invalid model_path:" << model_path;
+ if (data.model_path.empty()) {
+ LOG(ERROR) << "Invalid model_path:" << data.model_path << " for " << sensor_name;
return kVtEstimatorInvalidArgs;
}
if (num_linked_sensors == 0 || prev_samples_order < 1 ||
(!use_prev_samples && prev_samples_order > 1)) {
- LOG(ERROR) << "Invalid tflite_instance_ config: "
- << "number of linked sensor: " << num_linked_sensors
- << " use previous: " << use_prev_samples
- << " previous sample order: " << prev_samples_order;
+ LOG(ERROR) << "Invalid tflite_instance_ config: " << "number of linked sensor: "
+ << num_linked_sensors << " use previous: " << use_prev_samples
+ << " previous sample order: " << prev_samples_order << " for " << sensor_name;
return kVtEstimatorInitFailed;
}
@@ -296,9 +295,8 @@
}
if (output_label_count < 1 || num_hot_spots < 1) {
- LOG(ERROR) << "Invalid tflite_instance_ config:"
- << "number of hot spots: " << num_hot_spots
- << " predicted sample order: " << output_label_count;
+ LOG(ERROR) << "Invalid tflite_instance_ config:" << "number of hot spots: " << num_hot_spots
+ << " predicted sample order: " << output_label_count << " for " << sensor_name;
return kVtEstimatorInitFailed;
}
@@ -311,48 +309,48 @@
!tflite_instance_->tflite_methods.invoke || !tflite_instance_->tflite_methods.destroy ||
!tflite_instance_->tflite_methods.get_input_config_size ||
!tflite_instance_->tflite_methods.get_input_config) {
- LOG(ERROR) << "Invalid tflite methods";
+ LOG(ERROR) << "Invalid tflite methods for " << sensor_name;
return kVtEstimatorInitFailed;
}
tflite_instance_->tflite_wrapper =
tflite_instance_->tflite_methods.create(kNumInputTensors, kNumOutputTensors);
if (!tflite_instance_->tflite_wrapper) {
- LOG(ERROR) << "Failed to create tflite wrapper";
+ LOG(ERROR) << "Failed to create tflite wrapper for " << sensor_name;
return kVtEstimatorInitFailed;
}
int ret = tflite_instance_->tflite_methods.init(tflite_instance_->tflite_wrapper,
- model_path.c_str());
+ data.model_path.c_str());
if (ret) {
- LOG(ERROR) << "Failed to Init tflite_wrapper for " << model_path << " (ret: )" << ret
+ LOG(ERROR) << "Failed to Init tflite_wrapper for " << sensor_name << " (ret: " << ret
<< ")";
return kVtEstimatorInitFailed;
}
Json::Value input_config;
if (!GetInputConfig(&input_config)) {
- LOG(ERROR) << "Get Input Config failed for " << model_path;
+ LOG(ERROR) << "Get Input Config failed for " << sensor_name;
return kVtEstimatorInitFailed;
}
if (!ParseInputConfig(input_config)) {
- LOG(ERROR) << "Parse Input Config failed for " << model_path;
+ LOG(ERROR) << "Parse Input Config failed for " << sensor_name;
return kVtEstimatorInitFailed;
}
if (tflite_instance_->enable_input_validation && !tflite_instance_->input_range.size()) {
LOG(ERROR) << "Input ranges missing when input data validation is enabled for "
- << common_instance_->sensor_name;
+ << sensor_name;
return kVtEstimatorInitFailed;
}
common_instance_->offset_thresholds = data.offset_thresholds;
common_instance_->offset_values = data.offset_values;
- tflite_instance_->model_path = model_path;
+ tflite_instance_->model_path = data.model_path;
common_instance_->is_initialized = true;
- LOG(INFO) << "Successfully initialized VirtualTempEstimator for " << model_path;
+ LOG(INFO) << "Successfully initialized VirtualTempEstimator for " << sensor_name;
return kVtEstimatorOk;
}
@@ -363,6 +361,7 @@
return kVtEstimatorInitFailed;
}
+ std::string_view sensor_name = common_instance_->sensor_name;
size_t prev_samples_order = common_instance_->prev_samples_order;
size_t num_linked_sensors = common_instance_->num_linked_sensors;
@@ -370,12 +369,13 @@
if ((thermistors.size() != num_linked_sensors) || (output == nullptr)) {
LOG(ERROR) << "Invalid args Thermistors size[" << thermistors.size()
- << "] num_linked_sensors[" << num_linked_sensors << "] output[" << output << "]";
+ << "] num_linked_sensors[" << num_linked_sensors << "] output[" << output << "]"
+ << " for " << sensor_name;
return kVtEstimatorInvalidArgs;
}
if (common_instance_->is_initialized == false) {
- LOG(ERROR) << "VirtualTempEstimator not initialized to estimate";
+ LOG(ERROR) << "tflite_instance_ not initialized for " << sensor_name;
return kVtEstimatorInitFailed;
}
@@ -429,9 +429,10 @@
return kVtEstimatorInitFailed;
}
+ std::string_view sensor_name = common_instance_->sensor_name;
size_t num_linked_sensors = common_instance_->num_linked_sensors;
if ((thermistors.size() != num_linked_sensors) || (output == nullptr)) {
- LOG(ERROR) << "Invalid args for " << tflite_instance_->model_path
+ LOG(ERROR) << "Invalid args for " << sensor_name
<< " thermistors.size(): " << thermistors.size()
<< " num_linked_sensors: " << num_linked_sensors << " output: " << output;
return kVtEstimatorInvalidArgs;
@@ -443,13 +444,13 @@
input_data_str += ::android::base::StringPrintf("%0.2f ", thermistors[i]);
}
input_data_str += "]";
- LOG(INFO) << input_data_str;
+ LOG(INFO) << sensor_name << ": " << input_data_str;
// check time gap between samples and ignore stale previous samples
if (std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
tflite_instance_->prev_sample_time) >=
tflite_instance_->max_sample_interval) {
- LOG(INFO) << "Ignoring stale previous samples for " << common_instance_->sensor_name;
+ LOG(INFO) << "Ignoring stale previous samples for " << sensor_name;
common_instance_->cur_sample_count = 0;
}
@@ -463,7 +464,8 @@
thermistors[i] > tflite_instance_->input_range[i].max_threshold) {
LOG(INFO) << "thermistors[" << i << "] value: " << thermistors[i]
<< " not in range: " << tflite_instance_->input_range[i].min_threshold
- << " <= val <= " << tflite_instance_->input_range[i].max_threshold;
+ << " <= val <= " << tflite_instance_->input_range[i].max_threshold
+ << " for " << sensor_name;
common_instance_->cur_sample_count = 0;
return kVtEstimatorLowConfidence;
}
@@ -505,8 +507,7 @@
tflite_instance_->tflite_wrapper, model_input, input_buffer_size,
tflite_instance_->output_buffer, output_buffer_size);
if (ret) {
- LOG(ERROR) << "Failed to Invoke for " << tflite_instance_->model_path << " (ret: " << ret
- << ")";
+ LOG(ERROR) << "Failed to Invoke for " << sensor_name << " (ret: " << ret << ")";
return kVtEstimatorInvokeFailed;
}
tflite_instance_->last_update_time = boot_clock::now();
@@ -524,8 +525,8 @@
predict_log << predicted_value << " ";
data.emplace_back(predicted_value);
}
- LOG(INFO) << "model_output: [" << model_out_log.str() << "]";
- LOG(INFO) << "predicted_value: [" << predict_log.str() << "]";
+ LOG(INFO) << sensor_name << ": model_output: [" << model_out_log.str() << "]";
+ LOG(INFO) << sensor_name << ": predicted_value: [" << predict_log.str() << "]";
*output = data;
return kVtEstimatorOk;
@@ -550,7 +551,7 @@
}
if (!common_instance_->is_initialized) {
- LOG(ERROR) << "tflite_instance_ not initialized for " << tflite_instance_->model_path;
+ LOG(ERROR) << "tflite_instance_ not initialized for " << common_instance_->sensor_name;
return kVtEstimatorInitFailed;
}
@@ -579,7 +580,7 @@
}
if (!common_instance_->is_initialized) {
- LOG(ERROR) << "tflite_instance_ not initialized for " << tflite_instance_->model_path;
+ LOG(ERROR) << "tflite_instance_ not initialized for " << common_instance_->sensor_name;
return kVtEstimatorInitFailed;
}
diff --git a/vibrator/common/Android.bp b/vibrator/common/Android.bp
index cb21005..3dd55bf 100644
--- a/vibrator/common/Android.bp
+++ b/vibrator/common/Android.bp
@@ -33,6 +33,7 @@
values: [
"luxshare_ict_081545",
"luxshare_ict_lt_xlra1906d",
+ "legacy_zlra_actuator",
],
}
@@ -57,6 +58,11 @@
"-DLUXSHARE_ICT_LT_XLRA1906D",
],
},
+ legacy_zlra_actuator: {
+ cflags: [
+ "-DLEGACY_ZLRA_ACTUATOR",
+ ],
+ },
conditions_default: {
cflags: [
"-DUNSPECIFIED_ACTUATOR",
@@ -166,14 +172,57 @@
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
"-DLOG_TAG=\"VibratorStats\"",
],
+ static_libs: [
+ "libvibrator_atoms",
+ ],
shared_libs: [
"android.frameworks.stats-V2-ndk",
"libbase",
"libcutils",
"libbinder_ndk",
"liblog",
- "libprotobuf-cpp-lite",
"libutils",
- "pixelatoms-cpp",
+ ],
+}
+
+genrule {
+ name: "vibrator_atoms.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(out)" +
+ " --module vibrator" +
+ " --namespace android,hardware,google,pixel,VibratorAtoms" +
+ " --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
+ out: [
+ "vibrator_atoms.h",
+ ],
+ srcs: [
+ ":pixelatoms_proto",
+ ],
+}
+
+genrule {
+ name: "vibrator_atoms.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(out)" +
+ " --module vibrator" +
+ " --importHeader vibrator_atoms.h" +
+ " --namespace android,hardware,google,pixel,VibratorAtoms" +
+ " --vendor-proto hardware/google/pixel/pixelstats/pixelatoms.proto",
+ out: [
+ "vibrator_atoms.cpp",
+ ],
+ srcs: [
+ ":pixelatoms_proto",
+ ],
+}
+
+cc_library_static {
+ name: "libvibrator_atoms",
+ vendor: true,
+ generated_sources: ["vibrator_atoms.cpp"],
+ generated_headers: ["vibrator_atoms.h"],
+ export_generated_headers: ["vibrator_atoms.h"],
+ shared_libs: [
+ "android.frameworks.stats-V2-ndk",
],
}
diff --git a/vibrator/common/HardwareBase.cpp b/vibrator/common/HardwareBase.cpp
index f162429..fb15523 100644
--- a/vibrator/common/HardwareBase.cpp
+++ b/vibrator/common/HardwareBase.cpp
@@ -40,10 +40,6 @@
mNames[stream] = name;
}
-bool HwApiBase::has(const std::ios &stream) {
- return !!stream;
-}
-
void HwApiBase::debug(int fd) {
dprintf(fd, "Kernel:\n");
diff --git a/vibrator/common/HardwareBase.h b/vibrator/common/HardwareBase.h
index 36c3fca..d038c19 100644
--- a/vibrator/common/HardwareBase.h
+++ b/vibrator/common/HardwareBase.h
@@ -26,6 +26,7 @@
#include <map>
#include <sstream>
#include <string>
+#include <type_traits>
#include "utils.h"
@@ -78,7 +79,8 @@
void saveName(const std::string &name, const std::ios *stream);
template <typename T>
void open(const std::string &name, T *stream);
- bool has(const std::ios &stream);
+ template <typename T>
+ bool has(const T &stream);
template <typename T>
bool get(T *value, std::istream *stream);
template <typename T>
@@ -105,6 +107,16 @@
}
template <typename T>
+bool HwApiBase::has(const T &stream) {
+ if constexpr (std::is_same<T, std::fstream>::value || std::is_same<T, std::ofstream>::value ||
+ std::is_same<T, std::ifstream>::value)
+ return stream.is_open() && !stream.fail();
+
+ ALOGE("File stream is not of the correct type");
+ return false;
+}
+
+template <typename T>
bool HwApiBase::get(T *value, std::istream *stream) {
ATRACE_NAME("HwApi::get");
std::scoped_lock ioLock{mIoMutex};
diff --git a/vibrator/common/StatsBase.cpp b/vibrator/common/StatsBase.cpp
index a0402b4..9160d81 100644
--- a/vibrator/common/StatsBase.cpp
+++ b/vibrator/common/StatsBase.cpp
@@ -18,18 +18,19 @@
#include <aidl/android/frameworks/stats/IStats.h>
#include <android/binder_manager.h>
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
#include <log/log.h>
#include <utils/Trace.h>
+#include <vibrator_atoms.h>
#include <chrono>
#include <sstream>
using ::aidl::android::frameworks::stats::IStats;
using ::aidl::android::frameworks::stats::VendorAtom;
-using ::aidl::android::frameworks::stats::VendorAtomValue;
-namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
+namespace VibratorAtoms = ::android::hardware::google::pixel::VibratorAtoms;
+
+using VibratorAtoms::createVendorAtom;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
@@ -41,13 +42,13 @@
const char *atomToString(uint32_t atomId) {
switch (atomId) {
- case PixelAtoms::Atom::kVibratorPlaycountReported:
+ case VibratorAtoms::VIBRATOR_PLAYCOUNT_REPORTED:
return kAtomLookup[0];
break;
- case PixelAtoms::Atom::kVibratorLatencyReported:
+ case VibratorAtoms::VIBRATOR_LATENCY_REPORTED:
return kAtomLookup[1];
break;
- case PixelAtoms::Atom::kVibratorErrorsReported:
+ case VibratorAtoms::VIBRATOR_ERRORS_REPORTED:
return kAtomLookup[2];
break;
default:
@@ -223,62 +224,32 @@
VendorAtom StatsBase::vibratorPlaycountAtom() {
STATS_TRACE("vibratorPlaycountAtom()");
- std::vector<VendorAtomValue> values(2);
-
- {
- std::scoped_lock<std::mutex> lock(mDataAccess);
- values[0].set<VendorAtomValue::repeatedIntValue>(mWaveformCounts);
- values[1].set<VendorAtomValue::repeatedIntValue>(mDurationCounts);
- }
-
- return VendorAtom{
- .reverseDomainName = "",
- .atomId = PixelAtoms::Atom::kVibratorPlaycountReported,
- .values = std::move(values),
- };
+ std::scoped_lock<std::mutex> lock(mDataAccess);
+ return createVendorAtom(VibratorAtoms::VIBRATOR_PLAYCOUNT_REPORTED, "", mWaveformCounts,
+ mDurationCounts);
}
VendorAtom StatsBase::vibratorLatencyAtom() {
STATS_TRACE("vibratorLatencyAtom()");
- std::vector<VendorAtomValue> values(3);
std::vector<int32_t> avgLatencies;
- {
- std::scoped_lock<std::mutex> lock(mDataAccess);
- for (uint32_t i = 0; i < mLatencyCounts.size(); i++) {
- int32_t avg = 0;
- if (mLatencyCounts[0] > 0) {
- avg = mLatencyTotals[i] / mLatencyCounts[i];
- }
- avgLatencies.push_back(avg);
+ std::scoped_lock<std::mutex> lock(mDataAccess);
+ for (uint32_t i = 0; i < mLatencyCounts.size(); i++) {
+ int32_t avg = 0;
+ if (mLatencyCounts[0] > 0) {
+ avg = mLatencyTotals[i] / mLatencyCounts[i];
}
-
- values[0].set<VendorAtomValue::repeatedIntValue>(mMinLatencies);
- values[1].set<VendorAtomValue::repeatedIntValue>(mMaxLatencies);
+ avgLatencies.push_back(avg);
}
- values[2].set<VendorAtomValue::repeatedIntValue>(avgLatencies);
- return VendorAtom{
- .reverseDomainName = "",
- .atomId = PixelAtoms::Atom::kVibratorLatencyReported,
- .values = std::move(values),
- };
+ return createVendorAtom(VibratorAtoms::VIBRATOR_LATENCY_REPORTED, "", mMinLatencies,
+ mMaxLatencies, avgLatencies);
}
VendorAtom StatsBase::vibratorErrorAtom() {
STATS_TRACE("vibratorErrorAtom()");
- std::vector<VendorAtomValue> values(1);
-
- {
- std::scoped_lock<std::mutex> lock(mDataAccess);
- values[0].set<VendorAtomValue::repeatedIntValue>(mErrorCounts);
- }
-
- return VendorAtom{
- .reverseDomainName = "",
- .atomId = PixelAtoms::Atom::kVibratorErrorsReported,
- .values = std::move(values),
- };
+ std::scoped_lock<std::mutex> lock(mDataAccess);
+ return createVendorAtom(VibratorAtoms::VIBRATOR_ERRORS_REPORTED, "", mErrorCounts);
}
} // namespace vibrator
diff --git a/vibrator/common/utils.h b/vibrator/common/utils.h
index da0bca9..b5005a6 100644
--- a/vibrator/common/utils.h
+++ b/vibrator/common/utils.h
@@ -123,10 +123,12 @@
template <typename T>
static void openNoCreate(const std::string &file, T *outStream) {
- auto mode = std::is_base_of_v<std::ostream, T> ? std::ios_base::out : std::ios_base::in;
+ if (!std::filesystem::exists(file)) {
+ ALOGE("File does not exist: %s", file.c_str());
+ return;
+ }
- // Force 'in' mode to prevent file creation
- outStream->open(file, mode | std::ios_base::in);
+ outStream->open(file);
if (!*outStream) {
ALOGE("Failed to open %s (%d): %s", file.c_str(), errno, strerror(errno));
}
diff --git a/vibrator/cs40l25/Vibrator.cpp b/vibrator/cs40l25/Vibrator.cpp
index 1363dbe..e786048 100644
--- a/vibrator/cs40l25/Vibrator.cpp
+++ b/vibrator/cs40l25/Vibrator.cpp
@@ -31,6 +31,7 @@
#include <sstream>
#include "Stats.h"
+#include "utils.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
@@ -277,6 +278,21 @@
mHwApi->getEffectCount(&effectCount);
mEffectDurations.resize(effectCount);
+
+ mIsPrimitiveDelayEnabled =
+ utils::getProperty("ro.vendor.vibrator.hal.cs40L25.primitive_delays.enabled", false);
+
+ mDelayEffectDurations.resize(effectCount);
+ if (mIsPrimitiveDelayEnabled) {
+ mDelayEffectDurations = {
+ 25, 45, 45, 20, 20, 20, 20, 20,
+ }; /* delays for each effect based on measurements */
+ } else {
+ mDelayEffectDurations = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ }; /* no delay if property not set */
+ }
+
for (size_t effectIndex = 0; effectIndex < effectCount; effectIndex++) {
mHwApi->setEffectIndex(effectIndex);
uint32_t effectDuration;
@@ -527,6 +543,8 @@
effectBuilder << effectIndex << "." << intensityToVolLevel(e.scale, effectIndex) << ",";
mTotalDuration += mEffectDurations[effectIndex];
+
+ mTotalDuration += mDelayEffectDurations[effectIndex];
}
}
diff --git a/vibrator/cs40l25/Vibrator.h b/vibrator/cs40l25/Vibrator.h
index 7290805..e462cce 100644
--- a/vibrator/cs40l25/Vibrator.h
+++ b/vibrator/cs40l25/Vibrator.h
@@ -239,12 +239,14 @@
std::array<uint32_t, 2> mClickEffectVol;
std::array<uint32_t, 2> mLongEffectVol;
std::vector<uint32_t> mEffectDurations;
+ std::vector<uint32_t> mDelayEffectDurations;
std::future<void> mAsyncHandle;
int32_t mCompositionSizeMax;
struct pcm *mHapticPcm;
int mCard;
int mDevice;
bool mHasHapticAlsaDevice;
+ bool mIsPrimitiveDelayEnabled;
bool mIsUnderExternalControl;
float mResonantFrequency;
uint32_t mRedc{0};
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
index 13834ef..101cae8 100644
--- a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
@@ -1,4 +1,4 @@
-on boot
+on property:vendor.all.modules.ready=1
wait /sys/class/leds/vibrator/device
mkdir /mnt/vendor/persist/haptics 0770 system system
diff --git a/vibrator/cs40l25/fuzzer/Android.bp b/vibrator/cs40l25/fuzzer/Android.bp
new file mode 100644
index 0000000..5d990c6
--- /dev/null
+++ b/vibrator/cs40l25/fuzzer/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_pixel_system_sw_touch_haptic",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "VibratorHalCs40l25Fuzzer",
+ relative_install_path: "",
+ defaults: [
+ "VibratorHalCs40l25BinaryDefaults",
+ "service_fuzzer_defaults",
+ ],
+ srcs: [
+ "fuzzer-vibrator.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l25",
+ ],
+ fuzz_config: {
+ triage_assignee: "[email protected]",
+ componentid: 716924,
+ },
+}
diff --git a/vibrator/cs40l25/fuzzer/fuzzer-vibrator.cpp b/vibrator/cs40l25/fuzzer/fuzzer-vibrator.cpp
new file mode 100644
index 0000000..7fad137
--- /dev/null
+++ b/vibrator/cs40l25/fuzzer/fuzzer-vibrator.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "Hardware.h"
+#include "Vibrator.h"
+
+using ::aidl::android::hardware::vibrator::HwApi;
+using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::Vibrator;
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+// No stats collection.
+class FakeStatsApi : public Vibrator::StatsApi {
+ public:
+ FakeStatsApi() = default;
+ ~FakeStatsApi() = default;
+
+ bool logPrimitive(uint16_t) override { return true; }
+
+ bool logWaveform(uint16_t, int32_t) override { return true; }
+
+ bool logError(uint16_t) override { return true; }
+
+ bool logLatencyStart(uint16_t) override { return true; }
+
+ bool logLatencyEnd() { return true; }
+
+ void debug(int32_t) override {}
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ auto vibService = ndk::SharedRefBase::make<Vibrator>(
+ std::make_unique<HwApi>(), std::make_unique<HwCal>(), std::make_unique<FakeStatsApi>());
+
+ fuzzService(vibService->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
index 8ee3f34..58225c2 100644
--- a/vibrator/cs40l26/Hardware.h
+++ b/vibrator/cs40l26/Hardware.h
@@ -76,6 +76,9 @@
open("calibration/q_stored", &mQ);
open("default/vibe_state", &mVibeState);
open("default/num_waves", &mEffectCount);
+ open("default/braking_time_bank", &mEffectBrakingTimeBank);
+ open("default/braking_time_index", &mEffectBrakingTimeIndex);
+ open("default/braking_time_ms", &mEffectBrakingTimeMs);
open("default/owt_free_space", &mOwtFreeSpace);
open("default/f0_comp_enable", &mF0CompEnable);
open("default/redc_comp_enable", &mRedcCompEnable);
@@ -87,6 +90,16 @@
bool setRedc(std::string value) override { return set(value, &mRedc); }
bool setQ(std::string value) override { return set(value, &mQ); }
bool getEffectCount(uint32_t *value) override { return get(value, &mEffectCount); }
+ bool hasEffectBrakingTimeBank() override { return has(mEffectBrakingTimeBank); }
+ bool setEffectBrakingTimeBank(uint32_t value) override {
+ return set(value, &mEffectBrakingTimeBank);
+ }
+ bool setEffectBrakingTimeIndex(uint32_t value) override {
+ return set(value, &mEffectBrakingTimeIndex);
+ }
+ bool getEffectBrakingTimeMs(uint32_t *value) override {
+ return get(value, &mEffectBrakingTimeMs);
+ }
bool pollVibeState(uint32_t value, int32_t timeoutMs) override {
return poll(value, &mVibeState, timeoutMs);
}
@@ -437,6 +450,9 @@
std::ofstream mRedc;
std::ofstream mQ;
std::ifstream mEffectCount;
+ std::ofstream mEffectBrakingTimeBank;
+ std::ofstream mEffectBrakingTimeIndex;
+ std::ifstream mEffectBrakingTimeMs;
std::ifstream mVibeState;
std::ifstream mOwtFreeSpace;
std::ofstream mF0CompEnable;
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
index 3161528..81cc5fd 100644
--- a/vibrator/cs40l26/Vibrator.cpp
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -29,10 +29,12 @@
#include <cmath>
#include <fstream>
#include <iostream>
+#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <sstream>
+#include <string_view>
#include "DspMemChunk.h"
#include "Stats.h"
@@ -83,26 +85,6 @@
static constexpr auto POLLING_TIMEOUT = 50; // POLLING_TIMEOUT < ASYNC_COMPLETION_TIMEOUT
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
-// Measured resonant frequency, f0_measured, is represented by Q10.14 fixed
-// point format on cs40l26 devices. The expression to calculate f0 is:
-// f0 = f0_measured / 2^Q14_BIT_SHIFT
-// See the LRA Calibration Support documentation for more details.
-static constexpr int32_t Q14_BIT_SHIFT = 14;
-
-// Measured ReDC. The LRA series resistance (ReDC), expressed as follows
-// redc(ohms) = redc_measured / 2^Q15_BIT_SHIFT.
-// This value represents the unit-specific ReDC input to the click compensation
-// algorithm. It can be overwritten at a later time by writing to the redc_stored
-// sysfs control.
-// See the LRA Calibration Support documentation for more details.
-static constexpr int32_t Q15_BIT_SHIFT = 15;
-
-// Measured Q factor, q_measured, is represented by Q8.16 fixed
-// point format on cs40l26 devices. The expression to calculate q is:
-// q = q_measured / 2^Q16_BIT_SHIFT
-// See the LRA Calibration Support documentation for more details.
-static constexpr int32_t Q16_BIT_SHIFT = 16;
-
static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.0;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
@@ -167,8 +149,51 @@
std::vector<float> pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 1.0);
#endif
-static float redcToFloat(std::string *caldata) {
- return static_cast<float>(std::stoul(*caldata, nullptr, 16)) / (1 << Q15_BIT_SHIFT);
+enum class QValueFormat {
+ FORMAT_7_16, // Q
+ FORMAT_8_15, // Redc
+ FORMAT_9_14 // F0
+};
+
+static float qValueToFloat(std::string_view qValueInHex, QValueFormat qValueFormat, bool isSigned) {
+ uint32_t intBits = 0;
+ uint32_t fracBits = 0;
+ switch (qValueFormat) {
+ case QValueFormat::FORMAT_7_16:
+ intBits = 7;
+ fracBits = 16;
+ break;
+ case QValueFormat::FORMAT_8_15:
+ intBits = 8;
+ fracBits = 15;
+ break;
+ case QValueFormat::FORMAT_9_14:
+ intBits = 9;
+ fracBits = 14;
+ break;
+ default:
+ ALOGE("Q Format enum not implemented");
+ return std::numeric_limits<float>::quiet_NaN();
+ }
+
+ uint32_t totalBits = intBits + fracBits + (isSigned ? 1 : 0);
+
+ int valInt = 0;
+ std::stringstream ss;
+ ss << std::hex << qValueInHex;
+ ss >> valInt;
+
+ if (ss.fail() || !ss.eof()) {
+ ALOGE("Invalid hex format: %s", qValueInHex.data());
+ return std::numeric_limits<float>::quiet_NaN();
+ }
+
+ // Handle sign extension if necessary
+ if (isSigned && (valInt & (1 << (totalBits - 1)))) {
+ valInt -= 1 << totalBits;
+ }
+
+ return static_cast<float>(valInt) / (1 << fracBits);
}
Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal,
@@ -184,12 +209,15 @@
mFfEffects.resize(WAVEFORM_MAX_INDEX);
mEffectDurations.resize(WAVEFORM_MAX_INDEX);
+ mEffectBrakingDurations.resize(WAVEFORM_MAX_INDEX);
mEffectDurations = {
#if defined(UNSPECIFIED_ACTUATOR)
/* For Z-LRA actuators */
- 1000, 100, 25, 1000, 300, 133, 150, 500, 100, 6, 12, 1000, 13, 5,
+ 1000, 100, 25, 1000, 247, 166, 150, 500, 100, 6, 17, 1000, 13, 5,
+#elif defined(LEGACY_ZLRA_ACTUATOR)
+ 1000, 100, 25, 1000, 150, 100, 150, 500, 100, 6, 25, 1000, 13, 5,
#else
- 1000, 100, 12, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 13, 5,
+ 1000, 100, 9, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 13, 5,
#endif
}; /* 11+3 waveforms. The duration must < UINT16_MAX */
mEffectCustomData.reserve(WAVEFORM_MAX_INDEX);
@@ -222,6 +250,11 @@
if (mFfEffects[effectIndex].id != effectIndex) {
ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffects[effectIndex].id);
}
+
+ if (mHwApi->hasEffectBrakingTimeBank()) {
+ mHwApi->setEffectBrakingTimeIndex(effectIndex);
+ mHwApi->getEffectBrakingTimeMs(&mEffectBrakingDurations[effectIndex]);
+ }
} else {
/* Initiate placeholders for OWT effects. */
numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
@@ -241,8 +274,7 @@
if (mHwCal->getF0(&caldata)) {
mHwApi->setF0(caldata);
- mResonantFrequency =
- static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT);
+ mResonantFrequency = qValueToFloat(caldata, QValueFormat::FORMAT_9_14, false);
} else {
mStatsApi->logError(kHwCalError);
ALOGE("Failed to get resonant frequency (%d): %s, using default resonant HZ: %f", errno,
@@ -251,7 +283,7 @@
}
if (mHwCal->getRedc(&caldata)) {
mHwApi->setRedc(caldata);
- mRedc = redcToFloat(&caldata);
+ mRedc = qValueToFloat(caldata, QValueFormat::FORMAT_8_15, false);
}
if (mHwCal->getQ(&caldata)) {
mHwApi->setQ(caldata);
@@ -497,7 +529,7 @@
return status;
}
- *durationMs = mEffectDurations[effectIndex];
+ *durationMs = mEffectDurations[effectIndex] + mEffectBrakingDurations[effectIndex];
} else {
*durationMs = 0;
}
@@ -509,7 +541,6 @@
VFTRACE(composite, callback);
uint16_t size;
uint16_t nextEffectDelay;
- uint16_t totalDuration = 0;
mStatsApi->logLatencyStart(kCompositionEffectLatency);
@@ -521,7 +552,6 @@
/* Check if there is a wait before the first effect. */
nextEffectDelay = composite.front().delayMs;
- totalDuration += nextEffectDelay;
if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
ALOGE("%s: Invalid delay %u", __func__, nextEffectDelay);
mStatsApi->logError(kBadCompositeError);
@@ -558,7 +588,6 @@
return status;
}
effectVolLevel = intensityToVolLevel(e_curr.scale, effectIndex);
- totalDuration += mEffectDurations[effectIndex];
}
/* Fetch the next composite effect delay and fill into the current section */
@@ -573,7 +602,6 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
nextEffectDelay = delay;
- totalDuration += delay;
}
if (effectIndex == 0 && nextEffectDelay == 0) {
@@ -581,6 +609,9 @@
mStatsApi->logError(kBadCompositeError);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+
+ nextEffectDelay += mEffectBrakingDurations[effectIndex];
+
mStatsApi->logPrimitive(effectIndex);
ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/,
nextEffectDelay /*delay*/);
@@ -819,7 +850,7 @@
ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- *qFactor = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q16_BIT_SHIFT);
+ *qFactor = qValueToFloat(caldata, QValueFormat::FORMAT_7_16, false);
return ndk::ScopedAStatus::ok();
}
@@ -924,7 +955,7 @@
std::string caldata{8, '0'};
if (mHwCal->getRedc(&caldata)) {
mHwApi->setRedc(caldata);
- mRedc = redcToFloat(&caldata);
+ mRedc = qValueToFloat(caldata, QValueFormat::FORMAT_8_15, false);
} else {
mStatsApi->logError(kHwCalError);
ALOGE("Failed to get resistance value from calibration file");
@@ -1276,11 +1307,11 @@
dprintf(fd, " FF Effect:\n");
dprintf(fd, " Physical Waveform:\n");
- dprintf(fd, "\tId\tIndex\tt ->\tt'\n");
+ dprintf(fd, "\tId\tIndex\tt ->\tt'\tBrake\n");
for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
- dprintf(fd, "\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id,
+ dprintf(fd, "\t%d\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id,
mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
- mFfEffects[effectId].replay.length);
+ mFfEffects[effectId].replay.length, mEffectBrakingDurations[effectId]);
}
dprintf(fd, " OWT Waveform:\n");
dprintf(fd, "\tId\tBytes\tData\n");
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
index 8a9002b..99261a7 100644
--- a/vibrator/cs40l26/Vibrator.h
+++ b/vibrator/cs40l26/Vibrator.h
@@ -57,6 +57,15 @@
virtual bool setQ(std::string value) = 0;
// Reports the number of effect waveforms loaded in firmware.
virtual bool getEffectCount(uint32_t *value) = 0;
+ // Checks whether braking time bank is supported.
+ virtual bool hasEffectBrakingTimeBank() = 0;
+ // Specifies the bank of the effect for querying braking time.
+ // 0: RAM bank, 2: OWT bank
+ virtual bool setEffectBrakingTimeBank(uint32_t value) = 0;
+ // Specifies the index of an effect whose braking time is to be read.
+ virtual bool setEffectBrakingTimeIndex(uint32_t value) = 0;
+ // Gets the braking time duration of SVC effects (returns 0 if not SVC).
+ virtual bool getEffectBrakingTimeMs(uint32_t *value) = 0;
// Blocks until timeout or vibrator reaches desired state
// (2 = ASP enabled, 1 = haptic enabled, 0 = disabled).
virtual bool pollVibeState(uint32_t value, int32_t timeoutMs = -1) = 0;
@@ -251,6 +260,7 @@
std::array<uint32_t, 2> mLongEffectVol;
std::vector<ff_effect> mFfEffects;
std::vector<uint32_t> mEffectDurations;
+ std::vector<uint32_t> mEffectBrakingDurations;
std::vector<std::vector<int16_t>> mEffectCustomData;
std::future<void> mAsyncHandle;
int8_t mActiveId{-1};
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
index 6ea9d3b..d48e7ee 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
@@ -13,6 +13,9 @@
calibration/q_stored
default/vibe_state
default/num_waves
+ default/braking_time_bank
+ default/braking_time_index
+ default/braking_time_ms
default/f0_offset
default/owt_free_space
default/f0_comp_enable
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
index 9a46abc..ccf35d8 100644
--- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
@@ -13,6 +13,9 @@
calibration/q_stored
default/vibe_state
default/num_waves
+ default/braking_time_bank
+ default/braking_time_index
+ default/braking_time_ms
default/f0_offset
default/owt_free_space
default/f0_comp_enable
diff --git a/vibrator/cs40l26/fuzzer/Android.bp b/vibrator/cs40l26/fuzzer/Android.bp
new file mode 100644
index 0000000..c60c8bf
--- /dev/null
+++ b/vibrator/cs40l26/fuzzer/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_pixel_system_sw_touch_haptic",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "VibratorHalCs40l26Fuzzer",
+ relative_install_path: "",
+ defaults: [
+ "VibratorHalCs40l26BinaryDefaults",
+ "VibratorCapoDefaults",
+ "service_fuzzer_defaults",
+ ],
+ srcs: [
+ "fuzzer-vibrator.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ ],
+ fuzz_config: {
+ triage_assignee: "[email protected]",
+ componentid: 716924,
+ },
+}
diff --git a/vibrator/cs40l26/fuzzer/fuzzer-vibrator.cpp b/vibrator/cs40l26/fuzzer/fuzzer-vibrator.cpp
new file mode 100644
index 0000000..7fad137
--- /dev/null
+++ b/vibrator/cs40l26/fuzzer/fuzzer-vibrator.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "Hardware.h"
+#include "Vibrator.h"
+
+using ::aidl::android::hardware::vibrator::HwApi;
+using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::Vibrator;
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+// No stats collection.
+class FakeStatsApi : public Vibrator::StatsApi {
+ public:
+ FakeStatsApi() = default;
+ ~FakeStatsApi() = default;
+
+ bool logPrimitive(uint16_t) override { return true; }
+
+ bool logWaveform(uint16_t, int32_t) override { return true; }
+
+ bool logError(uint16_t) override { return true; }
+
+ bool logLatencyStart(uint16_t) override { return true; }
+
+ bool logLatencyEnd() { return true; }
+
+ void debug(int32_t) override {}
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ auto vibService = ndk::SharedRefBase::make<Vibrator>(
+ std::make_unique<HwApi>(), std::make_unique<HwCal>(), std::make_unique<FakeStatsApi>());
+
+ fuzzService(vibService->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/vibrator/cs40l26/tests/Android.bp b/vibrator/cs40l26/tests/Android.bp
index 8621537..348c9d5 100644
--- a/vibrator/cs40l26/tests/Android.bp
+++ b/vibrator/cs40l26/tests/Android.bp
@@ -19,7 +19,10 @@
cc_test {
name: "VibratorHalCs40l26TestSuite",
- defaults: ["VibratorHalCs40l26TestDefaults"],
+ defaults: [
+ "VibratorHalCs40l26TestDefaults",
+ "haptics_feature_defaults",
+ ],
srcs: [
"test-hwcal.cpp",
"test-hwapi.cpp",
diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h
index 0837938..da8daf4 100644
--- a/vibrator/cs40l26/tests/mocks.h
+++ b/vibrator/cs40l26/tests/mocks.h
@@ -28,6 +28,10 @@
MOCK_METHOD1(setRedc, bool(std::string value));
MOCK_METHOD1(setQ, bool(std::string value));
MOCK_METHOD1(getEffectCount, bool(uint32_t *value));
+ MOCK_METHOD0(hasEffectBrakingTimeBank, bool());
+ MOCK_METHOD1(setEffectBrakingTimeBank, bool(uint32_t value));
+ MOCK_METHOD1(setEffectBrakingTimeIndex, bool(uint32_t value));
+ MOCK_METHOD1(getEffectBrakingTimeMs, bool(uint32_t *value));
MOCK_METHOD2(pollVibeState, bool(uint32_t value, int32_t timeoutMs));
MOCK_METHOD0(hasOwtFreeSpace, bool());
MOCK_METHOD1(getOwtFreeSpace, bool(uint32_t *value));
diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp
index 698b68e..76cb897 100644
--- a/vibrator/cs40l26/tests/test-vibrator.cpp
+++ b/vibrator/cs40l26/tests/test-vibrator.cpp
@@ -75,7 +75,14 @@
static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
- 0, 100, 12, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 1000, 1000};
+#if defined(UNSPECIFIED_ACTUATOR)
+ /* For Z-LRA actuators */
+ 1000, 100, 25, 1000, 247, 166, 150, 500, 100, 6, 17, 1000, 13, 5};
+#elif defined(LEGACY_ZLRA_ACTUATOR)
+ 1000, 100, 25, 1000, 150, 100, 150, 500, 100, 6, 25, 1000, 13, 5};
+#else
+ 1000, 100, 9, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 13, 5};
+#endif
// Constants With Prescribed Values
@@ -395,6 +402,11 @@
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
+ EXPECT_CALL(*mMockApi, setEffectBrakingTimeBank(0)).WillRepeatedly(Return(true));
+ for (uint32_t i = 0; i < WAVEFORM_MAX_PHYSICAL_INDEX; i++) {
+ EXPECT_CALL(*mMockApi, setEffectBrakingTimeIndex(i)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mMockApi, getEffectBrakingTimeMs(_)).WillRepeatedly(Return(true));
+ }
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, getContextScale()).WillRepeatedly(Return(0));
EXPECT_CALL(*mMockApi, getContextEnable()).WillRepeatedly(Return(false));
diff --git a/vibrator/drv2624/fuzzer/Android.bp b/vibrator/drv2624/fuzzer/Android.bp
new file mode 100644
index 0000000..ae8df09
--- /dev/null
+++ b/vibrator/drv2624/fuzzer/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_pixel_system_sw_touch_haptic",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "VibratorHalDrv2624Fuzzer",
+ relative_install_path: "",
+ defaults: [
+ "VibratorHalDrv2624BinaryDefaults",
+ "service_fuzzer_defaults",
+ ],
+ srcs: [
+ "fuzzer-vibrator.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.drv2624",
+ ],
+ fuzz_config: {
+ triage_assignee: "[email protected]",
+ componentid: 716924,
+ },
+}
diff --git a/vibrator/drv2624/fuzzer/fuzzer-vibrator.cpp b/vibrator/drv2624/fuzzer/fuzzer-vibrator.cpp
new file mode 100644
index 0000000..d1b400a
--- /dev/null
+++ b/vibrator/drv2624/fuzzer/fuzzer-vibrator.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "Hardware.h"
+#include "Vibrator.h"
+
+using ::aidl::android::hardware::vibrator::HwApi;
+using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::Vibrator;
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ std::shared_ptr<Vibrator> vibService =
+ ndk::SharedRefBase::make<Vibrator>(HwApi::Create(), std::make_unique<HwCal>());
+
+ fuzzService(vibService->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}