summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bookatz <bookatz@google.com> 2018-02-01 15:35:04 -0800
committer Bookatz <bookatz@google.com> 2018-02-02 14:41:10 -0800
commitb223c4ecae9a101ef820ad71bf89461b5447a34b (patch)
tree90952beb3cfb33bf18784152828296d14ba1c217
parent4a740846223aa58af065e2256fb0caa0ce6f9c53 (diff)
Statsd - adb cmd for AppHook; long compare support
1. Create an adb command for statsd to let the adb user write AppHook to the StatsLog buffer. This can be used in the CTS tests (instead of relying on screen state changes, etc. for conditioning), and for local testing. 2. Fixes the fact that AppHook loggers can spoof uids (they can put whatever uid they want and statsd doesn't validate it - now it will). 3. Allow FieldValueMatcher to compare longs (not just ints). Fix: 72266788 Fix: 72836157 Fix: 72872130 Fix: 72829733 Test: manually did the adb command. Test: run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.alert.AnomalyDetectionTests Test: locally modified android.cts.statsd.alert.BroadcastSubscriberTests#testBroadcastSubscriber to have the app attempt both valid and invalid AppHook writes. Change-Id: I68931a71805bcfa6fe56e7a0a0d3f07290cb78d1
-rw-r--r--cmds/statsd/src/StatsService.cpp50
-rw-r--r--cmds/statsd/src/StatsService.h5
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp89
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp37
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h6
-rw-r--r--cmds/statsd/src/statsd_config.proto2
6 files changed, 144 insertions, 45 deletions
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 4e4145439e25..32da94f862c5 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -34,6 +34,7 @@
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <utils/String16.h>
+#include <statslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -235,6 +236,10 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>&
if (!args[0].compare(String8("write-to-disk"))) {
return cmd_write_data_to_disk(out);
}
+
+ if (!args[0].compare(String8("log-app-hook"))) {
+ return cmd_log_app_hook(out, args);
+ }
}
print_cmd_help(out);
@@ -272,6 +277,15 @@ void StatsService::print_cmd_help(FILE* out) {
fprintf(out, " Flushes all data on memory to disk.\n");
fprintf(out, "\n");
fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats log-app-hook [UID] LABEL STATE\n");
+ fprintf(out, " Writes an AppHook event to the statslog buffer.\n");
+ fprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
+ fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ fprintf(out, " uid is used.\n");
+ fprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
+ fprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
fprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
fprintf(out, "\n");
@@ -523,6 +537,42 @@ status_t StatsService::cmd_write_data_to_disk(FILE* out) {
return NO_ERROR;
}
+status_t StatsService::cmd_log_app_hook(FILE* out, const Vector<String8>& args) {
+ bool good = false;
+ int32_t uid;
+ int32_t label;
+ int32_t state;
+ const int argCount = args.size();
+ if (argCount == 3) {
+ // Automatically pick the UID
+ uid = IPCThreadState::self()->getCallingUid();
+ label = atoi(args[1].c_str());
+ state = atoi(args[2].c_str());
+ good = true;
+ } else if (argCount == 4) {
+ uid = atoi(args[1].c_str());
+ // If it's a userdebug or eng build, then the shell user can impersonate other uids.
+ // Otherwise, the uid must match the actual caller's uid.
+ if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
+ label = atoi(args[2].c_str());
+ state = atoi(args[3].c_str());
+ good = true;
+ } else {
+ fprintf(out,
+ "Selecting a UID for writing AppHook can only be dumped for other UIDs on eng"
+ " or userdebug builds.\n");
+ }
+ }
+ if (good) {
+ fprintf(out, "Logging AppHook(%d, %d, %d) to statslog.\n", uid, label, state);
+ android::util::stats_write(android::util::APP_HOOK, uid, label, state);
+ } else {
+ print_cmd_help(out);
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+}
+
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index fd3ed1dbed72..109752b0295f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -183,6 +183,11 @@ private:
status_t cmd_write_data_to_disk(FILE* out);
/**
+ * Write an AppHook event to the StatsLog buffer, as though StatsLog.write(APP_HOOK).
+ */
+ status_t cmd_log_app_hook(FILE* out, const Vector<String8>& args);
+
+ /**
* Print contents of a pulled metrics source.
*/
status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index b6f440f2e348..fae91729fe4f 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -129,43 +129,60 @@ bool matchesNonRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap
}
bool matched = false;
switch (matcher.value_matcher_case()) {
- case FieldValueMatcher::ValueMatcherCase::kEqBool:
- // Logd does not support bool, it is int instead.
- matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
- break;
- case FieldValueMatcher::ValueMatcherCase::kEqString:
- {
- if (IsAttributionUidField(*rootField)) {
- const int uid = ret.first->second.value_int();
- std::set<string> packageNames =
+ case FieldValueMatcher::ValueMatcherCase::kEqBool: {
+ // Logd does not support bool, it is int instead.
+ matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqString: {
+ if (IsAttributionUidField(*rootField)) {
+ const int uid = ret.first->second.value_int();
+ std::set<string> packageNames =
uidMap.getAppNamesFromUid(uid, true /* normalize*/);
- matched = packageNames.find(matcher.eq_string()) != packageNames.end();
- } else {
- matched = (ret.first->second.value_str() == matcher.eq_string());
- }
- }
- break;
- case FieldValueMatcher::ValueMatcherCase::kEqInt:
- matched = (ret.first->second.value_int() == matcher.eq_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLtInt:
- matched = (ret.first->second.value_int() < matcher.lt_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGtInt:
- matched = (ret.first->second.value_int() > matcher.gt_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLtFloat:
- matched = (ret.first->second.value_float() < matcher.lt_float());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGtFloat:
- matched = (ret.first->second.value_float() > matcher.gt_float());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLteInt:
- matched = (ret.first->second.value_int() <= matcher.lte_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGteInt:
- matched = (ret.first->second.value_int() >= matcher.gte_int());
- break;
+ matched = packageNames.find(matcher.eq_string()) != packageNames.end();
+ } else {
+ matched = (ret.first->second.value_str() == matcher.eq_string());
+ }
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val == matcher.eq_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val < matcher.lt_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val > matcher.gt_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
+ matched = (ret.first->second.value_float() < matcher.lt_float());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
+ matched = (ret.first->second.value_float() > matcher.gt_float());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLteInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val <= matcher.lte_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGteInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val >= matcher.gte_int());
+ break;
+ }
default:
break;
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 636289522780..417145c00e54 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -47,7 +47,7 @@ const int FIELD_ID_METRICS = 1;
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const long timeBaseSec, sp<UidMap> uidMap)
- : mConfigKey(key), mUidMap(uidMap) {
+ : mConfigKey(key), mUidMap(uidMap), mStatsdUid(getStatsdUid()) {
mConfigValid =
initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
@@ -61,6 +61,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
mAllowedUid.push_back(1000);
mAllowedUid.push_back(0);
+ mAllowedUid.push_back(mStatsdUid);
mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
} else {
for (const auto& source : config.allowed_log_source()) {
@@ -191,18 +192,28 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid.
// TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
+ status_t err = NO_ERROR;
+
+ // Uid is 3rd from last field and must match the caller's uid,
+ // unless that caller is statsd itself (statsd is allowed to spoof uids).
+ long appHookUid = event.GetLong(event.size()-2, &err);
+ int32_t loggerUid = event.GetUid();
+ if (err != NO_ERROR || (loggerUid != appHookUid && loggerUid != mStatsdUid)) {
+ VLOG("AppHook has invalid uid: claimed %ld but caller is %d", appHookUid, loggerUid);
+ return;
+ }
// Label is 2nd from last field and must be from [0, 15].
- status_t err = NO_ERROR;
- long label = event.GetLong(event.size()-1, &err);
- if (err != NO_ERROR || label < 0 || label > 15) {
- VLOG("App hook does not have valid label %ld", label);
+ long appHookLabel = event.GetLong(event.size()-1, &err);
+ if (err != NO_ERROR || appHookLabel < 0 || appHookLabel > 15) {
+ VLOG("AppHook does not have valid label %ld", appHookLabel);
return;
}
+
// The state must be from 0,3. This part of code must be manually updated.
- long apphookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR || apphookState < 0 || apphookState > 3) {
- VLOG("App hook does not have valid state %ld", apphookState);
+ long appHookState = event.GetLong(event.size(), &err);
+ if (err != NO_ERROR || appHookState < 0 || appHookState > 3) {
+ VLOG("AppHook does not have valid state %ld", appHookState);
return;
}
} else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
@@ -322,6 +333,16 @@ size_t MetricsManager::byteSize() {
return totalSize;
}
+int32_t MetricsManager::getStatsdUid() {
+ auto suit = UidMap::sAidToUidMapping.find("AID_STATSD");
+ if (suit != UidMap::sAidToUidMapping.end()) {
+ return suit->second;
+ } else {
+ ALOGE("Statsd failed to find its own uid!");
+ return -1;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index d4b9102d5ddc..a1220f9ecd49 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -75,6 +75,9 @@ private:
sp<UidMap> mUidMap;
+ // The uid of statsd.
+ const int32_t mStatsdUid;
+
bool mConfigValid = false;
// The uid log sources from StatsdConfig.
@@ -136,6 +139,9 @@ private:
void initLogSourceWhiteList();
+ // Fetches the uid of statsd from UidMap.
+ static int32_t getStatsdUid();
+
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 2ea79a64a5ea..3eaf7a17a3e2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -64,7 +64,7 @@ message FieldValueMatcher {
oneof value_matcher {
bool eq_bool = 3;
string eq_string = 4;
- int32 eq_int = 5;
+ int64 eq_int = 5;
int64 lt_int = 6;
int64 gt_int = 7;