diff options
Diffstat (limited to 'cmds')
70 files changed, 1232 insertions, 229 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 46917e4f6062..a6c7cae9bdb6 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -110,13 +110,30 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks) } else { mShuttingDown = true; } + ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); +} + +BootAnimation::~BootAnimation() { + if (mAnimation != nullptr) { + releaseAnimation(mAnimation); + mAnimation = nullptr; + } + ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); } void BootAnimation::onFirstRef() { status_t err = mSession->linkToComposerDeath(this); SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); if (err == NO_ERROR) { - run("BootAnimation", PRIORITY_DISPLAY); + // Load the animation content -- this can be slow (eg 200ms) + // called before waitForSurfaceFlinger() in main() to avoid wait + ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms", + mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); + preloadAnimation(); + ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms", + mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); } } @@ -306,6 +323,20 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; + return NO_ERROR; +} + +bool BootAnimation::preloadAnimation() { + findBootAnimationFile(); + if (!mZipFileName.isEmpty()) { + mAnimation = loadAnimation(mZipFileName); + return (mAnimation != nullptr); + } + + return false; +} + +void BootAnimation::findBootAnimationFile() { // If the device has encryption turned on or is in process // of being encrypted we show the encrypted boot animation. char decrypt[PROPERTY_VALUE_MAX]; @@ -320,7 +351,7 @@ status_t BootAnimation::readyToRun() { for (const char* f : encryptedBootFiles) { if (access(f, R_OK) == 0) { mZipFileName = f; - return NO_ERROR; + return; } } } @@ -332,10 +363,9 @@ status_t BootAnimation::readyToRun() { for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) { if (access(f, R_OK) == 0) { mZipFileName = f; - return NO_ERROR; + return; } } - return NO_ERROR; } bool BootAnimation::threadLoop() @@ -790,8 +820,6 @@ bool BootAnimation::preloadZip(Animation& animation) } } - mCallbacks->init(animation.parts); - zip->endIteration(cookie); return true; @@ -799,13 +827,25 @@ bool BootAnimation::preloadZip(Animation& animation) bool BootAnimation::movie() { - Animation* animation = loadAnimation(mZipFileName); - if (animation == NULL) + if (mAnimation == nullptr) { + mAnimation = loadAnimation(mZipFileName); + } + + if (mAnimation == nullptr) return false; + // mCallbacks->init() may get called recursively, + // this loop is needed to get the same results + for (const Animation::Part& part : mAnimation->parts) { + if (part.animation != nullptr) { + mCallbacks->init(part.animation->parts); + } + } + mCallbacks->init(mAnimation->parts); + bool anyPartHasClock = false; - for (size_t i=0; i < animation->parts.size(); i++) { - if(validClock(animation->parts[i])) { + for (size_t i=0; i < mAnimation->parts.size(); i++) { + if(validClock(mAnimation->parts[i])) { anyPartHasClock = true; break; } @@ -846,7 +886,7 @@ bool BootAnimation::movie() bool clockFontInitialized = false; if (mClockEnabled) { clockFontInitialized = - (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR); + (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR); mClockEnabled = clockFontInitialized; } @@ -855,7 +895,7 @@ bool BootAnimation::movie() mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL); } - playAnimation(*animation); + playAnimation(*mAnimation); if (mTimeCheckThread != nullptr) { mTimeCheckThread->requestExit(); @@ -863,10 +903,11 @@ bool BootAnimation::movie() } if (clockFontInitialized) { - glDeleteTextures(1, &animation->clockFont.texture.name); + glDeleteTextures(1, &mAnimation->clockFont.texture.name); } - releaseAnimation(animation); + releaseAnimation(mAnimation); + mAnimation = nullptr; return false; } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 19616cb790c7..dc19fb09ef1d 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -115,6 +115,7 @@ public: }; explicit BootAnimation(sp<Callbacks> callbacks); + virtual ~BootAnimation(); sp<SurfaceComposerClient> session() const; @@ -155,6 +156,8 @@ private: void releaseAnimation(Animation*) const; bool parseAnimationDesc(Animation&); bool preloadZip(Animation &animation); + void findBootAnimationFile(); + bool preloadAnimation(); void checkExit(); @@ -182,6 +185,7 @@ private: SortedVector<String8> mLoadedFiles; sp<TimeCheckThread> mTimeCheckThread = nullptr; sp<Callbacks> mCallbacks; + Animation* mAnimation = nullptr; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp index a52a5e92a840..6c7b3e51ff8d 100644 --- a/cmds/bootanimation/bootanimation_main.cpp +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -44,14 +44,16 @@ int main() sp<ProcessState> proc(ProcessState::self()); ProcessState::self()->startThreadPool(); + // create the boot animation object (may take up to 200ms for 2MB zip) + sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); + waitForSurfaceFlinger(); - // create the boot animation object - sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); + boot->run("BootAnimation", PRIORITY_DISPLAY); + ALOGV("Boot animation set up. Joining pool."); IPCThreadState::self()->joinThreadPool(); } - ALOGV("Boot animation exit"); return 0; } diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl index ea7274f3ea58..4a66715ad2b8 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -24,6 +24,7 @@ interface IIdmap2 { const int POLICY_SYSTEM_PARTITION = 0x00000002; const int POLICY_VENDOR_PARTITION = 0x00000004; const int POLICY_PRODUCT_PARTITION = 0x00000008; + const int POLICY_SIGNATURE = 0x00000010; @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 99b5f0ff3c2d..ec498ffb393c 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -284,7 +284,7 @@ std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream, bool CheckOverlayable(const LoadedPackage& target_package, const utils::OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_polices, const ResourceId& resid) { + const PolicyBitmask& fulfilled_policies, const ResourceId& resid) { const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid); if (overlayable_info == nullptr) { // If the resource does not have an overlayable definition, allow the resource to be overlaid. @@ -299,7 +299,7 @@ bool CheckOverlayable(const LoadedPackage& target_package, } // Enforce policy restrictions if the resource is declared as overlayable. - return (overlayable_info->policy_flags & fulfilled_polices) != 0; + return (overlayable_info->policy_flags & fulfilled_policies) != 0; } std::unique_ptr<const Idmap> Idmap::FromApkAssets( diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp index 0f87ef0c4ea9..6649288dfa41 100644 --- a/cmds/idmap2/libidmap2/Policies.cpp +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -35,6 +35,7 @@ const std::map<android::StringPiece, PolicyFlags> kStringToFlag = { {"product", PolicyFlags::POLICY_PRODUCT_PARTITION}, {"system", PolicyFlags::POLICY_SYSTEM_PARTITION}, {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION}, + {"signature", PolicyFlags::POLICY_SIGNATURE}, }; } // namespace diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 0e0e25f4f0f3..9a0412efbde4 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -129,28 +129,31 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/policy_system_vendor ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/str1 + success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/policy_signature + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str1 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0000); - success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str2 + success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str2 ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str3 + success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str3 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0001); - success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str4 + success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str4 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0002); - success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/x + success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/x ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/y + success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/y ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/z + success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/z ASSERT_FALSE(success); } diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 8514e12e8c17..2e85eb6215d1 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -39,12 +39,13 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { [](unsigned char type ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 6U); + ASSERT_EQ(v->size(), 7U); ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({ root + "/.", root + "/..", root + "/overlay", root + "/target", + root + "/signature-overlay", root + "/system-overlay", root + "/system-overlay-invalid", })); @@ -56,15 +57,22 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 9U); + ASSERT_EQ(v->size(), 10U); ASSERT_EQ( std::set<std::string>(v->begin(), v->end()), std::set<std::string>( - {root + "/target/target.apk", root + "/target/target-no-overlayable.apk", - root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk", - root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-static-1.apk", - root + "/overlay/overlay-static-2.apk", root + "/system-overlay/system-overlay.apk", - root + "/system-overlay-invalid/system-overlay-invalid.apk"})); + { + root + "/target/target.apk", + root + "/target/target-no-overlayable.apk", + root + "/overlay/overlay.apk", + root + "/overlay/overlay-no-name.apk", + root + "/overlay/overlay-no-name-static.apk", + root + "/overlay/overlay-static-1.apk", + root + "/overlay/overlay-static-2.apk", + root + "/signature-overlay/signature-overlay.apk", + root + "/system-overlay/system-overlay.apk", + root + "/system-overlay-invalid/system-overlay-invalid.apk" + })); } TEST(FileUtilsTests, ReadFile) { diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 1216f9ec736a..a6a2ada76712 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -132,9 +132,9 @@ TEST_F(Idmap2BinaryTests, Dump) { ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020009 -> 0x7f020000 string/str1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020001 string/str3"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020002 string/str4"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020000 string/str1"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000d -> 0x7f020002 string/str4"), std::string::npos); ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos); // clang-format off @@ -286,7 +286,7 @@ TEST_F(Idmap2BinaryTests, Lookup) { "lookup", "--idmap-path", GetIdmapPath(), "--config", "", - "--resid", "0x7f020009"}); // string/str1 + "--resid", "0x7f02000a"}); // string/str1 // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index b40521f054af..53ec03ba3d5e 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -191,8 +191,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xdd53ca29); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xa71ccd77); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xd513ca1b); + ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); @@ -217,7 +217,7 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 9U); + ASSERT_EQ(types[1]->GetEntryOffset(), 10U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); @@ -254,11 +254,76 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor +} + +TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE; + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_flags, /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 1U); + ASSERT_EQ(types[0]->GetEntryOffset(), 7U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature +} + +TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC; + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_flags, /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 0U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 0U); // can't overlay, so contains nothing } // Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled. @@ -292,11 +357,12 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); ASSERT_EQ(types[0]->GetEntry(0), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0006U); // string/policy_system_vendor } // Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled. @@ -330,14 +396,15 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgn ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 6U); + ASSERT_EQ(types[0]->GetEntryCount(), 7U); ASSERT_EQ(types[0]->GetEntryOffset(), 3U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/other ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_product - ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_system_vendor } // The resources of APKs that do not include an overlayable declaration should not restrict what @@ -371,14 +438,15 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayable) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 6U); + ASSERT_EQ(types[0]->GetEntryCount(), 7U); ASSERT_EQ(types[0]->GetEntryOffset(), 3U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/other ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_product ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/string/policy_signature + ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_system_vendor } // The resources of APKs that do not include an overlayable declaration should not restrict what @@ -418,7 +486,7 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTar ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 9U); + ASSERT_EQ(types[1]->GetEntryOffset(), 10U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index a5588c330d85..7ec13ed0ade7 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -52,8 +52,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000008: dd53ca29 target crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000000c: a71ccd77 overlay crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000008: d513ca1b target crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build index e60da803388b..e879f443c7b1 100644..100755 --- a/cmds/idmap2/tests/data/overlay/build +++ b/cmds/idmap2/tests/data/overlay/build @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk" +FRAMEWORK_RES_APK="${ANDROID_BUILD_TOP}/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk" aapt2 compile --dir res -o compiled.flata diff --git a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml new file mode 100644 index 000000000000..5dacebded529 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="test.overlay.system"> + <overlay + android:targetPackage="test.target" + android:targetName="TestResources"/> +</manifest> diff --git a/cmds/idmap2/tests/data/signature-overlay/build b/cmds/idmap2/tests/data/signature-overlay/build new file mode 100755 index 000000000000..fdd8301c3b7d --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/build @@ -0,0 +1,26 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + +aapt2 compile --dir res -o compiled.flata + +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o signature-overlay.apk \ + compiled.flata + +rm compiled.flata diff --git a/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml new file mode 100644 index 000000000000..59e7d8ed69c1 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- This overlay will fulfill the policy "signature". This allows it overlay the + following resources. --> + <string name="policy_signature">policy_signature</string> +</resources> diff --git a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk Binary files differnew file mode 100644 index 000000000000..b2c490dcbb90 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build index 920e1f8ad6f3..920e1f8ad6f3 100644..100755 --- a/cmds/idmap2/tests/data/system-overlay-invalid/build +++ b/cmds/idmap2/tests/data/system-overlay-invalid/build diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml index af1bea16daee..02704009d743 100644 --- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml +++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml @@ -22,6 +22,7 @@ <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. --> <string name="policy_product">policy_product</string> + <string name="policy_signature">policy_signature</string> <!-- Requests to overlay a resource that is not declared as overlayable. --> <string name="not_overlayable">not_overlayable</string> diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk Binary files differindex 710ed9067f69..9448939b5636 100644 --- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk +++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build index be0d2390f535..be0d2390f535 100644..100755 --- a/cmds/idmap2/tests/data/system-overlay/build +++ b/cmds/idmap2/tests/data/system-overlay/build diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build index 137ddb5ecaa1..e6df742cc9da 100644..100755 --- a/cmds/idmap2/tests/data/target/build +++ b/cmds/idmap2/tests/data/target/build @@ -17,5 +17,5 @@ aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata rm compiled.flata aapt2 compile res/values/values.xml -o . -aapt2 link --manifest AndroidManifest.xml -A assets -o target_no_overlayable.apk values_values.arsc.flat +aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat rm values_values.arsc.flat
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml index 02d25630546f..0bf83fa50b75 100644 --- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml +++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml @@ -15,20 +15,12 @@ --> <resources> <overlayable name="TestResources"> - <!-- Publicly overlayable resources --> - <item type="string" name="a" /> - <item type="string" name="b" /> - <item type="string" name="c" /> - <item type="string" name="str1" /> - <item type="string" name="str2" /> - <item type="string" name="str3" /> - <item type="string" name="str4" /> - <item type="string" name="x" /> - <item type="string" name="y" /> - <item type="string" name="z" /> - <item type="integer" name="int1" /> - - <!-- Resources with partition restrictins --> + <!-- Resources with signature restrictions --> + <policy type="signature"> + <item type="string" name="policy_signature" /> + </policy> + + <!-- Resources with partition restrictions --> <policy type="system"> <item type="string" name="policy_system" /> </policy> @@ -41,12 +33,26 @@ <item type="string" name="policy_product" /> </policy> + <!-- Resources publicly overlayable --> <policy type="public"> <item type="string" name="policy_public" /> + <item type="string" name="a" /> + <item type="string" name="b" /> + <item type="string" name="c" /> + <item type="string" name="str1" /> + <item type="string" name="str2" /> + <item type="string" name="str3" /> + <item type="string" name="str4" /> + <item type="string" name="x" /> + <item type="string" name="y" /> + <item type="string" name="z" /> + <item type="integer" name="int1" /> </policy> </overlayable> <overlayable name="OtherResources"> - <item type="string" name="other" /> + <policy type="public"> + <item type="string" name="other" /> + </policy> </overlayable> </resources>
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml index 0d337f3890a8..edd53f4c9b52 100644 --- a/cmds/idmap2/tests/data/target/res/values/values.xml +++ b/cmds/idmap2/tests/data/target/res/values/values.xml @@ -33,6 +33,7 @@ <string name="policy_system_vendor">policy_system_vendor</string> <string name="policy_product">policy_product</string> <string name="policy_public">policy_public</string> + <string name="policy_signature">policy_signature</string> <item type="string" name="other" /> </resources> diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk Binary files differindex 8676cbb9dc3f..908b54a2f0c3 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex ecbe87578684..da3c1aef5fba 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index faf2053835fa..f4086557870d 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -76,6 +76,7 @@ cc_defaults { "src/external/SubsystemSleepStatePuller.cpp", "src/external/PowerStatsPuller.cpp", "src/external/ResourceHealthManagerPuller.cpp", + "src/external/TrainInfoPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/puller_util.cpp", "src/logd/LogEvent.cpp", @@ -238,6 +239,7 @@ cc_test { "tests/guardrail/StatsdStats_test.cpp", "tests/metrics/metrics_test_helper.cpp", "tests/statsd_test_util.cpp", + "tests/storage/StorageManager_test.cpp", "tests/e2e/WakelockDuration_e2e_test.cpp", "tests/e2e/MetricActivation_e2e_test.cpp", "tests/e2e/MetricConditionLink_e2e_test.cpp", diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 80ed80776829..13f5c8ae5fd8 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -19,6 +19,7 @@ #include "FieldValue.h" #include "HashableDimensionKey.h" #include "math.h" +#include "statslog.h" namespace android { namespace os { @@ -122,6 +123,24 @@ bool isAttributionUidField(const FieldValue& value) { return false; } +int32_t getUidIfExists(const FieldValue& value) { + bool isUid = false; + // the field is uid field if the field is the uid field in attribution node or marked as + // is_uid in atoms.proto + if (isAttributionUidField(value)) { + isUid = true; + } else { + auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag()); + if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { + int uidField = it->second; // uidField is the field number in proto + isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField && + value.mValue.getType() == INT; + } + } + + return isUid ? value.mValue.int_value : -1; +} + bool isAttributionUidField(const Field& field, const Value& value) { int f = field.getField() & 0xff007f; if (f == 0x10001 && value.getType() == INT) { diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index a5d00ac4e72b..6729e052b5ee 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -386,6 +386,9 @@ bool HasPositionALL(const FieldMatcher& matcher); bool isAttributionUidField(const FieldValue& value); +/* returns uid if the field is uid field, or -1 if the field is not a uid field */ +int getUidIfExists(const FieldValue& value); + void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); bool isAttributionUidField(const Field& field, const Value& value); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index b478fed49a54..c542b6215c88 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -31,6 +31,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionController.h> +#include <cutils/multiuser.h> #include <dirent.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> @@ -47,6 +48,7 @@ using namespace android; using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; namespace android { @@ -62,6 +64,8 @@ constexpr const char* kOpUsage = "android:get_usage_stats"; // for StatsDataDumpProto const int FIELD_ID_REPORTS_LIST = 1; +// for TrainInfo experiment id serialization +const int FIELD_ID_EXPERIMENT_ID = 1; static binder::Status ok() { return binder::Status::ok(); @@ -155,7 +159,7 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) } })) { - mUidMap = new UidMap(); + mUidMap = UidMap::getInstance(); mPullerManager = new StatsPullerManager(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); @@ -1167,6 +1171,56 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p return Status::ok(); } +Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName, + int64_t trainVersionCode, int options, + int32_t state, + const std::vector<int64_t>& experimentIds) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + // For testing + if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) { + return ok(); + } + + // Caller must be granted these permissions + if (!checkCallingPermission(String16(kPermissionDump))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); + } + if (!checkCallingPermission(String16(kPermissionUsage))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); + } + // TODO: add verifier permission + + userid_t userId = multiuser_get_user_id(uid); + + bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING; + bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED; + bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + + ProtoOutputStream proto; + for (const auto& expId : experimentIds) { + proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, + (long long)expId); + } + + vector<uint8_t> buffer; + buffer.resize(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging, + rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId); + mProcessor->OnLogEvent(&event); + StorageManager::writeTrainInfo(trainVersionCode, buffer); + return Status::ok(); +} + hardware::Return<void> StatsService::reportSpeakerImpedance( const SpeakerImpedance& speakerImpedance) { LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 7f10d74ec7d6..d24565a63054 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -31,6 +31,7 @@ #include <android/frameworks/stats/1.0/types.h> #include <android/os/BnStatsManager.h> #include <android/os/IStatsCompanionService.h> +#include <android/os/IStatsManager.h> #include <binder/IResultReceiver.h> #include <utils/Looper.h> @@ -186,6 +187,13 @@ public: virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; /** + * Binder call to log BinaryPushStateChanged atom. + */ + virtual Status sendBinaryPushStateChangedAtom( + const android::String16& trainName, int64_t trainVersionCode, int options, + int32_t state, const std::vector<int64_t>& experimentIds) override; + + /** * Binder call to get SpeakerImpedance atom. */ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 8d7369935ea4..019a9f7e5f9a 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -78,8 +78,8 @@ void AlarmTracker::informAlarmsFired( } if (!mSubscriptions.empty()) { VLOG("AlarmTracker triggers the subscribers."); - triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey, - mSubscriptions); + triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY, + 0 /* metricValue N/A */, mConfigKey, mSubscriptions); } firedAlarms.erase(mInternalAlarm); mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index ee111cddcfd7..d1dcb5df7838 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -207,7 +207,8 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } -void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) { +void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId, + const MetricDimensionKey& key, int64_t metricValue) { // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on // real time right now. if (isInRefractoryPeriod(timestampNs, key)) { @@ -225,7 +226,7 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDime if (!mSubscriptions.empty()) { ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", mAlert.id(), key.toString().c_str()); - informSubscribers(key); + informSubscribers(key, metricId, metricValue); } else { ALOGI("An anomaly has occurred! (But no subscriber for that alert.)"); } @@ -238,11 +239,11 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDime } void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, - const int64_t& currBucketNum, + const int64_t& currBucketNum, int64_t metricId, const MetricDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { - declareAnomaly(timestampNs, key); + declareAnomaly(timestampNs, metricId, key, currentBucketValue); } } @@ -255,8 +256,9 @@ bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs, return false; } -void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { - triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions); +void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id, + int64_t metricValue) { + triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions); } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 927e2df1b53a..e9414735b82b 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -67,14 +67,16 @@ public: const int64_t& currentBucketValue); // Informs incidentd about the detected alert. - void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key); + void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key, + int64_t metricValue); // Detects if, based on past buckets plus the new currentBucketValue (which generally // represents the partially-filled current bucket), an anomaly has happened, and if so, // declares an anomaly and informs relevant subscribers. // Also advances to currBucketNum-1. void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum, - const MetricDimensionKey& key, const int64_t& currentBucketValue); + int64_t metricId, const MetricDimensionKey& key, + const int64_t& currentBucketValue); // Init the AlarmMonitor which is shared across anomaly trackers. virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) { @@ -176,7 +178,7 @@ protected: virtual void resetStorage(); // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred. - void informSubscribers(const MetricDimensionKey& key); + void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index 3acfd171848e..2b56810170e5 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -65,7 +65,9 @@ void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey, // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now. if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) { - declareAnomaly(timestampNs, dimensionKey); + declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey, + mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - + itr->second->timestampSec); } if (mAlarmMonitor != nullptr) { mAlarmMonitor->remove(itr->second); @@ -100,7 +102,9 @@ void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs, // Now declare each of these alarms to have fired. for (const auto& kv : matchedAlarms) { - declareAnomaly(timestampNs, kv.first); + declareAnomaly( + timestampNs, mAlert.metric_id(), kv.first, + mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec); mAlarms.erase(kv.first); firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. } diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index 6b46b8b3b900..548a6869436d 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -30,9 +30,8 @@ namespace android { namespace os { namespace statsd { -void triggerSubscribers(const int64_t rule_id, - const MetricDimensionKey& dimensionKey, - const ConfigKey& configKey, +void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey, + int64_t metricValue, const ConfigKey& configKey, const std::vector<Subscription>& subscriptions) { VLOG("informSubscribers called."); if (subscriptions.empty()) { @@ -50,13 +49,14 @@ void triggerSubscribers(const int64_t rule_id, } switch (subscription.subscriber_information_case()) { case Subscription::SubscriberInformationCase::kIncidentdDetails: - if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) { + if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId, + dimensionKey, metricValue, configKey)) { ALOGW("Failed to generate incident report."); } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(), - subscription.id(), rule_id, configKey)) { + subscription.id(), ruleId, configKey)) { ALOGW("Failed to generate perfetto traces."); } break; @@ -66,7 +66,7 @@ void triggerSubscribers(const int64_t rule_id, break; case Subscription::SubscriberInformationCase::kPerfprofdDetails: if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(), - rule_id, configKey)) { + ruleId, configKey)) { ALOGW("Failed to generate perfprofd traces."); } break; @@ -76,7 +76,6 @@ void triggerSubscribers(const int64_t rule_id, } } - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h index dba8981a72aa..1df3c8991f94 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.h +++ b/cmds/statsd/src/anomaly/subscriber_util.h @@ -24,10 +24,9 @@ namespace android { namespace os { namespace statsd { -void triggerSubscribers(const int64_t rule_id, - const MetricDimensionKey& dimensionKey, - const ConfigKey& configKey, - const std::vector<Subscription>& subscriptions); +void triggerSubscribers(const int64_t ruleId, const int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, + const ConfigKey& configKey, const std::vector<Subscription>& subscriptions); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 29f67c7e6f9b..d78647eebe1f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -245,6 +245,8 @@ message Atom { AssistGestureStageReported assist_gesture_stage_reported = 174; AssistGestureFeedbackReported assist_gesture_feedback_reported = 175; AssistGestureProgressReported assist_gesture_progress_reported = 176; + TouchGestureClassified touch_gesture_classified = 177; + HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; } // Pulled events will start at field 10000. @@ -302,6 +304,8 @@ message Atom { RoleHolder role_holder = 10049; DangerousPermissionState dangerous_permission_state = 10050; TrainInfo train_info = 10051; + TimeZoneDataInfo time_zone_data_info = 10052; + SDCardInfo sdcard_info = 10053; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -2407,6 +2411,37 @@ message TouchEventReported { } /** + * Logs gesture classification and timing information for touch events. + * + * Logged from: + * frameworks/base/core/java/android/view/GestureDetector.java + * frameworks/base/core/java/android/view/View.java + */ +message TouchGestureClassified { + // The source of the classification (e.g. Java class name). + optional string source = 1; + + enum Classification { + UNKNOWN_CLASSIFICATION = 0; + SINGLE_TAP = 1; + DOUBLE_TAP = 2; + LONG_PRESS = 3; + DEEP_PRESS = 4; + SCROLL = 5; + } + // The classification of the gesture. + optional Classification classification = 2; + + // The interval from the start of a touch event stream until the + // classification was made. + optional int32 latency_millis = 3; + + // The distance from the location of the first touch event to the + // location of the touch event when the classification was made. + optional float displacement_px = 4; +} + +/** * Logs that a setting was updated. * Logged from: * frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -3156,6 +3191,13 @@ message FlagFlipUpdateOccurred { optional int64 order_id = 2; } +/** + * Potential experiment ids that goes with a train install. + */ +message TrainExperimentIds { + repeated int64 experiment_id = 1; +} + /* * Logs when a binary push state changes. * Logged by the installer via public api. @@ -3182,8 +3224,14 @@ message BinaryPushStateChanged { INSTALL_FAILURE = 6; INSTALL_CANCELLED = 7; INSTALLER_ROLLBACK_REQUESTED = 8; + INSTALLER_ROLLBACK_SUCCESS = 9; + INSTALLER_ROLLBACK_FAILURE = 10; } optional State state = 6; + // Possible experiment ids for monitoring this push. + optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES]; + // user id + optional int32 user_id = 8; } /** Represents USB port overheat event. */ @@ -3217,6 +3265,28 @@ message BatteryCycleCount { optional int32 cycle_count = 1; } +/** + * Logs that an SD card is mounted and information about it, its type (public or private) and the + * size in bytes. + * Pulled from: + * StatsCompanionService + */ + +message SDCardInfo { + + enum Type { + UNKNOWN = 0; + TYPE_PUBLIC = 1; + TYPE_PRIVATE = 2; + OTHERS = 3; + } + + // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal. + optional Type type = 1; + // Total size of the sd card in bytes. + optional int64 size_bytes = 2; +} + /* * Logs when a connection becomes available and lost. * Logged in StatsCompanionService.java @@ -3269,6 +3339,33 @@ message ServiceLaunchReported { optional string service_name = 3; } +/** + * Logs when a hidden API is used. + * + * Logged from: + * libcore/libart/src/main/java/dalvik/system/VMRuntime.java + */ +message HiddenApiUsed { + // The uid of the app making the hidden access. + optional int32 uid = 1 [(is_uid) = true]; + + // Signature of the method or field accessed. + optional string signature = 2; + + enum AccessMethod { + NONE = 0; + REFLECTION = 1; + JNI = 2; + LINKING = 3; + } + + // Type of access. + optional AccessMethod access_method = 3; + + // Whether the access was prevented or not. + optional bool access_denied = 4; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4752,6 +4849,12 @@ message AppCompacted { // The process state at the time of compaction. optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN]; + + // Free ZRAM in kilobytes before compaction. + optional int64 before_zram_free_kilobytes = 17; + + // Free ZRAM in kilobytes after compaction. + optional int64 after_zram_free_kilobytes = 18; } /** @@ -5511,13 +5614,6 @@ message DeviceIdentifierAccessDenied { } /** - * Potential experiment ids that goes with a train install. - */ -message TrainExperimentIds { - repeated int64 experiment_id = 1; -} - -/** * Pulls the ongoing mainline install train version code. * Pulled from StatsCompanionService */ @@ -5558,3 +5654,11 @@ message AssistGestureProgressReported { // [0,100] progress for the assist gesture. optional int32 progress = 1; } + +/* + * Information about the time zone data on a device. + */ +message TimeZoneDataInfo { + // A version identifier for the data set on device. e.g. "2018i" + optional string tzdb_version = 1; +} diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ed72b2914a34..1513834b6724 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -29,10 +29,11 @@ #include "../statscompanion_util.h" #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" -#include "StatsCompanionServicePuller.h" #include "StatsCallbackPuller.h" +#include "StatsCompanionServicePuller.h" #include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" +#include "TrainInfoPuller.h" #include "statslog.h" #include <iostream> @@ -152,7 +153,7 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, // temperature {android::util::TEMPERATURE, - {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, + {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, // binder_calls {android::util::BINDER_CALLS, {.additiveFields = {4, 5, 6, 8, 12}, @@ -231,6 +232,14 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // PermissionState. {android::util::DANGEROUS_PERMISSION_STATE, {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}}, + // TrainInfo. + {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}}, + // TimeZoneDataInfo. + {android::util::TIME_ZONE_DATA_INFO, + {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}}, + // SDCardInfo + {android::util::SDCARD_INFO, + {.puller = new StatsCompanionServicePuller(android::util::SDCARD_INFO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp new file mode 100644 index 000000000000..9d0924297912 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "external/StatsPuller.h" + +#include "TrainInfoPuller.h" +#include "logd/LogEvent.h" +#include "stats_log_util.h" +#include "statslog.h" +#include "storage/StorageManager.h" + +using std::make_shared; +using std::shared_ptr; + +namespace android { +namespace os { +namespace statsd { + +TrainInfoPuller::TrainInfoPuller() : + StatsPuller(android::util::TRAIN_INFO) { +} + +bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { + InstallTrainInfo trainInfo; + bool ret = StorageManager::readTrainInfo(trainInfo); + if (!ret) { + ALOGW("Failed to read train info."); + return false; + } + auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo); + data->push_back(event); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h new file mode 100644 index 000000000000..615d02351fd3 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Reads train info from disk. + */ +class TrainInfoPuller : public StatsPuller { + public: + TrainInfoPuller(); + + private: + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index b433c41518cc..d661ee8651be 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -478,6 +478,11 @@ void StatsdStats::noteBucketDropped(int64_t metricId) { getAtomMetricStats(metricId).bucketDropped++; } +void StatsdStats::noteBucketUnknownCondition(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).bucketUnknownCondition++; +} + void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).conditionChangeInNextBucket++; @@ -497,7 +502,7 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); } -StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) { +StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { auto atomMetricStatsIter = mAtomMetricStats.find(metricId); if (atomMetricStatsIter != mAtomMetricStats.end()) { return atomMetricStatsIter->second; diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 5275c8f34fd0..e039be2b4395 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -409,6 +409,11 @@ public: void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); /** + * Number of buckets with unknown condition. + */ + void noteBucketUnknownCondition(int64_t metricId); + + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue * to collect stats after reset() has been called. @@ -458,6 +463,7 @@ public: long bucketDropped = 0; int64_t minBucketBoundaryDelayNs = 0; int64_t maxBucketBoundaryDelayNs = 0; + long bucketUnknownCondition = 0; } AtomMetricStats; private: @@ -488,7 +494,7 @@ private: std::map<int, PulledAtomStats> mPulledAtomStats; // Maps metric ID to its stats. The size is capped by the number of metrics. - std::map<int, AtomMetricStats> mAtomMetricStats; + std::map<int64_t, AtomMetricStats> mAtomMetricStats; struct LogLossStats { LogLossStats(int32_t sec, int32_t count, int32_t error) @@ -532,7 +538,7 @@ private: * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference * will live as long as `this`. */ - StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId); + StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId); FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 40a4070d2974..dec36b54a1ce 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -20,6 +20,8 @@ #include "stats_log_util.h" #include "statslog.h" +#include <binder/IPCThreadState.h> + namespace android { namespace os { namespace statsd { @@ -180,6 +182,25 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT } } +LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId) { + mLogdTimestampNs = getWallClockNs(); + mElapsedTimestampNs = getElapsedRealtimeNs(); + mTagId = android::util::BINARY_PUSH_STATE_CHANGED; + mLogUid = android::IPCThreadState::self()->getCallingUid(); + + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled))); + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId))); +} + LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance) { mLogdTimestampNs = wallClockTimestampNs; @@ -340,6 +361,16 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, } } +LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& trainInfo) { + mLogdTimestampNs = wallClockTimestampNs; + mElapsedTimestampNs = elapsedTimestampNs; + mTagId = android::util::TRAIN_INFO; + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds))); +} + LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {} LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 784376a1580c..111a619760df 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -55,6 +55,11 @@ struct AttributionNodeInternal { int32_t mUid; std::string mTag; }; + +struct InstallTrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; /** * Wrapper for the log_msg structure. */ @@ -97,6 +102,11 @@ public: const std::map<int32_t, std::string>& string_map, const std::map<int32_t, float>& float_map); + // Constructs a BinaryPushStateChanged LogEvent from API call. + explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance); @@ -127,6 +137,9 @@ public: explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const VendorAtom& vendorAtom); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& installTrainInfo); + ~LogEvent(); /** diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 350745b2c326..e84f88d407d3 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -250,7 +250,7 @@ void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { @@ -303,7 +303,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( if (prev != mCurrentFullCounters->end()) { countWholeBucket += prev->second; } - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, countWholeBucket); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 6c1c47bbc093..da6b97cc4e59 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -433,7 +433,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; flushIfNeededLocked(eventTime); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { for (auto& pair : whatIt.second) { @@ -767,12 +767,13 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, !mSameConditionDimensionsInTracker, !mHasLinksToAllConditionDimensionsInTracker, &dimensionKeysInCondition); - condition = (conditionState == ConditionState::kTrue); + condition = conditionState == ConditionState::kTrue; if (mDimensionsInCondition.empty() && condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; if (condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 1b830a3f69f5..ec561b527025 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -137,6 +137,7 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); + FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket); FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 7e695a69988f..3b4af6533e34 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -132,7 +132,7 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } void EventMetricProducer::onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index d56a355f15d6..63017936b1db 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -320,15 +320,15 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { // 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 && mCurrentSlicedBucket->empty(); + triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty(); break; } case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } case GaugeMetric::FIRST_N_SAMPLES: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } default: @@ -364,7 +364,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTimeNs) { VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId); flushIfNeededLocked(eventTimeNs); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. @@ -377,7 +377,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. - mCondition = overallCondition; + mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. @@ -485,8 +485,8 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( gaugeVal = value.long_value; } for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, - gaugeVal); + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, + eventKey, gaugeVal); } } } diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 11075685b7fa..4cf5333947d3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -45,7 +45,8 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo &dimensionKeysInCondition); condition = (conditionState == ConditionState::kTrue); } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; } if (mDimensionsInCondition.empty() && condition) { @@ -107,7 +108,8 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } - if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) { + if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT && + it->second.state == ActivationState::kNotActive) { it->second.state = ActivationState::kActiveOnBoot; return; } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 849cb76ec392..8ab3b0680276 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -59,7 +59,7 @@ public: mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), - mCondition(conditionIndex >= 0 ? false : true), + mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue), mConditionSliced(false), mWizard(wizard), mConditionTrackerIndex(conditionIndex), @@ -315,7 +315,7 @@ protected: int64_t mBucketSizeNs; - bool mCondition; + ConditionState mCondition; bool mConditionSliced; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 851ae9962d09..3cf378d7d7ce 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -154,7 +154,7 @@ ValueMetricProducer::ValueMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; // Kicks off the puller immediately if condition is true and diff based. - if (mIsPulled && mCondition && mUseDiff) { + if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { pullAndMatchEventsLocked(startTimeNs); } VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), @@ -341,17 +341,21 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, flushIfNeededLocked(eventTimeNs); // Pull on condition changes. - if (mIsPulled && (mCondition != condition)) { + bool conditionChanged = mCondition != condition; + bool unknownToFalse = mCondition == ConditionState::kUnknown + && condition == ConditionState::kFalse; + // We do not need to pull when we go from unknown to false. + if (mIsPulled && conditionChanged && !unknownToFalse) { pullAndMatchEventsLocked(eventTimeNs); } // when condition change from true to false, clear diff base but don't // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && mCondition && !condition) { + if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) { resetBase(); } - mCondition = condition; + mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; } void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { @@ -372,7 +376,7 @@ int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTime void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition) { + if (mCondition == ConditionState::kTrue) { if (!pullSuccess) { // If the pull failed, we won't be able to compute a diff. invalidateCurrentBucket(); @@ -693,8 +697,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn wholeBucketVal += prev->second; } for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly( - eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, + wholeBucketVal); } } } @@ -725,6 +729,10 @@ void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { } void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { + if (mCondition == ConditionState::kUnknown) { + StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); + } + VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index ccb1d4359e89..081e61ed21fa 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -155,8 +155,8 @@ protected: const int64_t& currentBucketValue) { for (auto& anomalyTracker : mAnomalyTrackers) { if (anomalyTracker != nullptr) { - anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey, - currentBucketValue); + anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId, + mEventKey, currentBucketValue); } } } diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 5cf012638dce..d4b57dd68134 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -73,6 +73,11 @@ UidMap::UidMap() : mBytesUsed(0) {} UidMap::~UidMap() {} +sp<UidMap> UidMap::getInstance() { + static sp<UidMap> sInstance = new UidMap(); + return sInstance; +} + bool UidMap::hasApp(int uid, const string& packageName) const { lock_guard<mutex> lock(mMutex); @@ -336,6 +341,61 @@ size_t UidMap::getBytesUsed() const { return mBytesUsed; } +void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto) { + lock_guard<mutex> lock(mMutex); + + writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids, + str_set, proto); +} + +void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, + const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); + for (const auto& kv : mMap) { + if (!interestingUids.empty() && + interestingUids.find(kv.first.first) == interestingUids.end()) { + continue; + } + uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_SNAPSHOT_PACKAGE_INFO); + if (str_set != nullptr) { + str_set->insert(kv.first.second); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, + (long long)Hash64(kv.first.second)); + if (includeVersionStrings) { + str_set->insert(kv.second.versionString); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, + (long long)Hash64(kv.second.versionString)); + } + if (includeInstaller) { + str_set->insert(kv.second.installer); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, + (long long)Hash64(kv.second.installer)); + } + } else { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); + if (includeVersionStrings) { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, + kv.second.versionString); + } + if (includeInstaller) { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, + kv.second.installer); + } + } + + proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, + (long long)kv.second.versionCode); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); + proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); + proto->end(token); + } +} + void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, bool includeVersionStrings, bool includeInstaller, ProtoOutputStream* proto) { @@ -381,43 +441,9 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::s // Write snapshot from current uid map state. uint64_t snapshotsToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); - for (const auto& kv : mMap) { - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SNAPSHOT_PACKAGE_INFO); - - if (str_set != nullptr) { - str_set->insert(kv.first.second); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, - (long long)Hash64(kv.first.second)); - if (includeVersionStrings) { - str_set->insert(kv.second.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, - (long long)Hash64(kv.second.versionString)); - } - if (includeInstaller) { - str_set->insert(kv.second.installer); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, - (long long)Hash64(kv.second.installer)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, - kv.second.versionString); - } - if (includeInstaller) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, - kv.second.installer); - } - } - - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, - (long long)kv.second.versionCode); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); - proto->end(token); - } + writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, + std::set<int32_t>() /*empty uid set means including every uid*/, + str_set, proto); proto->end(snapshotsToken); int64_t prevMin = getMinimumTimestampNs(); diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 75ff507ef09a..a7c5fb27375c 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -91,6 +91,8 @@ public: UidMap(); ~UidMap(); static const std::map<std::string, uint32_t> sAidToUidMapping; + + static sp<UidMap> getInstance(); /* * All three inputs must be the same size, and the jth element in each array refers to the same * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. @@ -152,12 +154,25 @@ public: std::set<int32_t> getAppUid(const string& package) const; + // Write current PackageInfoSnapshot to ProtoOutputStream. + // interestingUids: If not empty, only write the package info for these uids. If empty, write + // package info for all uids. + // str_set: if not null, add new string to the set and write str_hash to proto + // if null, write string to proto. + void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller, + const std::set<int32_t>& interestingUids, std::set<string>* str_set, + ProtoOutputStream* proto); + private: std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const; string normalizeAppName(const string& appName) const; void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output); + void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto); + mutable mutex mMutex; mutable mutex mIsolatedMutex; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index f7428a50b3da..623d8bc06e38 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -425,6 +425,7 @@ message StatsdStatsReport { optional int64 bucket_dropped = 8; optional int64 min_bucket_boundary_delay_ns = 9; optional int64 max_bucket_boundary_delay_ns = 10; + optional int64 bucket_unknown_condition = 11; } repeated AtomMetricStats atom_metric_stats = 17; @@ -456,3 +457,17 @@ message StatsdStatsReport { } repeated LogLossStats detected_log_loss = 16; } + +message AlertTriggerDetails { + message MetricValue { + optional int64 metric_id = 1; + optional DimensionsValue dimension_in_what = 2; + optional DimensionsValue dimension_in_condition = 3; + optional int64 value = 4; + } + oneof value { + MetricValue trigger_metric = 1; + EventMetricData trigger_event = 2; + } + optional UidMapping.PackageInfoSnapshot package_info = 3; +} diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 01043a2143f0..ca645e186614 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -84,6 +84,7 @@ const int FIELD_ID_INVALIDATED_BUCKET = 7; const int FIELD_ID_BUCKET_DROPPED = 8; const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; +const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; namespace { @@ -489,11 +490,11 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> protoOutput->end(token); } -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput) { uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (int32_t)pair.first); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, (long long)pair.second.hardDimensionLimitReached); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, @@ -512,6 +513,8 @@ void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricSt (long long)pair.second.minBucketBoundaryDelayNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, (long long)pair.second.maxBucketBoundaryDelayNs); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, + (long long)pair.second.bucketUnknownCondition); protoOutput->end(token); } diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index dcea0e653e22..59d4865f8977 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -74,7 +74,7 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> util::ProtoOutputStream* protoOutput); // Helper function to write AtomMetricStats to ProtoOutputStream -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput); template<class T> diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 90f641a34b85..165e57c73743 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -38,10 +38,13 @@ 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" // for ConfigMetricsReportList const int FIELD_ID_REPORTS = 2; +std::mutex StorageManager::sTrainInfoMutex; + using android::base::StringPrintf; using std::unique_ptr; @@ -92,6 +95,71 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte close(fd); } +bool StorageManager::writeTrainInfo(int64_t trainVersionCode, + const std::vector<uint8_t>& experimentIds) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + deleteAllFiles(TRAIN_INFO_DIR); + + string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode); + + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd == -1) { + VLOG("Attempt to access %s but failed", file_name.c_str()); + return false; + } + + size_t result = write(fd, experimentIds.data(), experimentIds.size()); + if (result == experimentIds.size()) { + VLOG("Successfully wrote %s", file_name.c_str()); + } else { + VLOG("Failed to write %s", file_name.c_str()); + return false; + } + + result = fchown(fd, AID_STATSD, AID_STATSD); + if (result) { + VLOG("Failed to chown %s to statsd", file_name.c_str()); + return false; + } + + close(fd); + return true; +} + +bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); + + if (dir == NULL) { + VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); + return false; + } + + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] == '.') { + continue; + } + trainInfo.trainVersionCode = StrToInt64(name); + string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name); + int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string str; + if (android::base::ReadFdToString(fd, &str)) { + close(fd); + std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds)); + VLOG("Read train info file successful: %s", fullPath.c_str()); + return true; + } + } + close(fd); + } + return false; +} + void StorageManager::deleteFile(const char* file) { if (remove(file) != 0) { VLOG("Attempt to delete %s but is not found", file); diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index dcf3bb607380..d6df8674e59b 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -29,6 +29,11 @@ namespace statsd { using android::util::ProtoOutputStream; +struct TrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; + class StorageManager : public virtual RefBase { public: /** @@ -37,6 +42,16 @@ public: static void writeFile(const char* file, const void* buffer, int numBytes); /** + * Writes train info. + */ + static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds); + + /** + * Reads train info. + */ + static bool readTrainInfo(InstallTrainInfo& trainInfo); + + /** * Reads the file content to the buffer. */ static bool readFileToString(const char* file, string* content); @@ -109,6 +124,8 @@ private: * Prints disk usage statistics about a directory related to statsd. */ static void printDirStats(int out, const char* path); + + static std::mutex sTrainInfoMutex; }; } // namespace statsd diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index 42cac0cc4122..0ed2d75802da 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -16,7 +16,10 @@ #define DEBUG false #include "Log.h" +#include "FieldValue.h" #include "IncidentdReporter.h" +#include "packages/UidMap.h" +#include "stats_log_util.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> @@ -43,8 +46,18 @@ const int FIELD_ID_CONFIG_KEY = 3; const int FIELD_ID_CONFIG_KEY_UID = 1; const int FIELD_ID_CONFIG_KEY_ID = 2; +const int FIELD_ID_TRIGGER_DETAILS = 4; +const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1; +const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1; +const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2; +const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3; +const int FIELD_ID_METRIC_VALUE_VALUE = 4; + +const int FIELD_ID_PACKAGE_INFO = 3; + namespace { -void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) { +void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey, + int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) { ProtoOutputStream headerProto; headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); uint64_t token = @@ -53,6 +66,58 @@ void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uin headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId()); headerProto.end(token); + token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS); + + // MetricValue trigger_metric = 1; + uint64_t metricToken = + headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC); + // message MetricValue { + // optional int64 metric_id = 1; + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId); + // optional DimensionsValue dimension_in_what = 2; + uint64_t dimToken = + headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto); + headerProto.end(dimToken); + + // optional DimensionsValue dimension_in_condition = 3; + dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto); + headerProto.end(dimToken); + + // optional int64 value = 4; + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue); + + // } + headerProto.end(metricToken); + + // write relevant uid package info + std::set<int32_t> uids; + + for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) { + int uid = getUidIfExists(dim); + // any uid <= 2000 are predefined AID_* + if (uid > 2000) { + uids.insert(uid); + } + } + + for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) { + int uid = getUidIfExists(dim); + if (uid > 2000) { + uids.insert(uid); + } + } + + if (!uids.empty()) { + uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO); + UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids, + nullptr /*string set*/, &headerProto); + headerProto.end(token); + } + + headerProto.end(token); + protoData->resize(headerProto.size()); size_t pos = 0; auto iter = headerProto.data(); @@ -65,7 +130,8 @@ void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uin } } // namespace -bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, +bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, const ConfigKey& configKey) { if (config.section_size() == 0) { VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, @@ -76,7 +142,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_ IncidentReportArgs incidentReport; vector<uint8_t> protoData; - getProtoData(rule_id, configKey, &protoData); + getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData); incidentReport.addHeader(protoData); for (int i = 0; i < config.section_size(); i++) { diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h index 1b83fe23de8f..e78a4d98dcd8 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ b/cmds/statsd/src/subscriber/IncidentdReporter.h @@ -16,6 +16,7 @@ #pragma once +#include "HashableDimensionKey.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails @@ -26,7 +27,8 @@ namespace statsd { /** * Calls incidentd to trigger an incident report and put in dropbox for uploading. */ -bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, +bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, const ConfigKey& configKey); } // namespace statsd diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index e0cbd5d25716..a98ecd586b42 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -27,3 +27,4 @@ on post-fs-data mkdir /data/misc/stats-data/ 0770 statsd system mkdir /data/misc/stats-service/ 0770 statsd system mkdir /data/misc/stats-active-metric/ 0770 statsd system + mkdir /data/misc/train-info/ 0770 statsd system diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 5f3aae3ab93a..4579ca6008ef 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -297,7 +297,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { // Setup a simple config, no activation StatsdConfig config1; - config1.set_id(12341); + int64_t cfgId1 = 12341; + config1.set_id(cfgId1); config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); *config1.add_atom_matcher() = wakelockAcquireMatcher; @@ -314,14 +315,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { countMetric2->set_what(wakelockAcquireMatcher.id()); countMetric2->set_bucket(FIVE_MINUTES); - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + ConfigKey cfgKey1(uid, cfgId1); // Add another config, with two metrics, one with activation StatsdConfig config2; - config2.set_id(12342); + int64_t cfgId2 = 12342; + config2.set_id(cfgId2); config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config2.add_atom_matcher() = wakelockAcquireMatcher; @@ -344,11 +343,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric3ActivationTrigger->set_ttl_seconds(100); - ConfigKey cfgKey2(uid, 12342); + ConfigKey cfgKey2(uid, cfgId2); // Add another config, with two metrics, both with activations StatsdConfig config3; - config3.set_id(12342); + int64_t cfgId3 = 12343; + config3.set_id(cfgId3); config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config3.add_atom_matcher() = wakelockAcquireMatcher; @@ -376,14 +376,37 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric6ActivationTrigger->set_ttl_seconds(200); - ConfigKey cfgKey3(uid, 12343); + ConfigKey cfgKey3(uid, cfgId3); - processor->OnConfigUpdated(2, cfgKey2, config2); - processor->OnConfigUpdated(3, cfgKey3, config3); + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; - EXPECT_EQ(3, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + timeBase1, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(1, cfgKey1, config1); + processor.OnConfigUpdated(2, cfgKey2, config2); + processor.OnConfigUpdated(3, cfgKey3, config3); + + EXPECT_EQ(3, processor.mMetricsManagers.size()); + + // Expect the first config and both metrics in it to be active. + auto it = processor.mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager1 = it->second; EXPECT_TRUE(metricsManager1->isActive()); @@ -407,8 +430,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer2 = *metricIt; EXPECT_TRUE(metricProducer2->isActive()); - it = processor->mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. + it = processor.mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager2 = it->second; EXPECT_TRUE(metricsManager2->isActive()); @@ -432,8 +456,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer4 = *metricIt; EXPECT_TRUE(metricProducer4->isActive()); - it = processor->mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect the third config and both metrics in it to be inactive. + it = processor.mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager3 = it->second; EXPECT_FALSE(metricsManager3->isActive()); @@ -457,10 +482,30 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer6 = *metricIt; EXPECT_FALSE(metricProducer6->isActive()); + // No broadcast for active configs should have happened yet. + EXPECT_EQ(broadcastCount, 0); + + // Activate all 3 metrics that were not active. std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + // Assert that all 3 configs are active. + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_TRUE(metricsManager3->isActive()); + + // A broadcast should have happened, and all 3 configs should be active in the broadcast. + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 3); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) + != activeConfigsBroadcast.end()); + + // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; EXPECT_TRUE(metricProducer3->isActive()); int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime); @@ -472,8 +517,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime); EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6); - processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC); + processor.WriteMetricsActivationToDisk(shutDownTime); + // Create a second StatsLogProcessor and push the same 3 configs. long timeBase2 = 1000; sp<StatsLogProcessor> processor2 = CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); @@ -481,6 +527,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); EXPECT_EQ(3, processor2->mMetricsManagers.size()); + + // First config and both metrics are active. it = processor2->mMetricsManagers.find(cfgKey1); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1001 = it->second; @@ -506,6 +554,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1002 = *metricIt; EXPECT_TRUE(metricProducer1002->isActive()); + // Second config is active. Metric 3 is inactive, metric 4 is active. it = processor2->mMetricsManagers.find(cfgKey2); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1002 = it->second; @@ -531,6 +580,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1004 = *metricIt; EXPECT_TRUE(metricProducer1004->isActive()); + // Config 3 is inactive. both metrics are inactive. it = processor2->mMetricsManagers.find(cfgKey3); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1003 = it->second; @@ -557,6 +607,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1006 = *metricIt; EXPECT_FALSE(metricProducer1006->isActive()); + // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. EXPECT_FALSE(metricProducer1003->isActive()); const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns); @@ -572,12 +623,16 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->LoadMetricsActivationFromDisk(); + // After loading activations from disk, assert that all 3 metrics are active. EXPECT_TRUE(metricProducer1003->isActive()); EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns); EXPECT_TRUE(metricProducer1005->isActive()); EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns); EXPECT_TRUE(metricProducer1006->isActive()); EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns); + + // Make sure no more broadcasts have happened. + EXPECT_EQ(broadcastCount, 1); } TEST(StatsLogProcessorTest, TestActivationOnBoot) { diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 960fbdab1c15..c10703c36b98 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -90,7 +90,8 @@ void detectAndDeclareAnomalies(AnomalyTracker& tracker, const std::shared_ptr<DimToValMap>& bucket, const int64_t& eventTimestamp) { for (const auto& kv : *bucket) { - tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second); + tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first, + kv.second); } } diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index 29e86f3f9456..85d8a5613a8b 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -66,17 +66,42 @@ TEST(MetricActivationE2eTest, TestCountMetric) { auto config = CreateStatsdConfig(); int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - sp<MetricProducer> metricProducer = - processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + bucketStartTimeNs, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // Two activations: one is triggered by battery saver mode (tracker index 0), the other is // triggered by screen on event (tracker index 2). @@ -93,13 +118,19 @@ TEST(MetricActivationE2eTest, TestCountMetric) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -109,12 +140,13 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -126,7 +158,8 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -134,15 +167,21 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -153,8 +192,12 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // Re-activate. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -163,11 +206,11 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, ADB_DUMP, &buffer); EXPECT_TRUE(buffer.size() > 0); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index b54096441d3f..b294cad1802c 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -117,6 +117,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -140,6 +141,51 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { EXPECT_EQ(1LL, buckets2[0].mDuration); } +TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); + LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); + LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event3.init(); + LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + event4.init(); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + + EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { /** * The duration starts from the first bucket, through the two partial buckets (10-70sec), diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 572b1991f426..7e7ffeda8053 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -2176,7 +2176,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; vector<shared_ptr<LogEvent>> allData; valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); @@ -2226,7 +2226,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Max delay is set to 0 so pull will exceed max delay. valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2257,7 +2257,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Event should be skipped since it is from previous bucket. // Pull should not be called. @@ -2299,7 +2299,7 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.mHasGlobalBase = false; valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2351,7 +2351,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2429,7 +2429,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2); EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid); EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); @@ -2481,7 +2481,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2564,7 +2564,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp new file mode 100644 index 000000000000..f66de0518f80 --- /dev/null +++ b/cmds/statsd/tests/storage/StorageManager_test.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <stdio.h> +#include "src/storage/StorageManager.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using namespace testing; +using std::make_shared; +using std::shared_ptr; +using std::vector; +using testing::Contains; + +TEST(StorageManagerTest, TrainInfoReadWriteTest) { + InstallTrainInfo trainInfo; + trainInfo.trainVersionCode = 12345; + const char* expIds = "test_ids"; + trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); + + StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds); + + InstallTrainInfo result; + StorageManager::readTrainInfo(result); + EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode); + EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size()); + EXPECT_EQ(trainInfo.experimentIds, result.experimentIds); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index d29e68e5a187..3180b77f5700 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -26,6 +26,8 @@ import android.os.SystemClock; import android.os.SystemProperties; public class PowerCommand extends Svc.Command { + private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0; + public PowerCommand() { super("power"); } @@ -42,7 +44,17 @@ public class PowerCommand extends Svc.Command { + " svc power reboot [reason]\n" + " Perform a runtime shutdown and reboot device with specified reason.\n" + " svc power shutdown\n" - + " Perform a runtime shutdown and power off the device.\n"; + + " Perform a runtime shutdown and power off the device.\n" + + " svc power forcesuspend [t]\n" + + " Force the system into suspend, ignoring all wakelocks.\n" + + " t - Number of milliseconds to wait before issuing force-suspend.\n" + + " Helps with devices that can't suspend while plugged in.\n" + + " Defaults to " + FORCE_SUSPEND_DELAY_DEFAULT_MILLIS + ".\n" + + " When using a delay, you must use the nohup shell modifier:\n" + + " 'adb shell nohup svc power forcesuspend [time]'\n" + + " Use caution; this is dangerous. It puts the device to sleep\n" + + " immediately without giving apps or the system an opportunity to\n" + + " save their state.\n"; } public void run(String[] args) { @@ -101,6 +113,20 @@ public class PowerCommand extends Svc.Command { maybeLogRemoteException("Failed to shutdown."); } return; + } else if ("forcesuspend".equals(args[1])) { + int delayMillis = args.length > 2 + ? Integer.parseInt(args[2]) : FORCE_SUSPEND_DELAY_DEFAULT_MILLIS; + try { + Thread.sleep(delayMillis); + if (!pm.forceSuspend()) { + System.err.println("Failed to force suspend."); + } + } catch (InterruptedException e) { + System.err.println("Failed to force suspend: " + e); + } catch (RemoteException e) { + maybeLogRemoteException("Failed to force-suspend with exception: " + e); + } + return; } } } |