summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-09-23 10:20:23 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-09-23 10:20:23 +0000
commitccd352d104ac316c53298fd3988bcf656923bab4 (patch)
tree05793d5a720fb11b5e06808194be971872e24911
parent60ac11eaa6b92d9dc8772520de3e59ecebbff0bd (diff)
parent01ffc29dbec1ffce7c8eac9e4ebb3fc80b7bb135 (diff)
downloadStatsD-android13-mainline-scheduling-release.tar.gz
Snap for 9098257 from 01ffc29dbec1ffce7c8eac9e4ebb3fc80b7bb135 to mainline-scheduling-releaseaml_sch_331113000aml_sch_331111000android13-mainline-scheduling-release
Change-Id: I6ddabcfec3b615b2c26a0d4f669d796b1de94b48
-rw-r--r--apex/Android.bp7
-rw-r--r--apex/apex_manifest.json5
-rw-r--r--lib/libstatspull/Android.bp2
-rw-r--r--lib/libstatspull/stats_pull_atom_callback.cpp57
-rw-r--r--lib/libstatssocket/statsd_writer.c2
-rw-r--r--service/Android.bp1
-rw-r--r--service/java/com/android/server/stats/StatsCompanionService.java3
-rw-r--r--statsd/src/flags/FlagProvider.h3
-rw-r--r--statsd/src/guardrail/StatsdStats.h6
-rw-r--r--statsd/src/main.cpp44
-rw-r--r--statsd/src/matchers/matcher_util.cpp118
-rw-r--r--statsd/src/metrics/DurationMetricProducer.cpp17
-rw-r--r--statsd/src/metrics/DurationMetricProducer.h2
-rw-r--r--statsd/src/metrics/GaugeMetricProducer.cpp38
-rw-r--r--statsd/src/metrics/GaugeMetricProducer.h7
-rw-r--r--statsd/src/metrics/MetricProducer.cpp20
-rw-r--r--statsd/src/metrics/MetricProducer.h6
-rw-r--r--statsd/src/metrics/MetricsManager.h3
-rw-r--r--statsd/src/metrics/NumericValueMetricProducer.cpp5
-rw-r--r--statsd/src/metrics/NumericValueMetricProducer.h3
-rw-r--r--statsd/src/metrics/ValueMetricProducer.cpp35
-rw-r--r--statsd/src/metrics/ValueMetricProducer.h5
-rw-r--r--statsd/src/shell/ShellSubscriber.cpp12
-rw-r--r--statsd/src/shell/ShellSubscriber.h9
-rw-r--r--statsd/src/stats_log.proto2
-rw-r--r--statsd/src/statsd_config.proto10
-rw-r--r--statsd/src/storage/StorageManager.cpp52
-rw-r--r--statsd/src/storage/StorageManager.h10
-rw-r--r--statsd/tests/LogEntryMatcher_test.cpp301
-rw-r--r--statsd/tests/MetricsManager_test.cpp66
-rw-r--r--statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp76
-rw-r--r--statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp122
-rw-r--r--statsd/tests/shell/ShellSubscriber_test.cpp23
-rw-r--r--statsd/tests/statsd_test_util.cpp14
-rw-r--r--statsd/tests/statsd_test_util.h2
-rw-r--r--statsd/tests/storage/StorageManager_test.cpp84
-rw-r--r--statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java1
-rw-r--r--tests/src/android/cts/statsd/atom/AtomTestCase.java29
-rw-r--r--tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java4
-rw-r--r--tests/src/android/cts/statsd/metric/ValueMetricsTests.java7
40 files changed, 1007 insertions, 206 deletions
diff --git a/apex/Android.bp b/apex/Android.bp
index 2988a1d9..543627fd 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -69,8 +69,11 @@ prebuilt_etc {
// ==========================================================
sdk {
name: "statsd-module-sdk",
- bootclasspath_fragments: ["com.android.os.statsd-bootclasspath-fragment"],
- systemserverclasspath_fragments: ["com.android.os.statsd-systemserverclasspath-fragment"],
+ apexes: [
+ // Adds exportable dependencies of the APEX to the sdk,
+ // e.g. *classpath_fragments.
+ "com.android.os.statsd",
+ ],
native_shared_libs: [
"libstatssocket",
],
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index 0fb32272..a8ea55c9 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,5 +1,8 @@
{
"name": "com.android.os.statsd",
- "version": 339990000
+
+ // Placeholder module version to be replaced during build.
+ // Do not change!
+ "version": 0
}
diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp
index c0bc46d7..60f237c2 100644
--- a/lib/libstatspull/Android.bp
+++ b/lib/libstatspull/Android.bp
@@ -123,7 +123,7 @@ cc_test {
test_suites: ["general-tests", "mts-statsd"],
test_config: "libstatspull_test.xml",
- //TODO(b/153588990): Remove when the build system properly separates
+ //TODO(b/153588990): Remove when the build system properly separates
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
diff --git a/lib/libstatspull/stats_pull_atom_callback.cpp b/lib/libstatspull/stats_pull_atom_callback.cpp
index f85db08d..8a1c89b5 100644
--- a/lib/libstatspull/stats_pull_atom_callback.cpp
+++ b/lib/libstatspull/stats_pull_atom_callback.cpp
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-#include <map>
-#include <thread>
-#include <vector>
-
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-
#include <aidl/android/os/BnPullAtomCallback.h>
#include <aidl/android/os/IPullAtomResultReceiver.h>
#include <aidl/android/os/IStatsd.h>
@@ -28,6 +21,12 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+
+#include <map>
+#include <thread>
+#include <vector>
using Status = ::ndk::ScopedAStatus;
using aidl::android::os::BnPullAtomCallback;
@@ -159,20 +158,20 @@ class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
};
/**
- * @brief pullAtomMutex is used to guard simultaneous access to mPullers from below threads
+ * @brief pullersMutex is used to guard simultaneous access to pullers from below threads
* Main thread
* - AStatsManager_setPullAtomCallback()
* - AStatsManager_clearPullAtomCallback()
* Binder thread:
* - StatsdProvider::binderDied()
*/
-static std::mutex pullAtomMutex;
+static std::mutex pullersMutex;
-static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
+static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullers;
class StatsdProvider {
public:
- StatsdProvider() : deathRecipient(AIBinder_DeathRecipient_new(binderDied)) {
+ StatsdProvider() : mDeathRecipient(AIBinder_DeathRecipient_new(binderDied)) {
}
~StatsdProvider() {
@@ -180,21 +179,21 @@ public:
}
std::shared_ptr<IStatsd> getStatsService() {
- std::lock_guard<std::mutex> lock(statsdMutex);
- if (!statsd) {
+ std::lock_guard<std::mutex> lock(mStatsdMutex);
+ if (!mStatsd) {
// Fetch statsd
::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
- statsd = IStatsd::fromBinder(binder);
- if (statsd) {
- AIBinder_linkToDeath(binder.get(), deathRecipient.get(), this);
+ mStatsd = IStatsd::fromBinder(binder);
+ if (mStatsd) {
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), this);
}
}
- return statsd;
+ return mStatsd;
}
void resetStatsService() {
- std::lock_guard<std::mutex> lock(statsdMutex);
- statsd = nullptr;
+ std::lock_guard<std::mutex> lock(mStatsdMutex);
+ mStatsd = nullptr;
}
static void binderDied(void* cookie) {
@@ -210,8 +209,8 @@ public:
// copy of the data with the lock held before iterating through the map.
std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
{
- std::lock_guard<std::mutex> lock(pullAtomMutex);
- pullersCopy = mPullers;
+ std::lock_guard<std::mutex> lock(pullersMutex);
+ pullersCopy = pullers;
}
for (const auto& it : pullersCopy) {
statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
@@ -222,16 +221,16 @@ public:
private:
/**
- * @brief statsdMutex is used to guard simultaneous access to statsd from below threads:
+ * @brief mStatsdMutex is used to guard simultaneous access to mStatsd from below threads:
* Work thread
* - registerStatsPullAtomCallbackBlocking()
* - unregisterStatsPullAtomCallbackBlocking()
* Binder thread:
* - StatsdProvider::binderDied()
*/
- std::mutex statsdMutex;
- std::shared_ptr<IStatsd> statsd;
- ::ndk::ScopedAIBinder_DeathRecipient deathRecipient;
+ std::mutex mStatsdMutex;
+ std::shared_ptr<IStatsd> mStatsd;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
};
static std::shared_ptr<StatsdProvider> statsProvider = std::make_shared<StatsdProvider>();
@@ -276,9 +275,9 @@ void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomM
timeoutMillis, additiveFields);
{
- std::lock_guard<std::mutex> lg(pullAtomMutex);
+ std::lock_guard<std::mutex> lock(pullersMutex);
// Always add to the map. If statsd is dead, we will add them when it comes back.
- mPullers[atom_tag] = callbackBinder;
+ pullers[atom_tag] = callbackBinder;
}
std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, statsProvider,
@@ -288,10 +287,10 @@ void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomM
void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
{
- std::lock_guard<std::mutex> lg(pullAtomMutex);
+ std::lock_guard<std::mutex> lock(pullersMutex);
// Always remove the puller from our map.
// If statsd is down, we will not register it when it comes back.
- mPullers.erase(atom_tag);
+ pullers.erase(atom_tag);
}
std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag, statsProvider);
diff --git a/lib/libstatssocket/statsd_writer.c b/lib/libstatssocket/statsd_writer.c
index c00c9783..06695fe4 100644
--- a/lib/libstatssocket/statsd_writer.c
+++ b/lib/libstatssocket/statsd_writer.c
@@ -76,7 +76,7 @@ static int statsdAvailable();
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
+static void statsdNoteDrop(int error, int tag);
static int statsdIsClosed();
struct android_log_transport_write statsdLoggerWrite = {
diff --git a/service/Android.bp b/service/Android.bp
index bad74ce7..5e96b1e2 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -25,6 +25,7 @@ filegroup {
java_library {
name: "service-statsd",
+ defaults: ["standalone-system-server-module-optimize-defaults"],
srcs: [ ":service-statsd-sources" ],
sdk_version: "system_server_current",
libs: [
diff --git a/service/java/com/android/server/stats/StatsCompanionService.java b/service/java/com/android/server/stats/StatsCompanionService.java
index bc7e25e6..f8b9400e 100644
--- a/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/service/java/com/android/server/stats/StatsCompanionService.java
@@ -297,7 +297,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
List<PackageInfo> allPackages = new ArrayList<>(
pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES
| PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_ANY_USER,
+ | PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
userHandle.getIdentifier()));
// We make a second query to package manager for the apex modules because package manager
// returns both installed and uninstalled apexes with
diff --git a/statsd/src/flags/FlagProvider.h b/statsd/src/flags/FlagProvider.h
index dcdf35fb..6684ba4d 100644
--- a/statsd/src/flags/FlagProvider.h
+++ b/statsd/src/flags/FlagProvider.h
@@ -126,8 +126,7 @@ private:
FRIEND_TEST(FlagProviderTest_SPlus, TestGetFlagBoolServerFlagEmptyDefaultTrue);
FRIEND_TEST(FlagProviderTest_SPlus_RealValues, TestGetBootFlagBoolServerFlagTrue);
FRIEND_TEST(FlagProviderTest_SPlus_RealValues, TestGetBootFlagBoolServerFlagFalse);
- FRIEND_TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagFalse);
- FRIEND_TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagTrue);
+ FRIEND_TEST(MetricsManagerTest_SPlus, TestAtomMatcherOptimizationEnabledFlag);
};
} // namespace statsd
diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h
index d812e55a..1393e56f 100644
--- a/statsd/src/guardrail/StatsdStats.h
+++ b/statsd/src/guardrail/StatsdStats.h
@@ -82,9 +82,9 @@ struct ConfigStats {
};
struct UidMapStats {
- int32_t changes;
- int32_t bytes_used;
- int32_t dropped_changes;
+ int32_t changes = 0;
+ int32_t bytes_used = 0;
+ int32_t dropped_changes = 0;
int32_t deleted_apps = 0;
};
diff --git a/statsd/src/main.cpp b/statsd/src/main.cpp
index 66b43894..cd64fe7f 100644
--- a/statsd/src/main.cpp
+++ b/statsd/src/main.cpp
@@ -39,20 +39,12 @@ using std::make_shared;
shared_ptr<StatsService> gStatsService = nullptr;
sp<StatsSocketListener> gSocketListener = nullptr;
+int gCtrlPipe[2];
void signalHandler(int sig) {
- if (sig == SIGPIPE) {
- // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
- // client process. Don't prematurely exit(1) here. Instead, ignore the
- // signal and allow the write call to return EPIPE.
- ALOGI("statsd received SIGPIPE. Ignoring signal.");
- return;
- }
-
- if (gSocketListener != nullptr) gSocketListener->stopListener();
- if (gStatsService != nullptr) gStatsService->Terminate();
ALOGW("statsd terminated on receiving signal %d.", sig);
- exit(1);
+ const char c = 'q';
+ write(gCtrlPipe[1], &c, 1);
}
void registerSignalHandlers()
@@ -60,11 +52,15 @@ void registerSignalHandlers()
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- sa.sa_handler = signalHandler;
+
+ sa.sa_handler = SIG_IGN;
+ // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
+ // client process. Don't prematurely exit(1) here. Instead, ignore the
+ // signal and allow the write call to return EPIPE.
sigaction(SIGPIPE, &sa, nullptr);
- sigaction(SIGHUP, &sa, nullptr);
- sigaction(SIGINT, &sa, nullptr);
- sigaction(SIGQUIT, &sa, nullptr);
+
+ pipe2(gCtrlPipe, O_CLOEXEC);
+ sa.sa_handler = signalHandler;
sigaction(SIGTERM, &sa, nullptr);
}
@@ -94,8 +90,6 @@ int main(int /*argc*/, char** /*argv*/) {
return -1;
}
- registerSignalHandlers();
-
gStatsService->sayHiToStatsCompanion();
gStatsService->Startup();
@@ -108,6 +102,22 @@ int main(int /*argc*/, char** /*argv*/) {
exit(1);
}
+ // Use self-pipe to notify this thread to gracefully quit
+ // when receiving SIGTERM
+ registerSignalHandlers();
+ std::thread([] {
+ while (true) {
+ char c;
+ int i = read(gCtrlPipe[0], &c, 1);
+ if (i < 0) {
+ if (errno == EINTR) continue;
+ }
+ gSocketListener->stopListener();
+ gStatsService->Terminate();
+ exit(1);
+ }
+ }).detach();
+
// Loop forever -- the reports run on this thread in a handler, and the
// binder calls remain responsive in their pool of one thread.
while (true) {
diff --git a/statsd/src/matchers/matcher_util.cpp b/statsd/src/matchers/matcher_util.cpp
index 27b9fb22..20450ddd 100644
--- a/statsd/src/matchers/matcher_util.cpp
+++ b/statsd/src/matchers/matcher_util.cpp
@@ -16,9 +16,12 @@
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "src/statsd_config.pb.h"
-#include "matchers/AtomMatchingTracker.h"
#include "matchers/matcher_util.h"
+
+#include <fnmatch.h>
+
+#include "matchers/AtomMatchingTracker.h"
+#include "src/statsd_config.pb.h"
#include "stats_util.h"
using std::set;
@@ -97,6 +100,33 @@ bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
return false;
}
+bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+ const string& wildcardPattern) {
+ if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
+ int uid = fieldValue.mValue.int_value;
+ // TODO(b/236886985): replace aid/uid mapping with efficient bidirectional container
+ // AidToUidMapping will never have uids above 10000
+ if (uid < 10000) {
+ for (auto aidIt = UidMap::sAidToUidMapping.begin();
+ aidIt != UidMap::sAidToUidMapping.end(); ++aidIt) {
+ if ((int)aidIt->second == uid) {
+ // Assumes there is only one aid mapping for each uid
+ return fnmatch(wildcardPattern.c_str(), aidIt->first.c_str(), 0) == 0;
+ }
+ }
+ }
+ std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
+ for (const auto& packageName : packageNames) {
+ if (fnmatch(wildcardPattern.c_str(), packageName.c_str(), 0) == 0) {
+ return true;
+ }
+ }
+ } else if (fieldValue.mValue.getType() == STRING) {
+ return fnmatch(wildcardPattern.c_str(), fieldValue.mValue.str_value.c_str(), 0) == 0;
+ }
+ return false;
+}
+
bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end, int depth) {
if (depth > 2) {
@@ -237,13 +267,18 @@ bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
const auto& str_list = matcher.neq_any_string();
for (int i = start; i < end; i++) {
+ bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
- return false;
+ notEqAll = false;
+ break;
}
}
+ if (notEqAll) {
+ return true;
+ }
}
- return true;
+ return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
const auto& str_list = matcher.eq_any_string();
@@ -256,6 +291,41 @@ bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
}
return false;
}
+ case FieldValueMatcher::ValueMatcherCase::kEqWildcardString: {
+ for (int i = start; i < end; i++) {
+ if (tryMatchWildcardString(uidMap, values[i], matcher.eq_wildcard_string())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqAnyWildcardString: {
+ const auto& str_list = matcher.eq_any_wildcard_string();
+ for (int i = start; i < end; i++) {
+ for (const auto& str : str_list.str_value()) {
+ if (tryMatchWildcardString(uidMap, values[i], str)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kNeqAnyWildcardString: {
+ const auto& str_list = matcher.neq_any_wildcard_string();
+ for (int i = start; i < end; i++) {
+ bool notEqAll = true;
+ for (const auto& str : str_list.str_value()) {
+ if (tryMatchWildcardString(uidMap, values[i], str)) {
+ notEqAll = false;
+ break;
+ }
+ }
+ if (notEqAll) {
+ return true;
+ }
+ }
+ return false;
+ }
case FieldValueMatcher::ValueMatcherCase::kEqInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
@@ -270,6 +340,46 @@ bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
}
return false;
}
+ case FieldValueMatcher::ValueMatcherCase::kEqAnyInt: {
+ const auto& int_list = matcher.eq_any_int();
+ for (int i = start; i < end; i++) {
+ for (const int int_value : int_list.int_value()) {
+ if (values[i].mValue.getType() == INT &&
+ (int_value == values[i].mValue.int_value)) {
+ return true;
+ }
+ // eq_any_int covers both int and long.
+ if (values[i].mValue.getType() == LONG &&
+ (int_value == values[i].mValue.long_value)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kNeqAnyInt: {
+ const auto& int_list = matcher.neq_any_int();
+ for (int i = start; i < end; i++) {
+ bool notEqAll = true;
+ for (const int int_value : int_list.int_value()) {
+ if (values[i].mValue.getType() == INT &&
+ (int_value == values[i].mValue.int_value)) {
+ notEqAll = false;
+ break;
+ }
+ // neq_any_int covers both int and long.
+ if (values[i].mValue.getType() == LONG &&
+ (int_value == values[i].mValue.long_value)) {
+ notEqAll = false;
+ break;
+ }
+ }
+ if (notEqAll) {
+ return true;
+ }
+ }
+ return false;
+ }
case FieldValueMatcher::ValueMatcherCase::kLtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
diff --git a/statsd/src/metrics/DurationMetricProducer.cpp b/statsd/src/metrics/DurationMetricProducer.cpp
index 5b7a1c1a..b32a1706 100644
--- a/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/statsd/src/metrics/DurationMetricProducer.cpp
@@ -434,27 +434,28 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit
onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime);
}
-void DurationMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs) {
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
+void DurationMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs,
+ const bool isActive) {
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
if (!mConditionSliced) {
if (ConditionState::kTrue != mCondition) {
return;
}
- if (mIsActive) {
+ if (isActive) {
flushIfNeededLocked(eventTimeNs);
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
+ whatIt.second->onConditionChanged(isActive, eventTimeNs);
}
- } else if (mIsActive) {
+ } else if (isActive) {
flushIfNeededLocked(eventTimeNs);
- onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
- } else { // mConditionSliced == true && !mIsActive
+ onSlicedConditionMayChangeInternalLocked(isActive, eventTimeNs);
+ } else { // mConditionSliced == true && !isActive
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
+ whatIt.second->onConditionChanged(isActive, eventTimeNs);
}
}
}
diff --git a/statsd/src/metrics/DurationMetricProducer.h b/statsd/src/metrics/DurationMetricProducer.h
index 206882b2..ba5fe958 100644
--- a/statsd/src/metrics/DurationMetricProducer.h
+++ b/statsd/src/metrics/DurationMetricProducer.h
@@ -99,7 +99,7 @@ private:
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t eventTimeNs) override;
+ void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
diff --git a/statsd/src/metrics/GaugeMetricProducer.cpp b/statsd/src/metrics/GaugeMetricProducer.cpp
index 4df3e24d..faa284c7 100644
--- a/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -204,7 +204,8 @@ bool GaugeMetricProducer::onConfigUpdatedLocked(
// If this is a config update, we must have just forced a partial bucket. Pull if needed to get
// data for the new bucket.
- if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mCondition == ConditionState::kTrue && mIsActive && mIsPulled &&
+ mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
}
return true;
@@ -356,26 +357,25 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
void GaugeMetricProducer::prepareFirstBucketLocked() {
- if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mCondition == ConditionState::kTrue && mIsActive && mIsPulled &&
+ mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
}
}
+// Only call if mCondition == ConditionState::kTrue && metric is active.
void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
bool triggerPuller = false;
switch(mSamplingType) {
// When the metric wants to do random sampling and there is already one gauge atom for the
// current bucket, do not do it again.
case GaugeMetric::RANDOM_ONE_SAMPLE: {
- triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
- break;
- }
- case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
- triggerPuller = mCondition == ConditionState::kTrue;
+ triggerPuller = mCurrentSlicedBucket->empty();
break;
}
+ case GaugeMetric::CONDITION_CHANGE_TO_TRUE:
case GaugeMetric::FIRST_N_SAMPLES: {
- triggerPuller = mCondition == ConditionState::kTrue;
+ triggerPuller = true;
break;
}
default:
@@ -406,12 +406,15 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
}
}
-void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs) {
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
- if (ConditionState::kTrue != mCondition || !mIsPulled) {
+void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs,
+ const bool isActive) {
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
+
+ if (ConditionState::kTrue != mCondition) {
return;
}
- if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) {
+
+ if (isActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs);
}
}
@@ -426,7 +429,9 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
}
flushIfNeededLocked(eventTimeNs);
- if (mIsPulled && mTriggerAtomId == -1) {
+ if (conditionMet && mIsPulled &&
+ (mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE ||
+ mSamplingType == GaugeMetric::CONDITION_CHANGE_TO_TRUE)) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -443,7 +448,7 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition
flushIfNeededLocked(eventTimeNs);
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
- if (mIsPulled && mTriggerAtomId == -1) {
+ if (overallCondition && mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -528,6 +533,9 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked(
flushIfNeededLocked(eventTimeNs);
if (mTriggerAtomId == event.GetTagId()) {
+ // Both Active state and Condition are true here.
+ // Active state being true is checked in onMatchedLogEventLocked.
+ // Condition being true is checked at the start of this method.
pullAndMatchEventsLocked(eventTimeNs);
return;
}
@@ -638,7 +646,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId,
slice.first.toString().c_str());
}
- } else {
+ } else if (mIsActive) {
mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
if (!maxDropEventsReached()) {
diff --git a/statsd/src/metrics/GaugeMetricProducer.h b/statsd/src/metrics/GaugeMetricProducer.h
index 7e1b0c27..af995359 100644
--- a/statsd/src/metrics/GaugeMetricProducer.h
+++ b/statsd/src/metrics/GaugeMetricProducer.h
@@ -82,7 +82,7 @@ public:
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void notifyAppUpgradeInternalLocked(const int64_t eventTimeNs) override {
flushLocked(eventTimeNs);
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
}
};
@@ -92,7 +92,7 @@ public:
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
}
};
@@ -120,7 +120,7 @@ private:
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t eventTimeNs) override;
+ void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
@@ -140,6 +140,7 @@ private:
void prepareFirstBucketLocked() override;
+ // Only call if mCondition == ConditionState::kTrue && metric is active.
void pullAndMatchEventsLocked(const int64_t timestampNs);
bool onConfigUpdatedLocked(
diff --git a/statsd/src/metrics/MetricProducer.cpp b/statsd/src/metrics/MetricProducer.cpp
index 17aa3128..75c24762 100644
--- a/statsd/src/metrics/MetricProducer.cpp
+++ b/statsd/src/metrics/MetricProducer.cpp
@@ -194,9 +194,13 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
if (!mIsActive) {
return;
}
- mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
- if (!mIsActive) {
- onActiveStateChangedLocked(elapsedTimestampNs);
+ const bool isActive = evaluateActiveStateLocked(elapsedTimestampNs);
+ if (!isActive) { // Metric went from active to not active.
+ onActiveStateChangedLocked(elapsedTimestampNs, false);
+
+ // Set mIsActive to false after onActiveStateChangedLocked to ensure any pulls that occur
+ // through onActiveStateChangedLocked are processed.
+ mIsActive = false;
}
}
@@ -215,10 +219,12 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT
}
activation->start_ns = elapsedTimestampNs;
activation->state = ActivationState::kActive;
- bool oldActiveState = mIsActive;
- mIsActive = true;
- if (!oldActiveState) { // Metric went from not active to active.
- onActiveStateChangedLocked(elapsedTimestampNs);
+ if (!mIsActive) { // Metric was previously inactive and now is active.
+ // Set mIsActive to true before onActiveStateChangedLocked to ensure any pulls that occur
+ // through onActiveStateChangedLocked are processed.
+ mIsActive = true;
+
+ onActiveStateChangedLocked(elapsedTimestampNs, true);
}
}
diff --git a/statsd/src/metrics/MetricProducer.h b/statsd/src/metrics/MetricProducer.h
index 95f8dd43..ab749512 100644
--- a/statsd/src/metrics/MetricProducer.h
+++ b/statsd/src/metrics/MetricProducer.h
@@ -394,7 +394,7 @@ protected:
/**
* Flushes all the data including the current partial bucket.
*/
- virtual void flushLocked(const int64_t& eventTimeNs) {
+ void flushLocked(const int64_t& eventTimeNs) {
flushIfNeededLocked(eventTimeNs);
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
@@ -445,8 +445,8 @@ protected:
bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
- virtual void onActiveStateChangedLocked(const int64_t eventTimeNs) {
- if (!mIsActive) {
+ virtual void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) {
+ if (!isActive) {
flushLocked(eventTimeNs);
}
}
diff --git a/statsd/src/metrics/MetricsManager.h b/statsd/src/metrics/MetricsManager.h
index 1eccf8d8..c855a050 100644
--- a/statsd/src/metrics/MetricsManager.h
+++ b/statsd/src/metrics/MetricsManager.h
@@ -347,10 +347,9 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
- FRIEND_TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagFalse);
- FRIEND_TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagTrue);
FRIEND_TEST(MetricsManagerTest, TestLogSources);
FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
+ FRIEND_TEST(MetricsManagerTest_SPlus, TestAtomMatcherOptimizationEnabledFlag);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
diff --git a/statsd/src/metrics/NumericValueMetricProducer.cpp b/statsd/src/metrics/NumericValueMetricProducer.cpp
index 703b07b3..4f8ae235 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.cpp
+++ b/statsd/src/metrics/NumericValueMetricProducer.cpp
@@ -128,10 +128,11 @@ void NumericValueMetricProducer::writePastBucketAggregateToProto(
protoOutput->end(valueToken);
}
-void NumericValueMetricProducer::onActiveStateChangedInternalLocked(const int64_t eventTimeNs) {
+void NumericValueMetricProducer::onActiveStateChangedInternalLocked(const int64_t eventTimeNs,
+ const bool isActive) {
// When active state changes from true to false for pulled metric, clear diff base but don't
// reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && !mIsActive) {
+ if (mUseDiff && !isActive) {
resetBase();
}
}
diff --git a/statsd/src/metrics/NumericValueMetricProducer.h b/statsd/src/metrics/NumericValueMetricProducer.h
index 8eca3f89..12468158 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.h
+++ b/statsd/src/metrics/NumericValueMetricProducer.h
@@ -66,7 +66,8 @@ private:
return config.value_metric(configIndex).links();
}
- void onActiveStateChangedInternalLocked(const int64_t eventTimeNs) override;
+ void onActiveStateChangedInternalLocked(const int64_t eventTimeNs,
+ const bool isActive) override;
// Only called when mIsActive and the event is NOT too late.
void onConditionChangedInternalLocked(const ConditionState oldCondition,
diff --git a/statsd/src/metrics/ValueMetricProducer.cpp b/statsd/src/metrics/ValueMetricProducer.cpp
index 50958ca5..05002842 100644
--- a/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/statsd/src/metrics/ValueMetricProducer.cpp
@@ -158,9 +158,7 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onStatsdInitCompleted(
const int64_t& eventTimeNs) {
lock_guard<mutex> lock(mMutex);
- // TODO(b/188837487): Add mIsActive check
-
- if (isPulled() && mCondition == ConditionState::kTrue) {
+ if (isPulled() && mCondition == ConditionState::kTrue && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
@@ -169,8 +167,7 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onStatsdInitCompleted(
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::notifyAppUpgradeInternalLocked(
const int64_t eventTimeNs) {
- // TODO(b/188837487): Add mIsActive check
- if (isPulled() && mCondition == ConditionState::kTrue) {
+ if (isPulled() && mCondition == ConditionState::kTrue && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
@@ -296,14 +293,12 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onDumpReportLocked(
const DumpLatency dumpLatency, set<string>* strSet, ProtoOutputStream* protoOutput) {
VLOG("metric %lld dump report now...", (long long)mMetricId);
- // TODO(b/188837487): Add mIsActive check
-
if (includeCurrentPartialBucket) {
// For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
// current bucket will have incomplete data and the next will have the wrong snapshot to do
// a diff against. If the condition is false, we are fine since the base data is reset and
// we are not tracking anything.
- if (isPulled() && mCondition == ConditionState::kTrue) {
+ if (isPulled() && mCondition == ConditionState::kTrue && mIsActive) {
switch (dumpLatency) {
case FAST:
invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED);
@@ -452,19 +447,24 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::invalidateCurrentBucket(
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::skipCurrentBucket(
const int64_t dropTimeNs, const BucketDropReason reason) {
+ if (!mIsActive) {
+ // Don't keep track of skipped buckets if metric is not active.
+ return;
+ }
+
if (!maxDropEventsReached()) {
mCurrentSkippedBucket.dropEvents.push_back(buildDropEvent(dropTimeNs, reason));
}
mCurrentBucketIsSkipped = true;
}
-// Handle active state change. Active state change is treated like a condition change:
+// Handle active state change. Active state change is *mostly* treated like a condition change:
// - drop bucket if active state change event arrives too late
// - if condition is true, pull data on active state changes
// - ConditionTimer tracks changes based on AND of condition and active state.
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::onActiveStateChangedLocked(
- const int64_t eventTimeNs) {
+ const int64_t eventTimeNs, const bool isActive) {
const bool eventLate = isEventLateLocked(eventTimeNs);
if (eventLate) {
// Drop bucket because event arrived too late, ie. we are missing data for this bucket.
@@ -472,10 +472,9 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onActiveStateChangedLocked
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
}
- // Call parent method once we've verified the validity of current bucket.
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
-
if (ConditionState::kTrue != mCondition) {
+ // Call parent method before early return.
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
return;
}
@@ -485,15 +484,17 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onActiveStateChangedLocked
pullAndMatchEventsLocked(eventTimeNs);
}
- onActiveStateChangedInternalLocked(eventTimeNs);
+ onActiveStateChangedInternalLocked(eventTimeNs, isActive);
}
- flushIfNeededLocked(eventTimeNs);
+ // Once any pulls are processed, call through to parent method which might flush the current
+ // bucket.
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
// Let condition timer know of new active state.
- mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+ mConditionTimer.onConditionChanged(isActive, eventTimeNs);
- updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
+ updateCurrentSlicedBucketConditionTimers(isActive, eventTimeNs);
}
template <typename AggregatedValue, typename DimExtras>
diff --git a/statsd/src/metrics/ValueMetricProducer.h b/statsd/src/metrics/ValueMetricProducer.h
index 956580ad..80d0bb67 100644
--- a/statsd/src/metrics/ValueMetricProducer.h
+++ b/statsd/src/metrics/ValueMetricProducer.h
@@ -167,9 +167,10 @@ protected:
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// ValueMetricProducer internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t eventTimeNs) override;
+ void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
- virtual void onActiveStateChangedInternalLocked(const int64_t eventTimeNs) {
+ virtual void onActiveStateChangedInternalLocked(const int64_t eventTimeNs,
+ const bool isActive) {
}
// ValueMetricProducer internal interface to handle condition change.
diff --git a/statsd/src/shell/ShellSubscriber.cpp b/statsd/src/shell/ShellSubscriber.cpp
index 35e64e47..eac65967 100644
--- a/statsd/src/shell/ShellSubscriber.cpp
+++ b/statsd/src/shell/ShellSubscriber.cpp
@@ -31,13 +31,13 @@ namespace statsd {
const static int FIELD_ID_ATOM = 1;
-void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
+bool ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
int myToken = claimToken();
VLOG("ShellSubscriber: new subscription %d has come in", myToken);
mSubscriptionShouldEnd.notify_one();
shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out);
- if (!readConfig(mySubscriptionInfo)) return;
+ if (!readConfig(mySubscriptionInfo)) return false;
{
std::unique_lock<std::mutex> lock(mMutex);
@@ -50,6 +50,7 @@ void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
}
}
+ return true;
}
void ShellSubscriber::spawnHelperThread(int myToken) {
@@ -87,6 +88,13 @@ bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo)
return false;
}
+ // Check bufferSize
+ if (bufferSize > (kMaxSizeKb * 1024)) {
+ ALOGE("Received config (%zu bytes) is larger than the max size (%zu bytes)", bufferSize,
+ (kMaxSizeKb * 1024));
+ return false;
+ }
+
// Read the config.
vector<uint8_t> buffer(bufferSize);
if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) {
diff --git a/statsd/src/shell/ShellSubscriber.h b/statsd/src/shell/ShellSubscriber.h
index 826ad31f..c6d11a3c 100644
--- a/statsd/src/shell/ShellSubscriber.h
+++ b/statsd/src/shell/ShellSubscriber.h
@@ -62,10 +62,14 @@ public:
ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
: mUidMap(uidMap), mPullerMgr(pullerMgr){};
- void startNewSubscription(int inFd, int outFd, int timeoutSec);
+ bool startNewSubscription(int inFd, int outFd, int timeoutSec);
void onLogEvent(const LogEvent& event);
+ inline size_t getMaxSizeKb() const {
+ return kMaxSizeKb;
+ }
+
private:
struct PullInfo {
PullInfo(const SimpleAtomMatcher& matcher, int64_t interval,
@@ -139,6 +143,9 @@ private:
// when next to send a heartbeat.
int64_t mLastWriteMs = 0;
const int64_t kMsBetweenHeartbeats = 1000;
+
+ // Cap the buffer size of configs to guard against bad allocations
+ static constexpr size_t kMaxSizeKb = 50;
};
} // namespace statsd
diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto
index c64a9be4..89fdb9c7 100644
--- a/statsd/src/stats_log.proto
+++ b/statsd/src/stats_log.proto
@@ -430,7 +430,7 @@ message ConfigMetricsReportList {
repeated ConfigMetricsReport reports = 2;
- reserved 10;
+ reserved 10 to 13, 101;
}
message StatsdStatsReport {
diff --git a/statsd/src/statsd_config.proto b/statsd/src/statsd_config.proto
index be733b4b..b75b6702 100644
--- a/statsd/src/statsd_config.proto
+++ b/statsd/src/statsd_config.proto
@@ -78,6 +78,12 @@ message FieldValueMatcher {
StringListMatcher eq_any_string = 13;
StringListMatcher neq_any_string = 14;
+ IntListMatcher eq_any_int = 15;
+ IntListMatcher neq_any_int = 16;
+
+ string eq_wildcard_string = 17;
+ StringListMatcher eq_any_wildcard_string = 18;
+ StringListMatcher neq_any_wildcard_string = 19;
}
}
@@ -89,6 +95,10 @@ message StringListMatcher {
repeated string str_value = 1;
}
+message IntListMatcher {
+ repeated int64 int_value = 1;
+}
+
enum LogicalOperation {
LOGICAL_OPERATION_UNSPECIFIED = 0;
AND = 1;
diff --git a/statsd/src/storage/StorageManager.cpp b/statsd/src/storage/StorageManager.cpp
index b3e8d8a1..686c2949 100644
--- a/statsd/src/storage/StorageManager.cpp
+++ b/statsd/src/storage/StorageManager.cpp
@@ -40,11 +40,6 @@ using std::map;
*/
#define STATS_DATA_DIR "/data/misc/stats-data"
#define STATS_SERVICE_DIR "/data/misc/stats-service"
-#define TRAIN_INFO_DIR "/data/misc/train-info"
-#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
-
-// Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -310,6 +305,32 @@ bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTr
return false;
}
+ // On devices that are upgrading from 32 to 64 bit, reading size_t bytes will result in reading
+ // the wrong amount of data. We try to detect that issue here and read the file correctly.
+ // In the normal case on 64-bit devices, the trainNameSize's upper 4 bytes should be 0, but if
+ // a 32-bit device wrote the file, these would be the first 4 bytes of the train name.
+ bool is32To64BitUpgrade = false;
+ if ((sizeof(size_t) == sizeof(int64_t)) && (trainNameSize & 0xFFFFFFFF00000000) != 0) {
+ is32To64BitUpgrade = true;
+ // Reset the file offset to the magic + version code, and reread from the train name size.
+ off_t seekResult = lseek(fd, sizeof(magic) + sizeof(trainInfo.trainVersionCode), SEEK_SET);
+
+ if (seekResult != sizeof(magic) + sizeof(trainInfo.trainVersionCode)) {
+ VLOG("Failed to reset file offset when reading 32 to 64 bit train info");
+ close(fd);
+ return false;
+ }
+
+ int32_t trainNameSize32Bit;
+ result = read(fd, &trainNameSize32Bit, sizeof(int32_t));
+ if (result != sizeof(int32_t)) {
+ VLOG("Failed to read train name size 32 bit from train info file");
+ close(fd);
+ return false;
+ }
+ trainNameSize = trainNameSize32Bit;
+ }
+
// Read trainName
trainInfo.trainName.resize(trainNameSize);
result = read(fd, trainInfo.trainName.data(), trainNameSize);
@@ -330,11 +351,22 @@ bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTr
// Read experiment ids count.
size_t experimentIdsCount;
- result = read(fd, &experimentIdsCount, sizeof(size_t));
- if (result != sizeof(size_t)) {
- VLOG("Failed to read train experiment id count from train info file");
- close(fd);
- return false;
+ int32_t experimentIdsCount32Bit;
+ if (is32To64BitUpgrade) {
+ result = read(fd, &experimentIdsCount32Bit, sizeof(int32_t));
+ if (result != sizeof(int32_t)) {
+ VLOG("Failed to read train experiment id count 32 bit from train info file");
+ close(fd);
+ return false;
+ }
+ experimentIdsCount = experimentIdsCount32Bit;
+ } else {
+ result = read(fd, &experimentIdsCount, sizeof(size_t));
+ if (result != sizeof(size_t)) {
+ VLOG("Failed to read train experiment id count from train info file");
+ close(fd);
+ return false;
+ }
}
// Read experimentIds
diff --git a/statsd/src/storage/StorageManager.h b/statsd/src/storage/StorageManager.h
index d59046df..b0840aea 100644
--- a/statsd/src/storage/StorageManager.h
+++ b/statsd/src/storage/StorageManager.h
@@ -29,6 +29,16 @@ namespace statsd {
using android::util::ProtoOutputStream;
+/**
+ * NOTE: these directories are protected by SELinux, any changes here must also update
+ * the SELinux policies.
+ */
+#define TRAIN_INFO_DIR "/data/misc/train-info"
+#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
+
+// Magic word at the start of the train info file, change this if changing the file format
+const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
+
class StorageManager : public virtual RefBase {
public:
struct FileInfo {
diff --git a/statsd/tests/LogEntryMatcher_test.cpp b/statsd/tests/LogEntryMatcher_test.cpp
index 8a4fb194..d0a063f0 100644
--- a/statsd/tests/LogEntryMatcher_test.cpp
+++ b/statsd/tests/LogEntryMatcher_test.cpp
@@ -948,7 +948,7 @@ TEST(AtomMatcherTest, TestNeqAnyStringMatcher_RepeatedStringField) {
fieldValueMatcher->set_position(Position::LAST);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
neqStringList->add_str_value("str3");
fieldValueMatcher->set_position(Position::FIRST);
@@ -956,7 +956,7 @@ TEST(AtomMatcherTest, TestNeqAnyStringMatcher_RepeatedStringField) {
fieldValueMatcher->set_position(Position::LAST);
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
neqStringList->add_str_value("str1");
fieldValueMatcher->set_position(Position::FIRST);
@@ -1204,6 +1204,303 @@ TEST(AtomMatcherTest, TestNorMatcher) {
matcherResults.push_back(MatchingState::kMatched);
EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
+
+TEST(AtomMatcherTest, TestUidFieldMatcherWithWildcardString) {
+ sp<UidMap> uidMap = new UidMap();
+ uidMap->updateMap(
+ 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+ android::String16("v1"), android::String16("v2")},
+ {android::String16("package0"), android::String16("pkg1"), android::String16("pkg1"),
+ android::String16("package2"), android::String16("package3")} /* package name list */,
+ {android::String16(""), android::String16(""), android::String16(""),
+ android::String16(""), android::String16("")},
+ /* certificateHash */ {{}, {}, {}, {}, {}});
+
+ // Set up matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ simpleMatcher->add_field_value_matcher()->set_field(1);
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string("pkg*");
+
+ // Event without is_uid annotation.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event1, TAG_ID, 0, 1111);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+ // Event where mapping from uid to package name occurs.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID, 1111, ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+ // Event where uid maps to package names that don't fit wildcard pattern.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event3, TAG_ID, 3333, ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+
+ // Update matcher to match one AID
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string(
+ "AID_SYSTEM"); // uid 1000
+
+ // Event where mapping from uid to aid doesn't fit wildcard pattern.
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event4, TAG_ID, 1005, ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+
+ // Event where mapping from uid to aid does fit wildcard pattern.
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event5, TAG_ID, 1000, ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event5));
+
+ // Update matcher to match multiple AIDs
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string("AID_SDCARD_*");
+
+ // Event where mapping from uid to aid doesn't fit wildcard pattern.
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event6, TAG_ID, 1036, ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6));
+
+ // Event where mapping from uid to aid does fit wildcard pattern.
+ LogEvent event7(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event7, TAG_ID, 1034, ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+
+ LogEvent event8(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event8, TAG_ID, 1035, ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+}
+
+TEST(AtomMatcherTest, TestWildcardStringMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+ // Set up the matcher
+ AtomMatcher matcher;
+ SimpleAtomMatcher* simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ FieldValueMatcher* fieldValueMatcher = simpleMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(FIELD_ID_1);
+ // Matches any string that begins with "test.string:test_" and ends with number between 0 and 9
+ // inclusive
+ fieldValueMatcher->set_eq_wildcard_string("test.string:test_[0-9]");
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event1, TAG_ID, 0, "test.string:test_0");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event2, TAG_ID, 0, "test.string:test_19");
+ EXPECT_FALSE(
+ matchesSimple(uidMap, *simpleMatcher, event2)); // extra character at end of string
+
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event3, TAG_ID, 0, "extra.test.string:test_1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
+ event3)); // extra characters at beginning of string
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event4, TAG_ID, 0, "test.string:test_");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
+ event4)); // missing character from 0-9 at end of string
+
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event5, TAG_ID, 0, "est.string:test_1");
+ EXPECT_FALSE(
+ matchesSimple(uidMap, *simpleMatcher, event5)); // missing 't' at beginning of string
+
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event6, TAG_ID, 0, "test.string:test_1extra");
+ EXPECT_FALSE(
+ matchesSimple(uidMap, *simpleMatcher, event6)); // extra characters at end of string
+
+ // Matches any string that contains "test.string:test_" + any extra characters before or after
+ fieldValueMatcher->set_eq_wildcard_string("*test.string:test_*");
+
+ LogEvent event7(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event7, TAG_ID, 0, "test.string:test_");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+
+ LogEvent event8(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event8, TAG_ID, 0, "extra.test.string:test_");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+
+ LogEvent event9(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event9, TAG_ID, 0, "test.string:test_extra");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event9));
+
+ LogEvent event10(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event10, TAG_ID, 0, "est.string:test_");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event10));
+
+ LogEvent event11(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event11, TAG_ID, 0, "test.string:test");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event11));
+}
+
+TEST(AtomMatcherTest, TestEqAnyWildcardStringMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher
+ AtomMatcher matcher;
+ SimpleAtomMatcher* simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+
+ FieldValueMatcher* fieldValueMatcher = simpleMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(FIELD_ID_1);
+ StringListMatcher* eqWildcardStrList = fieldValueMatcher->mutable_eq_any_wildcard_string();
+ eqWildcardStrList->add_str_value("first_string_*");
+ eqWildcardStrList->add_str_value("second_string_*");
+
+ // First wildcard pattern matched.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event1, TAG_ID, 0, "first_string_1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+ // Second wildcard pattern matched.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event2, TAG_ID, 0, "second_string_1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+ // No wildcard patterns matched.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event3, TAG_ID, 0, "third_string_1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+}
+
+TEST(AtomMatcherTest, TestNeqAnyWildcardStringMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location_1", "location_2", "location"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+ // Set up the matcher. Match first tag.
+ AtomMatcher matcher;
+ SimpleAtomMatcher* simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ FieldValueMatcher* attributionMatcher = simpleMatcher->add_field_value_matcher();
+ attributionMatcher->set_field(FIELD_ID_1);
+ attributionMatcher->set_position(Position::FIRST);
+ FieldValueMatcher* attributionTagMatcher =
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagMatcher->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringListMatcher* neqWildcardStrList =
+ attributionTagMatcher->mutable_neq_any_wildcard_string();
+
+ // First tag is not matched. neq string list {"tag"}
+ neqWildcardStrList->add_str_value("tag");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // First tag is matched. neq string list {"tag", "location_*"}
+ neqWildcardStrList->add_str_value("location_*");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match last tag.
+ attributionMatcher->set_position(Position::LAST);
+
+ // Last tag is not matched. neq string list {"tag", "location_*"}
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Last tag is matched. neq string list {"tag", "location_*", "location*"}
+ neqWildcardStrList->add_str_value("location*");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match any tag.
+ attributionMatcher->set_position(Position::ANY);
+
+ // All tags are matched. neq string list {"tag", "location_*", "location*"}
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Set up another log event.
+ std::vector<string> attributionTags2 = {"location_1", "location", "string"};
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event2, TAG_ID, 0, attributionUids, attributionTags2, "some value");
+
+ // Tag "string" is not matched. neq string list {"tag", "location_*", "location*"}
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+}
+
+TEST(AtomMatcherTest, TestEqAnyIntMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher
+ AtomMatcher matcher;
+ SimpleAtomMatcher* simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+
+ FieldValueMatcher* fieldValueMatcher = simpleMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(FIELD_ID_1);
+ IntListMatcher* eqIntList = fieldValueMatcher->mutable_eq_any_int();
+ eqIntList->add_int_value(3);
+ eqIntList->add_int_value(5);
+
+ // First int matched.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event1, TAG_ID, 0, 3);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+ // Second int matched.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event2, TAG_ID, 0, 5);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+ // No ints matched.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event3, TAG_ID, 0, 4);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+}
+
+TEST(AtomMatcherTest, TestNeqAnyIntMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+ // Set up the matcher. Match first uid.
+ AtomMatcher matcher;
+ SimpleAtomMatcher* simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ FieldValueMatcher* attributionMatcher = simpleMatcher->add_field_value_matcher();
+ attributionMatcher->set_field(FIELD_ID_1);
+ attributionMatcher->set_position(Position::FIRST);
+ FieldValueMatcher* attributionUidMatcher =
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher();
+ attributionUidMatcher->set_field(ATTRIBUTION_UID_FIELD_ID);
+ IntListMatcher* neqIntList = attributionUidMatcher->mutable_neq_any_int();
+
+ // First uid is not matched. neq int list {4444}
+ neqIntList->add_int_value(4444);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // First uid is matched. neq int list {4444, 1111}
+ neqIntList->add_int_value(1111);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match last uid.
+ attributionMatcher->set_position(Position::LAST);
+
+ // Last uid is not matched. neq int list {4444, 1111}
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Last uid is matched. neq int list {4444, 1111, 3333}
+ neqIntList->add_int_value(3333);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match any uid.
+ attributionMatcher->set_position(Position::ANY);
+
+ // Uid 2222 is not matched. neq int list {4444, 1111, 3333}
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // All uids are matched. neq int list {4444, 1111, 3333, 2222}
+ neqIntList->add_int_value(2222);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/MetricsManager_test.cpp b/statsd/tests/MetricsManager_test.cpp
index 5cab8006..9650f2d6 100644
--- a/statsd/tests/MetricsManager_test.cpp
+++ b/statsd/tests/MetricsManager_test.cpp
@@ -35,6 +35,7 @@
using namespace testing;
using android::sp;
+using android::modules::sdklevel::IsAtLeastS;
using android::os::statsd::Predicate;
using std::map;
using std::set;
@@ -264,24 +265,55 @@ TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) {
UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}})));
}
-TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagFalse) {
- FlagProvider::getInstance().overrideFlag(OPTIMIZATION_ATOM_MATCHER_MAP_FLAG, FLAG_FALSE,
- /*isBootFlag=*/true);
+struct MetricsManagerServerFlagParam {
+ string flagValue;
+ string label;
+};
+
+class MetricsManagerTest_SPlus
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<MetricsManagerServerFlagParam> {
+protected:
+ void SetUp() override {
+ if (shouldSkipTest()) {
+ GTEST_SKIP() << skipReason();
+ }
+
+ originalFlagValue = FlagProvider::getInstance().getFlagString(
+ OPTIMIZATION_ATOM_MATCHER_MAP_FLAG, FLAG_EMPTY);
+ }
- sp<UidMap> uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
+ bool shouldSkipTest() const {
+ return !IsAtLeastS();
+ }
- StatsdConfig config = buildGoodConfig();
- MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
- pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+ string skipReason() const {
+ return "Skipping MetricsManagerTest_SPlus because device is not S+";
+ }
- EXPECT_FALSE(metricsManager.mAtomMatcherOptimizationEnabled);
-}
+ void TearDown() override {
+ if (originalFlagValue) {
+ writeFlag(OPTIMIZATION_ATOM_MATCHER_MAP_FLAG, originalFlagValue.value());
+ }
+ }
-TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagTrue) {
- FlagProvider::getInstance().overrideFlag(OPTIMIZATION_ATOM_MATCHER_MAP_FLAG, FLAG_TRUE,
+ std::optional<string> originalFlagValue;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ MetricsManagerTest_SPlus, MetricsManagerTest_SPlus,
+ testing::ValuesIn<MetricsManagerServerFlagParam>({
+ // Server flag values
+ {FLAG_TRUE, "ServerFlagTrue"},
+ {FLAG_FALSE, "ServerFlagFalse"},
+ }),
+ [](const testing::TestParamInfo<MetricsManagerTest_SPlus::ParamType>& info) {
+ return info.param.label;
+ });
+
+TEST_P(MetricsManagerTest_SPlus, TestAtomMatcherOptimizationEnabledFlag) {
+ FlagProvider::getInstance().overrideFlag(OPTIMIZATION_ATOM_MATCHER_MAP_FLAG,
+ GetParam().flagValue,
/*isBootFlag=*/true);
sp<UidMap> uidMap;
@@ -293,7 +325,11 @@ TEST(MetricsManagerTest, TestAtomMatcherOptimizationEnabledFlagTrue) {
MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
- EXPECT_TRUE(metricsManager.mAtomMatcherOptimizationEnabled);
+ if (GetParam().flagValue == FLAG_TRUE) {
+ EXPECT_TRUE(metricsManager.mAtomMatcherOptimizationEnabled);
+ } else {
+ EXPECT_FALSE(metricsManager.mAtomMatcherOptimizationEnabled);
+ }
}
TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
diff --git a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 9f3ace4a..984b72e6 100644
--- a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -60,6 +60,8 @@ StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
gaugeMetric->set_bucket(FIVE_MINUTES);
gaugeMetric->set_max_pull_delay_sec(INT_MAX);
config.set_hash_strings_in_metric_report(false);
+ gaugeMetric->set_split_bucket_for_app_upgrade(true);
+ gaugeMetric->set_min_bucket_size_nanos(1000);
return config;
}
@@ -430,6 +432,8 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
event_activation->set_ttl_seconds(ttlNs / 1000000000);
+ StatsdStats::getInstance().reset();
+
ConfigKey cfgKey;
auto processor =
CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
@@ -438,10 +442,10 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
+ const int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
+ EXPECT_EQ(startBucketNum, 2);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// When creating the config, the gauge metric producer should register the alarm at the
@@ -453,13 +457,42 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+ // Check no pull occurred on metric initialization when it's not active.
+ const int64_t metricInitTimeNs = configAddedTimeNs + 1; // 10 mins + 1 ns.
+ processor->onStatsdInitCompleted(metricInitTimeNs);
+ StatsdStatsReport_PulledAtomStats pulledAtomStats = getPulledAtomStats();
+ EXPECT_EQ(pulledAtomStats.atom_id(), ATOM_TAG);
+ EXPECT_EQ(pulledAtomStats.total_pull(), 0);
+
+ // Check no pull occurred on app upgrade when metric is not active.
+ const int64_t appUpgradeTimeNs = metricInitTimeNs + 1; // 10 mins + 2 ns.
+ processor->notifyAppUpgrade(appUpgradeTimeNs, "appName", 1000 /* uid */, 2 /* version */);
+ pulledAtomStats = getPulledAtomStats();
+ EXPECT_EQ(pulledAtomStats.atom_id(), ATOM_TAG);
+ EXPECT_EQ(pulledAtomStats.total_pull(), 0);
+
+ // Check skipped bucket is not added when metric is not active.
+ int64_t dumpReportTimeNs = appUpgradeTimeNs + 1; // 10 mins + 3 ns.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &buffer);
+ ConfigMetricsReportList reports;
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics =
+ reports.reports(0).metrics(0).gauge_metrics();
+ EXPECT_EQ(gaugeMetrics.skipped_size(), 0);
+
// Pulling alarm arrives on time and reset the sequential pulling alarm.
// Event should not be kept.
processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- // Activate the metric. A pull occurs upon activation.
+ // Activate the metric. A pull occurs upon activation. The event is kept. 1 total
+ // 15 mins + 2 ms
const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
@@ -474,19 +507,27 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
// Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
+ // A pull should not occur here. 3 total.
+ // 25 mins + 2 ms + 1 ns.
+ const int64_t deactivationNs = activationNs + ttlNs + 1;
+ auto deactivationEvent = CreateScreenBrightnessChangedEvent(deactivationNs, 50);
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// Event should not be kept. 3 total.
+ // 30 mins + 3 ns.
processor->informPullAlarmFired(nextPullTimeNs + 3);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+ // Event should not be kept. 3 total.
+ // 35 mins + 2 ns.
processor->informPullAlarmFired(nextPullTimeNs + 2);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+ buffer.clear();
+ // 40 mins + 10 ns.
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 6 * bucketSizeNs + 10,
+ false /* include_current_partial_bucket */, true /* erase_data */,
ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -496,7 +537,7 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
backfillAggregatedAtoms(&reports);
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ gaugeMetrics = StatsLogReport::GaugeMetricDataWrapper();
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
ASSERT_GT((int)gaugeMetrics.data_size(), 0);
@@ -535,10 +576,21 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) {
ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
- bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(MillisToNano(NanoToMillis(deactivationNs)), bucketInfo.end_bucket_elapsed_nanos());
EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
+
+ // Check skipped bucket is not added after deactivation.
+ dumpReportTimeNs = configAddedTimeNs + 8 * bucketSizeNs + 10;
+ buffer.clear();
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ gaugeMetrics = reports.reports(0).metrics(0).gauge_metrics();
+ EXPECT_EQ(gaugeMetrics.skipped_size(), 0);
}
TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition) {
diff --git a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 1ea757a8..385a4730 100644
--- a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -60,6 +60,8 @@ StatsdConfig CreateStatsdConfig(bool useCondition = true) {
valueMetric->set_use_absolute_value_on_reset(true);
valueMetric->set_skip_zero_diff_output(false);
valueMetric->set_max_pull_delay_sec(INT_MAX);
+ valueMetric->set_split_bucket_for_app_upgrade(true);
+ valueMetric->set_min_bucket_size_nanos(1000);
return config;
}
@@ -422,6 +424,8 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
event_activation->set_ttl_seconds(ttlNs / 1000000000);
+ StatsdStats::getInstance().reset();
+
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
SharedRefBase::make<FakeSubsystemSleepCallback>(),
@@ -430,10 +434,10 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
+ const int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
+ EXPECT_EQ(startBucketNum, 2);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// When creating the config, the value metric producer should register the alarm at the
@@ -445,38 +449,109 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ // Initialize metric.
+ const int64_t metricInitTimeNs = configAddedTimeNs + 1; // 10 mins + 1 ns.
+ processor->onStatsdInitCompleted(metricInitTimeNs);
+
+ // Check no pull occurred since metric not active.
+ StatsdStatsReport_PulledAtomStats pulledAtomStats = getPulledAtomStats();
+ EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE);
+ EXPECT_EQ(pulledAtomStats.total_pull(), 0);
+
+ // Check skip bucket is not added when metric is not active.
+ int64_t dumpReportTimeNs = metricInitTimeNs + 1; // 10 mins + 2 ns.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &buffer);
+ ConfigMetricsReportList reports;
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics =
+ reports.reports(0).metrics(0).value_metrics();
+ EXPECT_EQ(valueMetrics.skipped_size(), 0);
+
+ // App upgrade.
+ const int64_t appUpgradeTimeNs = dumpReportTimeNs + 1; // 10 mins + 3 ns.
+ processor->notifyAppUpgrade(appUpgradeTimeNs, "appName", 1000 /* uid */, 2 /* version */);
+
+ // Check no pull occurred since metric not active.
+ pulledAtomStats = getPulledAtomStats();
+ EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE);
+ EXPECT_EQ(pulledAtomStats.total_pull(), 0);
+
+ // Check skip bucket is not added when metric is not active.
+ dumpReportTimeNs = appUpgradeTimeNs + 1; // 10 mins + 4 ns.
+ buffer.clear();
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ valueMetrics = reports.reports(0).metrics(0).value_metrics();
+ EXPECT_EQ(valueMetrics.skipped_size(), 0);
+
+ // Dump report with a pull. The pull should not happen because metric is inactive.
+ dumpReportTimeNs = dumpReportTimeNs + 1; // 10 mins + 6 ns.
+ buffer.clear();
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &buffer);
+ pulledAtomStats = getPulledAtomStats();
+ EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE);
+ EXPECT_EQ(pulledAtomStats.total_pull(), 0);
+
+ // Check skipped bucket is not added from the dump operation when metric is not active.
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ valueMetrics = reports.reports(0).metrics(0).value_metrics();
+ EXPECT_EQ(valueMetrics.skipped_size(), 0);
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm. This bucket is skipped.
processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- // Activate the metric. A pull occurs here
+ // Activate the metric. A pull occurs here that sets the base.
+ // 15 mins + 2 ms
const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+ // This bucket should be kept. 1 total
processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
+ // 25 mins + 2 ns.
+ // This bucket should be kept. 2 total
processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
// Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
+ // A pull occurs here and a partial bucket is created. The bucket ending here is kept. 3 total.
+ // 25 mins + 2 ms + 1 ns.
+ const int64_t deactivationNs = activationNs + ttlNs + 1;
+ auto deactivationEvent = CreateScreenBrightnessChangedEvent(deactivationNs, 50);
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+ // 30 mins + 3 ns. This bucket is skipped.
processor->informPullAlarmFired(expectedPullTimeNs + 3);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs);
+ // 35 mins + 4 ns. This bucket is skipped
processor->informPullAlarmFired(expectedPullTimeNs + 4);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
+ dumpReportTimeNs = configAddedTimeNs + 6 * bucketSizeNs + 10;
+ buffer.clear();
+ // 40 mins + 10 ns.
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, false /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
@@ -484,7 +559,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
backfillStartEndTimestamp(&reports);
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ valueMetrics = StatsLogReport::ValueMetricDataWrapper();
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
ASSERT_GT((int)valueMetrics.data_size(), 0);
@@ -494,8 +569,8 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- // We have 2 full buckets, the two surrounding the activation are dropped.
- ASSERT_EQ(2, data.bucket_info_size());
+ // We have 3 full buckets, the two surrounding the activation are dropped.
+ ASSERT_EQ(3, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
@@ -506,6 +581,25 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
ASSERT_EQ(1, bucketInfo.values_size());
+
+ bucketInfo = data.bucket_info(2);
+ EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
+ bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(MillisToNano(NanoToMillis(deactivationNs)), bucketInfo.end_bucket_elapsed_nanos());
+ ASSERT_EQ(1, bucketInfo.values_size());
+
+ // Check skipped bucket is not added after deactivation.
+ dumpReportTimeNs = configAddedTimeNs + 7 * bucketSizeNs + 10;
+ buffer.clear();
+ // 45 mins + 10 ns.
+ processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ valueMetrics = reports.reports(0).metrics(0).value_metrics();
+ EXPECT_EQ(valueMetrics.skipped_size(), 0);
}
/**
diff --git a/statsd/tests/shell/ShellSubscriber_test.cpp b/statsd/tests/shell/ShellSubscriber_test.cpp
index 37f5bf33..38b5fd15 100644
--- a/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -142,6 +142,29 @@ TEST(ShellSubscriberTest, testPushedSubscription) {
runShellTest(config, uidMap, pullerManager, pushedList, shellData);
}
+TEST(ShellSubscriberTest, testMaxSizeGuard) {
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
+
+ // set up 2 pipes for read/write config and data
+ int fds_config[2];
+ ASSERT_EQ(0, pipe(fds_config));
+
+ int fds_data[2];
+ ASSERT_EQ(0, pipe(fds_data));
+
+ // write invalid size of the config
+ size_t invalidBufferSize = (shellClient->getMaxSizeKb() * 1024) + 1;
+ write(fds_config[1], &invalidBufferSize, sizeof(invalidBufferSize));
+ close(fds_config[1]);
+ close(fds_data[0]);
+
+ EXPECT_FALSE(shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1));
+ close(fds_config[0]);
+ close(fds_data[1]);
+}
+
namespace {
int kUid1 = 1000;
diff --git a/statsd/tests/statsd_test_util.cpp b/statsd/tests/statsd_test_util.cpp
index 6101f650..33b6b568 100644
--- a/statsd/tests/statsd_test_util.cpp
+++ b/statsd/tests/statsd_test_util.cpp
@@ -2038,6 +2038,20 @@ vector<PackageInfo> buildPackageInfos(
return packageInfos;
}
+StatsdStatsReport_PulledAtomStats getPulledAtomStats() {
+ vector<uint8_t> statsBuffer;
+ StatsdStats::getInstance().dumpStats(&statsBuffer, false /*reset stats*/);
+ StatsdStatsReport statsReport;
+ StatsdStatsReport_PulledAtomStats pulledAtomStats;
+ if (!statsReport.ParseFromArray(&statsBuffer[0], statsBuffer.size())) {
+ return pulledAtomStats;
+ }
+ if (statsReport.pulled_atom_stats_size() == 0) {
+ return pulledAtomStats;
+ }
+ return statsReport.pulled_atom_stats(0);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/statsd_test_util.h b/statsd/tests/statsd_test_util.h
index 59b134ce..c4fa1fe4 100644
--- a/statsd/tests/statsd_test_util.h
+++ b/statsd/tests/statsd_test_util.h
@@ -721,6 +721,8 @@ std::vector<T> concatenate(const vector<T>& a, const vector<T>& b) {
result.insert(result.end(), b.begin(), b.end());
return result;
}
+
+StatsdStatsReport_PulledAtomStats getPulledAtomStats();
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/storage/StorageManager_test.cpp b/statsd/tests/storage/StorageManager_test.cpp
index 74eafbf5..96409e64 100644
--- a/statsd/tests/storage/StorageManager_test.cpp
+++ b/statsd/tests/storage/StorageManager_test.cpp
@@ -12,11 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/storage/StorageManager.h"
+
#include <android-base/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
-#include "src/storage/StorageManager.h"
+
+#include "android-base/stringprintf.h"
+#include "stats_log_util.h"
#ifdef __ANDROID__
@@ -196,6 +200,84 @@ TEST(StorageManagerTest, AppendConfigReportTest4) {
clearLocalHistoryTestFiles();
}
+TEST(StorageManagerTest, TrainInfoReadWrite32To64BitTest) {
+ InstallTrainInfo trainInfo;
+ trainInfo.trainVersionCode = 12345;
+ trainInfo.trainName = "This is a train name #)$(&&$";
+ trainInfo.status = 1;
+ const char* expIds = "test_ids";
+ trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
+
+ // Write the train info. fork the code to always write in 32 bit.
+ StorageManager::deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
+ std::string fileName = base::StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long)getWallClockSec(),
+ trainInfo.trainName.c_str());
+
+ int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ ASSERT_NE(fd, -1);
+
+ size_t result;
+ // Write the magic word
+ result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
+ ASSERT_EQ(result, sizeof(TRAIN_INFO_FILE_MAGIC));
+
+ // Write the train version
+ const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
+ result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
+ ASSERT_EQ(result, trainVersionCodeByteCount);
+
+ // Write # of bytes in trainName to file.
+ // NB: this is changed from size_t to int32_t for this test.
+ const int32_t trainNameSize = trainInfo.trainName.size();
+ const size_t trainNameSizeByteCount = sizeof(trainNameSize);
+ result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
+ ASSERT_EQ(result, trainNameSizeByteCount);
+
+ // Write trainName to file
+ result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
+ ASSERT_EQ(result, trainNameSize);
+
+ // Write status to file
+ const size_t statusByteCount = sizeof(trainInfo.status);
+ result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
+ ASSERT_EQ(result, statusByteCount);
+
+ // Write experiment id count to file.
+ // NB: this is changed from size_t to int32_t for this test.
+ const int32_t experimentIdsCount = trainInfo.experimentIds.size();
+ const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
+ result = write(fd, (uint8_t*)&experimentIdsCount, experimentIdsCountByteCount);
+ ASSERT_EQ(result, experimentIdsCountByteCount);
+
+ // Write experimentIds to file
+ for (size_t i = 0; i < experimentIdsCount; i++) {
+ const int64_t experimentId = trainInfo.experimentIds[i];
+ const size_t experimentIdByteCount = sizeof(experimentId);
+ result = write(fd, &experimentId, experimentIdByteCount);
+ ASSERT_EQ(result, experimentIdByteCount);
+ }
+
+ // Write bools to file
+ const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+ result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
+ ASSERT_EQ(result, boolByteCount);
+ result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
+ ASSERT_EQ(result, boolByteCount);
+ result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
+ ASSERT_EQ(result, boolByteCount);
+ close(fd);
+
+ InstallTrainInfo trainInfoResult;
+ EXPECT_TRUE(StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult));
+
+ EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
+ ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
+ EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
+ EXPECT_EQ(trainInfo.status, trainInfoResult.status);
+ ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
+ EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index d02d2c49..06034cb0 100644
--- a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -76,6 +76,7 @@ public class TestDrive {
"com.google.android.apps.nexuslauncher",
"AID_KEYSTORE",
"AID_VIRTUALIZATIONSERVICE",
+ "com.google.android.permissioncontroller",
};
private static final String[] DEFAULT_PULL_SOURCES = {
"AID_KEYSTORE",
diff --git a/tests/src/android/cts/statsd/atom/AtomTestCase.java b/tests/src/android/cts/statsd/atom/AtomTestCase.java
index 1f33c202..83ca2a27 100644
--- a/tests/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/tests/src/android/cts/statsd/atom/AtomTestCase.java
@@ -125,6 +125,8 @@ public class AtomTestCase extends BaseTestCase {
public static final String FEATURE_INCREMENTAL_DELIVERY =
"android.software.incremental_delivery";
+ public static final int SHELL_UID = 2000;
+
// Telephony phone types
public static final int PHONE_TYPE_GSM = 1;
public static final int PHONE_TYPE_CDMA = 2;
@@ -300,13 +302,14 @@ public class AtomTestCase extends BaseTestCase {
getDevice().pushFile(configFile, remotePath);
getDevice().executeShellCommand(
String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
- String.valueOf(CONFIG_ID)));
+ String.valueOf(SHELL_UID), String.valueOf(CONFIG_ID)));
getDevice().executeShellCommand("rm " + remotePath);
}
protected void removeConfig(long configId) throws Exception {
getDevice().executeShellCommand(
- String.join(" ", REMOVE_CONFIG_CMD, String.valueOf(configId)));
+ String.join(" ", REMOVE_CONFIG_CMD,
+ String.valueOf(SHELL_UID), String.valueOf(configId)));
}
/** Gets the statsd report and sorts it. Note that this also deletes that report from statsd. */
@@ -521,8 +524,8 @@ public class AtomTestCase extends BaseTestCase {
protected ConfigMetricsReportList getReportList() throws Exception {
try {
ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
- String.join(" ", DUMP_REPORT_CMD, String.valueOf(CONFIG_ID),
- "--include_current_bucket", "--proto"));
+ String.join(" ", DUMP_REPORT_CMD, String.valueOf(SHELL_UID),
+ String.valueOf(CONFIG_ID), "--include_current_bucket", "--proto"));
return reportList;
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
LogUtil.CLog.e("Failed to fetch and parse the statsd output report. "
@@ -872,21 +875,9 @@ public class AtomTestCase extends BaseTestCase {
data.subList(lastStateIdx+1, data.size()).clear();
}
- /** Returns the UID of the host, which should always either be SHELL (2000) or ROOT (0). */
+ /** Returns the UID of the host, which should always either be SHELL (2000). */
protected int getHostUid() throws DeviceNotAvailableException {
- String strUid = "";
- try {
- strUid = getDevice().executeShellCommand("id -u");
- return Integer.parseInt(strUid.trim());
- } catch (NumberFormatException e) {
- LogUtil.CLog.e("Failed to get host's uid via shell command. Found " + strUid);
- // Fall back to alternative method...
- if (getDevice().isAdbRoot()) {
- return 0; // ROOT
- } else {
- return 2000; // SHELL
- }
- }
+ return SHELL_UID;
}
protected String getProperty(String prop) throws Exception {
@@ -990,7 +981,7 @@ public class AtomTestCase extends BaseTestCase {
public void doAppBreadcrumbReported(int label, int state) throws Exception {
getDevice().executeShellCommand(String.format(
- "cmd stats log-app-breadcrumb %d %d", label, state));
+ "cmd stats log-app-breadcrumb %d %d %d", SHELL_UID, label, state));
}
protected void setBatteryLevel(int level) throws Exception {
diff --git a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index dacbac92..99df7175 100644
--- a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -302,9 +302,9 @@ public class DeviceAtomTestCase extends AtomTestCase {
protected void rebootDeviceAndWaitUntilReady() throws Exception {
rebootDevice();
- // Wait for 2 mins.
+ // Wait for 3 mins.
assertWithMessage("Device failed to boot")
- .that(getDevice().waitForBootComplete(120_000)).isTrue();
+ .that(getDevice().waitForBootComplete(180_000)).isTrue();
assertWithMessage("Stats service failed to start")
.that(waitForStatsServiceStart(60_000)).isTrue();
Thread.sleep(2_000);
diff --git a/tests/src/android/cts/statsd/metric/ValueMetricsTests.java b/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
index 0cf5bbb3..adfc565b 100644
--- a/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -319,11 +319,8 @@ public class ValueMetricsTests extends DeviceAtomTestCase {
LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
assertThat(metricReport.getValueMetrics().getDataList()).isEmpty();
- // Bucket is skipped because metric is not activated.
- assertThat(metricReport.getValueMetrics().getSkippedList()).isNotEmpty();
- assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEventList()).isNotEmpty();
- assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEvent(0).getDropReason())
- .isEqualTo(BucketDropReason.NO_DATA);
+ // Skipped buckets are not added when metric is empty.
+ assertThat(metricReport.getValueMetrics().getSkippedList()).isEmpty();
}
public void testValueMetricWithConditionAndActivation() throws Exception {