diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-23 10:20:23 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-23 10:20:23 +0000 |
commit | ccd352d104ac316c53298fd3988bcf656923bab4 (patch) | |
tree | 05793d5a720fb11b5e06808194be971872e24911 | |
parent | 60ac11eaa6b92d9dc8772520de3e59ecebbff0bd (diff) | |
parent | 01ffc29dbec1ffce7c8eac9e4ebb3fc80b7bb135 (diff) | |
download | StatsD-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
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 { |