diff options
Diffstat (limited to 'cmds')
80 files changed, 3623 insertions, 837 deletions
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index 2adbc1f6e1ae..7c30c8b1e1dd 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -17,8 +17,8 @@ package com.android.commands.am; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS; +import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; -import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL; import android.app.IActivityManager; import android.app.IInstrumentationWatcher; @@ -512,7 +512,7 @@ public class Instrument { flags |= INSTR_FLAG_DISABLE_TEST_API_CHECKS; } if (disableIsolatedStorage) { - flags |= INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL; + flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE; } if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId, abi)) { diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index ecb95bd11c2f..bb2de17b42f3 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -48,6 +48,7 @@ #include <ui/Region.h> #include <gui/ISurfaceComposer.h> +#include <gui/DisplayEventReceiver.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -113,8 +114,8 @@ static constexpr size_t TEXT_POS_LEN_MAX = 16; // --------------------------------------------------------------------------- BootAnimation::BootAnimation(sp<Callbacks> callbacks) - : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), - mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) { + : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), mTimeFormat12Hour(false), + mTimeCheckThread(nullptr), mCallbacks(callbacks), mLooper(new Looper(false)) { mSession = new SurfaceComposerClient(); std::string powerCtl = android::base::GetProperty("sys.powerctl", ""); @@ -154,8 +155,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const { return mSession; } -void BootAnimation::binderDied(const wp<IBinder>&) -{ +void BootAnimation::binderDied(const wp<IBinder>&) { // woah, surfaceflinger died! SLOGD("SurfaceFlinger died, exiting..."); @@ -219,8 +219,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, return NO_ERROR; } -status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) -{ +status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) { SkBitmap bitmap; sk_sp<SkData> data = SkData::MakeWithoutCopy(map->getDataPtr(), map->getDataLength()); @@ -278,6 +277,97 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) return NO_ERROR; } +class BootAnimation::DisplayEventCallback : public LooperCallback { + BootAnimation* mBootAnimation; + +public: + DisplayEventCallback(BootAnimation* bootAnimation) { + mBootAnimation = bootAnimation; + } + + int handleEvent(int /* fd */, int events, void* /* data */) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", + events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); + return 1; // keep the callback + } + + constexpr int kBufferSize = 100; + DisplayEventReceiver::Event buffer[kBufferSize]; + ssize_t numEvents; + do { + numEvents = mBootAnimation->mDisplayEventReceiver->getEvents(buffer, kBufferSize); + for (size_t i = 0; i < static_cast<size_t>(numEvents); i++) { + const auto& event = buffer[i]; + if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) { + SLOGV("Hotplug received"); + + if (!event.hotplug.connected) { + // ignore hotplug disconnect + continue; + } + auto token = SurfaceComposerClient::getPhysicalDisplayToken( + event.header.displayId); + + if (token != mBootAnimation->mDisplayToken) { + // ignore hotplug of a secondary display + continue; + } + + DisplayConfig displayConfig; + const status_t error = SurfaceComposerClient::getActiveDisplayConfig( + mBootAnimation->mDisplayToken, &displayConfig); + if (error != NO_ERROR) { + SLOGE("Can't get active display configuration."); + } + mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(), + displayConfig.resolution.getHeight()); + } + } + } while (numEvents > 0); + + return 1; // keep the callback + } +}; + +EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) { + const EGLint attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLint numConfigs; + EGLConfig config; + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + return config; +} + +ui::Size BootAnimation::limitSurfaceSize(int width, int height) const { + ui::Size limited(width, height); + bool wasLimited = false; + const float aspectRatio = float(width) / float(height); + if (mMaxWidth != 0 && width > mMaxWidth) { + limited.height = mMaxWidth / aspectRatio; + limited.width = mMaxWidth; + wasLimited = true; + } + if (mMaxHeight != 0 && limited.height > mMaxHeight) { + limited.height = mMaxHeight; + limited.width = mMaxHeight * aspectRatio; + wasLimited = true; + } + SLOGV_IF(wasLimited, "Surface size has been limited to [%dx%d] from [%dx%d]", + limited.width, limited.height, width, height); + return limited; +} + status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); @@ -291,8 +381,10 @@ status_t BootAnimation::readyToRun() { if (error != NO_ERROR) return error; - const ui::Size& resolution = displayConfig.resolution; - + mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0); + mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0); + ui::Size resolution = displayConfig.resolution; + resolution = limitSurfaceSize(resolution.width, resolution.height); // create the native surface sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"), resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565); @@ -346,25 +438,12 @@ status_t BootAnimation::readyToRun() { sp<Surface> s = control->getSurface(); // initialize opengl and egl - const EGLint attribs[] = { - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_NONE - }; - EGLint w, h; - EGLint numConfigs; - EGLConfig config; - EGLSurface surface; - EGLContext context; - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(display, nullptr, nullptr); - eglChooseConfig(display, attribs, &config, 1, &numConfigs); - surface = eglCreateWindowSurface(display, config, s.get(), nullptr); - context = eglCreateContext(display, config, nullptr, nullptr); + EGLConfig config = getEglConfig(display); + EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr); + EGLContext context = eglCreateContext(display, config, nullptr, nullptr); + EGLint w, h; eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -380,9 +459,47 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; + // Register a display event receiver + mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>(); + status_t status = mDisplayEventReceiver->initCheck(); + SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d", + status); + mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT, + new DisplayEventCallback(this), nullptr); + return NO_ERROR; } +void BootAnimation::resizeSurface(int newWidth, int newHeight) { + // We assume this function is called on the animation thread. + if (newWidth == mWidth && newHeight == mHeight) { + return; + } + SLOGV("Resizing the boot animation surface to %d %d", newWidth, newHeight); + + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(mDisplay, mSurface); + + const auto limitedSize = limitSurfaceSize(newWidth, newHeight); + mWidth = limitedSize.width; + mHeight = limitedSize.height; + + SurfaceComposerClient::Transaction t; + t.setSize(mFlingerSurfaceControl, mWidth, mHeight); + t.apply(); + + EGLConfig config = getEglConfig(mDisplay); + EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr); + if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) { + SLOGE("Can't make the new surface current. Error %d", eglGetError()); + return; + } + glViewport(0, 0, mWidth, mHeight); + glScissor(0, 0, mWidth, mHeight); + + mSurface = surface; +} + bool BootAnimation::preloadAnimation() { findBootAnimationFile(); if (!mZipFileName.isEmpty()) { @@ -443,15 +560,14 @@ void BootAnimation::findBootAnimationFile() { } } -bool BootAnimation::threadLoop() -{ - bool r; +bool BootAnimation::threadLoop() { + bool result; // We have no bootanimation file, so we use the stock android logo // animation. if (mZipFileName.isEmpty()) { - r = android(); + result = android(); } else { - r = movie(); + result = movie(); } eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -462,11 +578,10 @@ bool BootAnimation::threadLoop() eglTerminate(mDisplay); eglReleaseThread(); IPCThreadState::self()->stopProcess(); - return r; + return result; } -bool BootAnimation::android() -{ +bool BootAnimation::android() { SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); @@ -485,19 +600,19 @@ bool BootAnimation::android() glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - const GLint xc = (mWidth - mAndroid[0].w) / 2; - const GLint yc = (mHeight - mAndroid[0].h) / 2; - const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); - - glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), - updateRect.height()); - // Blend state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const nsecs_t startTime = systemTime(); do { + processDisplayEvents(); + const GLint xc = (mWidth - mAndroid[0].w) / 2; + const GLint yc = (mHeight - mAndroid[0].h) / 2; + const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); + glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), + updateRect.height()); + nsecs_t now = systemTime(); double time = now - startTime; float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w; @@ -612,8 +727,7 @@ static bool parseColor(const char str[7], float color[3]) { } -static bool readFile(ZipFileRO* zip, const char* name, String8& outString) -{ +static bool readFile(ZipFileRO* zip, const char* name, String8& outString) { ZipEntryRO entry = zip->findEntryByName(name); SLOGE_IF(!entry, "couldn't find %s", name); if (!entry) { @@ -734,8 +848,7 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) drawText(out, font, false, &x, &y); } -bool BootAnimation::parseAnimationDesc(Animation& animation) -{ +bool BootAnimation::parseAnimationDesc(Animation& animation) { String8 desString; if (!readFile(animation.zip, "desc.txt", desString)) { @@ -802,8 +915,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) return true; } -bool BootAnimation::preloadZip(Animation& animation) -{ +bool BootAnimation::preloadZip(Animation& animation) { // read all the data structures const size_t pcount = animation.parts.size(); void *cookie = nullptr; @@ -900,8 +1012,7 @@ bool BootAnimation::preloadZip(Animation& animation) return true; } -bool BootAnimation::movie() -{ +bool BootAnimation::movie() { if (mAnimation == nullptr) { mAnimation = loadAnimation(mZipFileName); } @@ -987,12 +1098,9 @@ bool BootAnimation::movie() return false; } -bool BootAnimation::playAnimation(const Animation& animation) -{ +bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; - const int animationX = (mWidth - animation.width) / 2; - const int animationY = (mHeight - animation.height) / 2; SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); @@ -1023,6 +1131,11 @@ bool BootAnimation::playAnimation(const Animation& animation) 1.0f); for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { + processDisplayEvents(); + + const int animationX = (mWidth - animation.width) / 2; + const int animationY = (mHeight - animation.height) / 2; + const Animation::Frame& frame(part.frames[j]); nsecs_t lastFrame = systemTime(); @@ -1106,6 +1219,12 @@ bool BootAnimation::playAnimation(const Animation& animation) return true; } +void BootAnimation::processDisplayEvents() { + // This will poll mDisplayEventReceiver and if there are new events it'll call + // displayEventCallback synchronously. + mLooper->pollOnce(0); +} + void BootAnimation::handleViewport(nsecs_t timestep) { if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) { return; @@ -1148,8 +1267,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) { mCurrentInset += delta; } -void BootAnimation::releaseAnimation(Animation* animation) const -{ +void BootAnimation::releaseAnimation(Animation* animation) const { for (Vector<Animation::Part>::iterator it = animation->parts.begin(), e = animation->parts.end(); it != e; ++it) { if (it->animation) @@ -1160,8 +1278,7 @@ void BootAnimation::releaseAnimation(Animation* animation) const delete animation; } -BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) -{ +BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) { if (mLoadedFiles.indexOf(fn) >= 0) { SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed", fn.string()); @@ -1323,5 +1440,4 @@ status_t BootAnimation::TimeCheckThread::readyToRun() { // --------------------------------------------------------------------------- -} -; // namespace android +} // namespace android diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 574d65e425cf..6ba7fd450fbb 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -18,11 +18,14 @@ #define ANDROID_BOOTANIMATION_H #include <vector> +#include <queue> #include <stdint.h> #include <sys/types.h> #include <androidfw/AssetManager.h> +#include <gui/DisplayEventReceiver.h> +#include <utils/Looper.h> #include <utils/Thread.h> #include <binder/IBinder.h> @@ -145,6 +148,11 @@ private: BootAnimation* mBootAnimation; }; + // Display event handling + class DisplayEventCallback; + int displayEventCallback(int fd, int events, void* data); + void processDisplayEvents(); + status_t initTexture(Texture* texture, AssetManager& asset, const char* name); status_t initTexture(FileMap* map, int* width, int* height); status_t initFont(Font* font, const char* fallback); @@ -161,6 +169,9 @@ private: void findBootAnimationFile(); bool findBootAnimationFileInternal(const std::vector<std::string>& files); bool preloadAnimation(); + EGLConfig getEglConfig(const EGLDisplay&); + ui::Size limitSurfaceSize(int width, int height) const; + void resizeSurface(int newWidth, int newHeight); void checkExit(); @@ -171,6 +182,8 @@ private: Texture mAndroid[2]; int mWidth; int mHeight; + int mMaxWidth = 0; + int mMaxHeight = 0; int mCurrentInset; int mTargetInset; bool mUseNpotTextures = false; @@ -189,6 +202,8 @@ private: sp<TimeCheckThread> mTimeCheckThread = nullptr; sp<Callbacks> mCallbacks; Animation* mAnimation = nullptr; + std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver; + sp<Looper> mLooper; }; // --------------------------------------------------------------------------- diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index ef5c4cec9166..878cef94b674 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -23,9 +23,16 @@ cc_defaults { "misc-*", "readability-*", ], + tidy_checks_as_errors: [ + "modernize-*", + "-modernize-avoid-c-arrays", + "-modernize-use-trailing-return-type", + "android-*", + "misc-*", + "readability-*", + ], tidy_flags: [ "-system-headers", - "-warnings-as-errors=*", ], } @@ -168,13 +175,13 @@ cc_binary { ], host_supported: true, srcs: [ + "idmap2/CommandUtils.cpp", "idmap2/Create.cpp", "idmap2/CreateMultiple.cpp", "idmap2/Dump.cpp", "idmap2/Lookup.cpp", "idmap2/Main.cpp", "idmap2/Scan.cpp", - "idmap2/Verify.cpp", ], target: { android: { diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp index 9cb67b33e6cf..8f5845bf2e53 100644 --- a/cmds/idmap2/idmap2/Verify.cpp +++ b/cmds/idmap2/idmap2/CommandUtils.cpp @@ -19,30 +19,19 @@ #include <string> #include <vector> -#include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::IdmapHeader; using android::idmap2::Result; using android::idmap2::Unit; -Result<Unit> Verify(const std::vector<std::string>& args) { - SYSTRACE << "Verify " << args; - std::string idmap_path; - - const CommandLineOptions opts = - CommandLineOptions("idmap2 verify") - .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path); - - const auto opts_ok = opts.Parse(args); - if (!opts_ok) { - return opts_ok.GetError(); - } - +Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path, + const std::string& overlay_path, PolicyBitmask fulfilled_policies, + bool enforce_overlayable) { + SYSTRACE << "Verify " << idmap_path; std::ifstream fin(idmap_path); const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin); fin.close(); @@ -50,7 +39,8 @@ Result<Unit> Verify(const std::vector<std::string>& args) { return Error("failed to parse idmap header"); } - const auto header_ok = header->IsUpToDate(); + const auto header_ok = header->IsUpToDate(target_path.c_str(), overlay_path.c_str(), + fulfilled_policies, enforce_overlayable); if (!header_ok) { return Error(header_ok.GetError(), "idmap not up to date"); } diff --git a/cmds/idmap2/idmap2/CommandUtils.h b/cmds/idmap2/idmap2/CommandUtils.h new file mode 100644 index 000000000000..e717e046d15d --- /dev/null +++ b/cmds/idmap2/idmap2/CommandUtils.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_IDMAP2_COMMAND_UTILS_H_ +#define IDMAP2_IDMAP2_COMMAND_UTILS_H_ + +#include "idmap2/PolicyUtils.h" +#include "idmap2/Result.h" + +android::idmap2::Result<android::idmap2::Unit> Verify(const std::string& idmap_path, + const std::string& target_path, + const std::string& overlay_path, + PolicyBitmask fulfilled_policies, + bool enforce_overlayable); + +#endif // IDMAP2_IDMAP2_COMMAND_UTILS_H_ diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h index e626738a2895..69eea8d262d2 100644 --- a/cmds/idmap2/idmap2/Commands.h +++ b/cmds/idmap2/idmap2/Commands.h @@ -27,6 +27,5 @@ android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector< android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args); android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args); android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args); -android::idmap2::Result<android::idmap2::Unit> Verify(const std::vector<std::string>& args); #endif // IDMAP2_IDMAP2_COMMANDS_H_ diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp index 4b70acc2969c..abdfaf4dccab 100644 --- a/cmds/idmap2/idmap2/CreateMultiple.cpp +++ b/cmds/idmap2/idmap2/CreateMultiple.cpp @@ -26,6 +26,7 @@ #include "android-base/stringprintf.h" #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/CommandLineOptions.h" +#include "idmap2/CommandUtils.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" @@ -103,7 +104,8 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { continue; } - if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}))) { + if (!Verify(idmap_path, target_apk_path, overlay_apk_path, fulfilled_policies, + !ignore_overlayable)) { const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); if (!overlay_apk) { LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp index a07e793d9f47..fb093f0f22a4 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -53,9 +53,8 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { int main(int argc, char** argv) { SYSTRACE << "main"; const NameToFunctionMap commands = { - {"create", Create}, {"create-multiple", CreateMultiple}, - {"dump", Dump}, {"lookup", Lookup}, - {"scan", Scan}, {"verify", Verify}, + {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup}, + {"scan", Scan}, }; if (argc <= 1) { PrintUsage(commands, std::cerr); diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index da0453216f03..36250450cc74 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -27,8 +27,11 @@ #include "Commands.h" #include "android-base/properties.h" #include "idmap2/CommandLineOptions.h" +#include "idmap2/CommandUtils.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/PolicyUtils.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" @@ -48,6 +51,7 @@ using android::idmap2::policy::kPolicyVendor; using android::idmap2::utils::ExtractOverlayManifestInfo; using android::idmap2::utils::FindFiles; using android::idmap2::utils::OverlayManifestInfo; +using android::idmap2::utils::PoliciesToBitmaskResult; using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; @@ -215,7 +219,15 @@ Result<Unit> Scan(const std::vector<std::string>& args) { std::stringstream stream; for (const auto& overlay : interesting_apks) { - if (!Verify(std::vector<std::string>({"--idmap-path", overlay.idmap_path}))) { + const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies); + if (!policy_bitmask) { + LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path + << "\": " << policy_bitmask.GetErrorMessage(); + continue; + } + + if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask, + !overlay.ignore_overlayable)) { std::vector<std::string> create_args = {"--target-apk-path", target_apk_path, "--overlay-apk-path", overlay.apk_path, "--idmap-path", overlay.idmap_path}; diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index a93184ff4787..f95b73f17222 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -33,16 +33,19 @@ #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" +#include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2/ZipFile.h" #include "utils/String8.h" using android::IPCThreadState; +using android::base::StringPrintf; using android::binder::Status; using android::idmap2::BinaryStreamVisitor; using android::idmap2::GetPackageCrc; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; +using android::idmap2::ZipFile; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::UidHasWriteAccessToPath; @@ -66,6 +69,21 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { return static_cast<PolicyBitmask>(arg); } +Status GetCrc(const std::string& apk_path, uint32_t* out_crc) { + const auto zip = ZipFile::Open(apk_path); + if (!zip) { + return error(StringPrintf("failed to open apk %s", apk_path.c_str())); + } + + const auto crc = GetPackageCrc(*zip); + if (!crc) { + return error(crc.GetErrorMessage()); + } + + *out_crc = *crc; + return ok(); +} + } // namespace namespace android::os { @@ -98,12 +116,12 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, } Status Idmap2Service::verifyIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, - int32_t fulfilled_policies ATTRIBUTE_UNUSED, - bool enforce_overlayable ATTRIBUTE_UNUSED, - int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { + const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, + bool* _aidl_return) { SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path; assert(_aidl_return); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); std::ifstream fin(idmap_path); const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin); @@ -113,35 +131,36 @@ Status Idmap2Service::verifyIdmap(const std::string& target_apk_path, return error("failed to parse idmap header"); } - if (strcmp(header->GetTargetPath().data(), target_apk_path.data()) != 0) { - *_aidl_return = false; - return ok(); - } - - if (target_apk_path != kFrameworkPath) { - *_aidl_return = (bool) header->IsUpToDate(); + uint32_t target_crc; + if (target_apk_path == kFrameworkPath && android_crc_) { + target_crc = *android_crc_; } else { - if (!android_crc_) { - // Loading the framework zip can take several milliseconds. Cache the crc of the framework - // resource APK to reduce repeated work during boot. - const auto target_zip = idmap2::ZipFile::Open(target_apk_path); - if (!target_zip) { - return error(base::StringPrintf("failed to open target %s", target_apk_path.c_str())); - } - - const auto target_crc = GetPackageCrc(*target_zip); - if (!target_crc) { - return error(target_crc.GetErrorMessage()); - } - - android_crc_ = *target_crc; + auto target_crc_status = GetCrc(target_apk_path, &target_crc); + if (!target_crc_status.isOk()) { + *_aidl_return = false; + return target_crc_status; + } + + // Loading the framework zip can take several milliseconds. Cache the crc of the framework + // resource APK to reduce repeated work during boot. + if (target_apk_path == kFrameworkPath) { + android_crc_ = target_crc; } + } - *_aidl_return = (bool) header->IsUpToDate(android_crc_.value()); + uint32_t overlay_crc; + auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc); + if (!overlay_crc_status.isOk()) { + *_aidl_return = false; + return overlay_crc_status; } - // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed - return ok(); + auto up_to_date = + header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), target_crc, overlay_crc, + ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable); + + *_aidl_return = static_cast<bool>(up_to_date); + return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage()); } Status Idmap2Service::createIdmap(const std::string& target_apk_path, diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 77a7b30a230e..0f05592b70f3 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -117,6 +117,14 @@ class IdmapHeader { return overlay_crc_; } + inline uint32_t GetFulfilledPolicies() const { + return fulfilled_policies_; + } + + bool GetEnforceOverlayable() const { + return enforce_overlayable_; + } + inline StringPiece GetTargetPath() const { return StringPiece(target_path_); } @@ -132,8 +140,11 @@ class IdmapHeader { // Invariant: anytime the idmap data encoding is changed, the idmap version // field *must* be incremented. Because of this, we know that if the idmap // header is up-to-date the entire file is up-to-date. - Result<Unit> IsUpToDate() const; - Result<Unit> IsUpToDate(uint32_t target_crc_) const; + Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, + PolicyBitmask fulfilled_policies, bool enforce_overlayable) const; + Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc, + uint32_t overlay_crc, PolicyBitmask fulfilled_policies, + bool enforce_overlayable) const; void accept(Visitor* v) const; @@ -145,6 +156,8 @@ class IdmapHeader { uint32_t version_; uint32_t target_crc_; uint32_t overlay_crc_; + uint32_t fulfilled_policies_; + bool enforce_overlayable_; char target_path_[kIdmapStringLength]; char overlay_path_[kIdmapStringLength]; std::string debug_info_; diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index 362dcb36007a..255212ad4c66 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -66,6 +66,8 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { Write32(header.GetVersion()); Write32(header.GetTargetCrc()); Write32(header.GetOverlayCrc()); + Write32(header.GetFulfilledPolicies()); + Write8(static_cast<uint8_t>(header.GetEnforceOverlayable())); WriteString256(header.GetTargetPath()); WriteString256(header.GetOverlayPath()); WriteString(header.GetDebugInfo()); diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 706b842b3b47..23c25a7089de 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -112,14 +112,17 @@ Result<uint32_t> GetPackageCrc(const ZipFile& zip) { std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader()); - + uint8_t enforce_overlayable; if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) || !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) || + !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) || !ReadString256(stream, idmap_header->overlay_path_)) { return nullptr; } + idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable); + auto debug_str = ReadString(stream); if (!debug_str) { return nullptr; @@ -129,21 +132,37 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s return std::move(idmap_header); } -Result<Unit> IdmapHeader::IsUpToDate() const { - const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_); +Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path, + PolicyBitmask fulfilled_policies, + bool enforce_overlayable) const { + const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path); if (!target_zip) { - return Error("failed to open target %s", GetTargetPath().to_string().c_str()); + return Error("failed to open target %s", target_path); } - Result<uint32_t> target_crc = GetPackageCrc(*target_zip); + const Result<uint32_t> target_crc = GetPackageCrc(*target_zip); if (!target_crc) { return Error("failed to get target crc"); } - return IsUpToDate(*target_crc); + const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path); + if (!overlay_zip) { + return Error("failed to overlay target %s", overlay_path); + } + + const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip); + if (!overlay_crc) { + return Error("failed to get overlay crc"); + } + + return IsUpToDate(target_path, overlay_path, *target_crc, *overlay_crc, fulfilled_policies, + enforce_overlayable); } -Result<Unit> IdmapHeader::IsUpToDate(uint32_t target_crc) const { +Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path, + uint32_t target_crc, uint32_t overlay_crc, + PolicyBitmask fulfilled_policies, + bool enforce_overlayable) const { if (magic_ != kIdmapMagic) { return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic); } @@ -157,19 +176,29 @@ Result<Unit> IdmapHeader::IsUpToDate(uint32_t target_crc) const { target_crc); } - const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_); - if (!overlay_zip) { - return Error("failed to open overlay %s", GetOverlayPath().to_string().c_str()); + if (overlay_crc_ != overlay_crc) { + return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_, + overlay_crc); } - Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip); - if (!overlay_crc) { - return Error("failed to get overlay crc"); + if (fulfilled_policies_ != fulfilled_policies) { + return Error("bad fulfilled policies: idmap version 0x%08x, file system version 0x%08x", + fulfilled_policies, fulfilled_policies_); } - if (overlay_crc_ != *overlay_crc) { - return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_, - *overlay_crc); + if (enforce_overlayable != enforce_overlayable_) { + return Error("bad enforce overlayable: idmap version %s, file system version %s", + enforce_overlayable ? "true" : "false", enforce_overlayable_ ? "true" : "false"); + } + + if (strcmp(target_path, target_path_) != 0) { + return Error("bad target path: idmap version %s, file system version %s", target_path, + target_path_); + } + + if (strcmp(overlay_path, overlay_path_) != 0) { + return Error("bad overlay path: idmap version %s, file system version %s", overlay_path, + overlay_path_); } return Unit{}; @@ -320,6 +349,9 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& targe } header->overlay_crc_ = *crc; + header->fulfilled_policies_ = fulfilled_policies; + header->enforce_overlayable_ = enforce_overlayable; + if (target_apk_path.size() > sizeof(header->target_path_)) { return Error("target apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(), sizeof(header->target_path_)); diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index 751c60c4add4..3f62a2ae2029 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -23,10 +23,12 @@ #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/ApkAssets.h" +#include "idmap2/PolicyUtils.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" using android::ApkAssets; +using android::idmap2::policy::PoliciesToDebugString; namespace { @@ -39,9 +41,6 @@ size_t StringSizeWhenEncoded(const std::string& s) { namespace android::idmap2 { -// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils -#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry)) - void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { } @@ -50,6 +49,9 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { print(header.GetVersion(), "version"); print(header.GetTargetCrc(), "target crc"); print(header.GetOverlayCrc(), "overlay crc"); + print(header.GetFulfilledPolicies(), "fulfilled policies: %s", + PoliciesToDebugString(header.GetFulfilledPolicies()).c_str()); + print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable"); print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path"); print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path"); print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info"); diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index f82c8f1af713..34589a1c39dc 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -291,13 +291,6 @@ Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_a const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, LogInfo& log_info) { - if (enforce_overlayable) { - log_info.Info(LogMessage() << "fulfilled_policies=" - << ConcatPolicies(BitmaskToPolicies(fulfilled_policies)) - << " enforce_overlayable=" - << (enforce_overlayable ? "true" : "false")); - } - AssetManager2 target_asset_manager; if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */, false /* filter_incompatible_configs*/)) { diff --git a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h index 4973b7638d10..5bd353af4ad3 100644 --- a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h +++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h @@ -21,9 +21,12 @@ #include <string> #include <vector> +#include "android-base/stringprintf.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +using android::base::StringPrintf; + using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; @@ -48,6 +51,29 @@ inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicySt {kPolicySystem, PolicyFlags::SYSTEM_PARTITION}, {kPolicyVendor, PolicyFlags::VENDOR_PARTITION}, }; + +inline static std::string PoliciesToDebugString(PolicyBitmask policies) { + std::string str; + uint32_t remaining = policies; + for (auto const& policy : kPolicyStringToFlag) { + if ((policies & policy.second) != policy.second) { + continue; + } + if (!str.empty()) { + str.append("|"); + } + str.append(policy.first.data()); + remaining &= ~policy.second; + } + if (remaining != 0) { + if (!str.empty()) { + str.append("|"); + } + str.append(StringPrintf("0x%08x", remaining)); + } + return !str.empty() ? str : "none"; +} + } // namespace android::idmap2::policy #endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index db4778c8ee09..5fea7bcdaac5 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -48,6 +48,11 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { ASSERT_TRUE(result2); const auto idmap2 = std::move(*result2); + ASSERT_EQ(idmap1->GetHeader()->GetFulfilledPolicies(), + idmap2->GetHeader()->GetFulfilledPolicies()); + ASSERT_EQ(idmap1->GetHeader()->GetEnforceOverlayable(), + idmap2->GetHeader()->GetEnforceOverlayable()); + ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath()); ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc()); ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath()); ASSERT_EQ(idmap1->GetData().size(), 1U); diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 87da36c01192..6fab5e0f8ae1 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -62,9 +62,11 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x03U); + ASSERT_EQ(header->GetVersion(), 0x04U); ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); + ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); + ASSERT_EQ(header->GetEnforceOverlayable(), true); ASSERT_EQ(header->GetTargetPath().to_string(), "targetX.apk"); ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk"); ASSERT_EQ(header->GetDebugInfo(), "debug"); @@ -73,7 +75,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len); // overwrite the target path string, including the terminating null, with '.' - for (size_t i = 0x10; i < 0x110; i++) { + for (size_t i = 0x15; i < 0x115; i++) { raw[i] = '.'; } std::istringstream stream(raw); @@ -82,7 +84,7 @@ TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { } TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { - const size_t offset = 0x21c; + const size_t offset = 0x221; std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), idmap_raw_data_len - offset); std::istringstream stream(raw); @@ -94,7 +96,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { } TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { - const size_t offset = 0x21c; + const size_t offset = 0x221; std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), idmap_raw_data_len - offset); std::istringstream stream(raw); @@ -128,9 +130,11 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); + ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11); + ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "targetX.apk"); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlayX.apk"); @@ -180,9 +184,11 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); + ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); + ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); } @@ -389,7 +395,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); - ASSERT_TRUE(header->IsUpToDate()); + ASSERT_TRUE(header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); // magic: bytes (0x0, 0x03) std::string bad_magic_string(stream.str()); @@ -402,7 +409,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { IdmapHeader::FromBinaryStream(bad_magic_stream); ASSERT_THAT(bad_magic_header, NotNull()); ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic()); - ASSERT_FALSE(bad_magic_header->IsUpToDate()); + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); // version: bytes (0x4, 0x07) std::string bad_version_string(stream.str()); @@ -415,7 +423,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { IdmapHeader::FromBinaryStream(bad_version_stream); ASSERT_THAT(bad_version_header, NotNull()); ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion()); - ASSERT_FALSE(bad_version_header->IsUpToDate()); + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); // target crc: bytes (0x8, 0xb) std::string bad_target_crc_string(stream.str()); @@ -428,7 +437,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { IdmapHeader::FromBinaryStream(bad_target_crc_stream); ASSERT_THAT(bad_target_crc_header, NotNull()); ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc()); - ASSERT_FALSE(bad_target_crc_header->IsUpToDate()); + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); // overlay crc: bytes (0xc, 0xf) std::string bad_overlay_crc_string(stream.str()); @@ -441,27 +451,55 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { IdmapHeader::FromBinaryStream(bad_overlay_crc_stream); ASSERT_THAT(bad_overlay_crc_header, NotNull()); ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc()); - ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate()); - - // target path: bytes (0x10, 0x10f) + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); + + // fulfilled policy: bytes (0x10, 0x13) + std::string bad_policy_string(stream.str()); + bad_policy_string[0x10] = '.'; + bad_policy_string[0x11] = '.'; + bad_policy_string[0x12] = '.'; + bad_policy_string[0x13] = '.'; + std::stringstream bad_policy_stream(bad_policy_string); + std::unique_ptr<const IdmapHeader> bad_policy_header = + IdmapHeader::FromBinaryStream(bad_policy_stream); + ASSERT_THAT(bad_policy_header, NotNull()); + ASSERT_NE(header->GetFulfilledPolicies(), bad_policy_header->GetFulfilledPolicies()); + ASSERT_FALSE(bad_policy_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); + + // enforce overlayable: bytes (0x14) + std::string bad_enforce_string(stream.str()); + bad_enforce_string[0x14] = '\0'; + std::stringstream bad_enforce_stream(bad_enforce_string); + std::unique_ptr<const IdmapHeader> bad_enforce_header = + IdmapHeader::FromBinaryStream(bad_enforce_stream); + ASSERT_THAT(bad_enforce_header, NotNull()); + ASSERT_NE(header->GetEnforceOverlayable(), bad_enforce_header->GetEnforceOverlayable()); + ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); + + // target path: bytes (0x15, 0x114) std::string bad_target_path_string(stream.str()); - bad_target_path_string[0x10] = '\0'; + bad_target_path_string[0x15] = '\0'; std::stringstream bad_target_path_stream(bad_target_path_string); std::unique_ptr<const IdmapHeader> bad_target_path_header = IdmapHeader::FromBinaryStream(bad_target_path_stream); ASSERT_THAT(bad_target_path_header, NotNull()); ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath()); - ASSERT_FALSE(bad_target_path_header->IsUpToDate()); + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); - // overlay path: bytes (0x110, 0x20f) + // overlay path: bytes (0x115, 0x214) std::string bad_overlay_path_string(stream.str()); - bad_overlay_path_string[0x110] = '\0'; + bad_overlay_path_string[0x115] = '\0'; std::stringstream bad_overlay_path_stream(bad_overlay_path_string); std::unique_ptr<const IdmapHeader> bad_overlay_path_header = IdmapHeader::FromBinaryStream(bad_overlay_path_stream); ASSERT_THAT(bad_overlay_path_header, NotNull()); ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath()); - ASSERT_FALSE(bad_overlay_path_header->IsUpToDate()); + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); } class TestVisitor : public Visitor { diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 5c5c81edee90..b268d5add141 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -43,6 +43,8 @@ namespace android::idmap2 { << str << "--------"; \ } while (0) +#define ADDRESS "[0-9a-f]{8}: " + TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { fclose(stderr); // silence expected warnings @@ -62,15 +64,16 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { RawPrintVisitor visitor(stream); (*idmap)->accept(&visitor); -#define ADDRESS "[0-9a-f]{8}: " ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000003 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING), stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str()); @@ -83,7 +86,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 value: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str()); -#undef ADDRESS } TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { @@ -99,22 +101,23 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { RawPrintVisitor visitor(stream); (*idmap)->accept(&visitor); - ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000004: 00000003 version\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000021c: 7f target package id\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000021d: 7f overlay package id\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000021e: 00000003 target entry count\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000222: 00000003 overlay entry count\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000226: 00000000 string pool index offset\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000022a: 00000000 string pool byte length\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000022e: 7f020000 target id\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000232: 01 type: reference\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000233: 7f020000 value\n"), std::string::npos); - - ASSERT_NE(stream.str().find("00000249: 7f020000 overlay id\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000024d: 7f020000 target id\n"), std::string::npos); + ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool byte length\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 01 type: reference\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 value\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index e899589c7e61..b599dcb0069a 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = { 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -38,7 +38,13 @@ const unsigned char idmap_raw_data[] = { // 0xc: overlay crc 0x78, 0x56, 0x00, 0x00, - // 0x10: target path "targetX.apk" + // 0x10: fulfilled policies + 0x11, 0x00, 0x00, 0x00, + + // 0x14: enforce overlayable + 0x01, + + // 0x15: target path "targetX.apk" 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -56,7 +62,7 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x110: overlay path "overlayX.apk" + // 0x115: overlay path "overlayX.apk" 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -74,7 +80,7 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x210: debug string + // 0x215: debug string // string length, including terminating null 0x08, 0x00, 0x00, 0x00, @@ -82,63 +88,63 @@ const unsigned char idmap_raw_data[] = { 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, // DATA HEADER - // 0x21c: target_package_id + // 0x221: target_package_id 0x7f, - // 0x21d: overlay_package_id + // 0x222: overlay_package_id 0x7f, - // 0x21e: target_entry_count + // 0x223: target_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x222: overlay_entry_count + // 0x227: overlay_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x226: string_pool_offset + // 0x22b: string_pool_offset 0x00, 0x00, 0x00, 0x00, - // 0x22a: string_pool_byte_length + // 0x22f: string_pool_byte_length 0x00, 0x00, 0x00, 0x00, // TARGET ENTRIES - // 0x22e: 0x7f020000 + // 0x233: 0x7f020000 0x00, 0x00, 0x02, 0x7f, - // 0x232: TYPE_REFERENCE + // 0x237: TYPE_REFERENCE 0x01, - // 0x233: 0x7f020000 + // 0x238: 0x7f020000 0x00, 0x00, 0x02, 0x7f, - // 0x237: 0x7f030000 + // 0x23c: 0x7f030000 0x00, 0x00, 0x03, 0x7f, - // 0x23b: TYPE_REFERENCE + // 0x240: TYPE_REFERENCE 0x01, - // 0x23c: 0x7f030000 + // 0x241: 0x7f030000 0x00, 0x00, 0x03, 0x7f, - // 0x240: 0x7f030002 + // 0x245: 0x7f030002 0x02, 0x00, 0x03, 0x7f, - // 0x244: TYPE_REFERENCE + // 0x249: TYPE_REFERENCE 0x01, - // 0x245: 0x7f030001 + // 0x24a: 0x7f030001 0x01, 0x00, 0x03, 0x7f, // OVERLAY ENTRIES - // 0x249: 0x7f020000 -> 0x7f020000 + // 0x24e: 0x7f020000 -> 0x7f020000 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - // 0x251: 0x7f030000 -> 0x7f030000 + // 0x256: 0x7f030000 -> 0x7f030000 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - // 0x259: 0x7f030001 -> 0x7f030002 + // 0x25e: 0x7f030001 -> 0x7f030002 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f}; -const unsigned int idmap_raw_data_len = 0x261; +const unsigned int idmap_raw_data_len = 0x266; std::string GetTestDataPath(); diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 61e5eb07130c..33e764988b75 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -477,14 +477,15 @@ status_t TextDumpsysSection::Execute(ReportWriter* writer) const { // Run dumping thread const uint64_t start = Nanotime(); - std::thread worker([&]() { + std::thread worker([write_fd = std::move(dumpPipe.writeFd()), service = std::move(service), + this]() mutable { // Don't crash the service if writing to a closed pipe (may happen if dumping times out) signal(SIGPIPE, sigpipe_handler); - status_t err = service->dump(dumpPipe.writeFd().get(), mArgs); + status_t err = service->dump(write_fd.get(), this->mArgs); if (err != OK) { ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err)); } - dumpPipe.writeFd().reset(); + write_fd.reset(); }); // Collect dump content diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index f30ed17c392f..0617eb6c0e66 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -116,7 +116,6 @@ cc_defaults { "libcutils", "libgtest_prod", "libprotoutil", - "libstatsmetadata", "libstatslog_statsd", "libsysutils", "libutils", @@ -129,51 +128,6 @@ cc_defaults { ], } -// ================ -// libstatsmetadata -// ================ - -genrule { - name: "atoms_info.h", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --atomsInfoHeader $(genDir)/atoms_info.h", - out: [ - "atoms_info.h", - ], -} - -genrule { - name: "atoms_info.cpp", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --atomsInfoCpp $(genDir)/atoms_info.cpp", - out: [ - "atoms_info.cpp", - ], -} - -cc_library_static { - name: "libstatsmetadata", - host_supported: true, - generated_sources: [ - "atoms_info.cpp", - ], - generated_headers: [ - "atoms_info.h", - ], - cflags: [ - "-Wall", - "-Werror", - ], - export_generated_headers: [ - "atoms_info.h", - ], - apex_available: [ - //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata - "com.android.os.statsd", - "test_com.android.os.statsd", - ], -} - genrule { name: "statslog_statsd.h", tools: ["stats-log-api-gen"], @@ -292,6 +246,7 @@ cc_test { name: "statsd_test", defaults: ["statsd_defaults"], test_suites: ["device-tests", "mts"], + test_config: "statsd_test.xml", //TODO(b/153588990): Remove when the build system properly separates //32bit and 64bit architectures. @@ -299,7 +254,10 @@ cc_test { multilib: { lib64: { suffix: "64", - } + }, + lib32: { + suffix: "32", + }, }, cflags: [ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 60e259be9558..e7b32c56551a 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -101,6 +101,7 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, mLargestTimestampSeen(0), mLastTimestampSeen(0) { mPullerManager->ForceClearPullerCache(); + StateManager::getInstance().updateLogSources(uidMap); } StatsLogProcessor::~StatsLogProcessor() { @@ -419,6 +420,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { // The field numbers need to be currently updated by hand with atoms.proto if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) { onIsolatedUidChangedEventLocked(*event); + } else { + // Map the isolated uid to host uid if necessary. + mapIsolatedUidToHostUidIfNecessaryLocked(event); } StateManager::getInstance().onLogEvent(*event); @@ -433,12 +437,6 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { mLastPullerCacheClearTimeSec = curTimeSec; } - - if (atomId != android::os::statsd::util::ISOLATED_UID_CHANGED) { - // Map the isolated uid to host uid if necessary. - mapIsolatedUidToHostUidIfNecessaryLocked(event); - } - std::unordered_set<int> uidsWithActiveConfigsChanged; std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid; // pass the event to metrics managers. @@ -531,7 +529,9 @@ void StatsLogProcessor::OnConfigUpdatedLocked( VLOG("StatsdConfig valid"); } else { // If there is any error in the config, don't use it. + // Remove any existing config with the same key. ALOGE("StatsdConfig NOT valid"); + mMetricsManagers.erase(key); } } @@ -1054,6 +1054,7 @@ void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const strin const int uid, const int64_t version) { std::lock_guard<std::mutex> lock(mMetricsMutex); VLOG("Received app upgrade"); + StateManager::getInstance().notifyAppChanged(apk, mUidMap); for (const auto& it : mMetricsManagers) { it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); } @@ -1063,6 +1064,7 @@ void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const strin const int uid) { std::lock_guard<std::mutex> lock(mMetricsMutex); VLOG("Received app removed"); + StateManager::getInstance().notifyAppChanged(apk, mUidMap); for (const auto& it : mMetricsManagers) { it.second->notifyAppRemoved(eventTimeNs, apk, uid); } @@ -1071,6 +1073,7 @@ void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const strin void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); VLOG("Received uid map"); + StateManager::getInstance().updateLogSources(mUidMap); for (const auto& it : mMetricsManagers) { it.second->onUidMapReceived(eventTimeNs); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 7090bd46635d..23f2584655b0 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -284,6 +284,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); + FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 4ffa040fafd4..6f952f637506 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -267,8 +267,11 @@ void StatsService::dumpIncidentSection(int out) { for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) { uint64_t reportsListToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST); + // Don't include the current bucket to avoid skipping buckets. + // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS + // or other alternatives to avoid skipping buckets for pulled metrics. mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), - true /* includeCurrentBucket */, false /* erase_data */, + false /* includeCurrentBucket */, false /* erase_data */, ADB_DUMP, FAST, &proto); @@ -1298,7 +1301,6 @@ Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experiment return Status::ok(); } - void StatsService::statsCompanionServiceDied(void* cookie) { auto thiz = static_cast<StatsService*>(cookie); thiz->statsCompanionServiceDiedImpl(); diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 5722f923d11e..6d9beb8f718d 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -60,11 +60,11 @@ void AlarmTracker::addSubscription(const Subscription& subscription) { } int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) { - if (currentTimeSec <= mAlarmSec) { + if (currentTimeSec < mAlarmSec) { return mAlarmSec; } int64_t periodsForward = - ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1; + ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1; return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC; } diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index 8527185d3891..ff5717e4fa78 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -110,8 +110,6 @@ extend google.protobuf.FieldOptions { optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC]; - optional bool allow_from_any_uid = 50003 [default = false]; - repeated string module = 50004; optional bool truncate_timestamp = 50005 [default = false]; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c6e9e01cc0d7..7a016522d597 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -43,6 +43,7 @@ import "frameworks/base/core/proto/android/server/location/enums.proto"; import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto"; +import "frameworks/base/core/proto/android/stats/connectivity/tethering.proto"; import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; @@ -144,8 +145,7 @@ message Atom { PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"]; WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"]; AnomalyDetected anomaly_detected = 46 [(module) = "statsd"]; - AppBreadcrumbReported app_breadcrumb_reported = - 47 [(allow_from_any_uid) = true, (module) = "statsd"]; + AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"]; AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"]; AppStartCanceled app_start_canceled = 49 [(module) = "framework"]; AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"]; @@ -156,7 +156,7 @@ message Atom { AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"]; ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"]; BootSequenceReported boot_sequence_reported = 57; - DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"]; + DaveyOccurred davey_occurred = 58 [(module) = "statsd"]; OverlayStateChanged overlay_state_changed = 59 [(module) = "framework", (module) = "statsdtest"]; ForegroundServiceStateChanged foreground_service_state_changed @@ -185,15 +185,14 @@ message Atom { WTFOccurred wtf_occurred = 80 [(module) = "framework"]; LowMemReported low_mem_reported = 81 [(module) = "framework"]; GenericAtom generic_atom = 82; - KeyValuePairsAtom key_value_pairs_atom = - 83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"]; + KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"]; VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"]; DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"]; ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true]; BiometricAcquired biometric_acquired = 87 [(module) = "framework"]; BiometricAuthenticated biometric_authenticated = 88 [(module) = "framework"]; BiometricErrorOccurred biometric_error_occurred = 89 [(module) = "framework"]; - UiEventReported ui_event_reported = 90 [(module) = "framework"]; + UiEventReported ui_event_reported = 90 [(module) = "framework", (module) = "sysui"]; BatteryHealthSnapshot battery_health_snapshot = 91; SlowIo slow_io = 92; BatteryCausedShutdown battery_caused_shutdown = 93; @@ -316,7 +315,7 @@ message Atom { AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"]; AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"]; TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"]; - HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true, (module) = "framework"]; + HiddenApiUsed hidden_api_used = 178 [(module) = "framework"]; StyleUIChanged style_ui_changed = 179 [(module) = "sysui"]; PrivacyIndicatorsInteracted privacy_indicators_interacted = 180 [(module) = "permissioncontroller"]; @@ -382,7 +381,7 @@ message Atom { UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226; CameraActionEvent camera_action_event = 227 [(module) = "framework"]; AppCompatibilityChangeReported app_compatibility_change_reported = - 228 [(allow_from_any_uid) = true, (module) = "framework"]; + 228 [(module) = "framework"]; PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"]; VmsClientConnectionStateChanged vms_client_connection_state_changed = 230 [(module) = "car"]; @@ -419,7 +418,7 @@ message Atom { DisplayJankReported display_jank_reported = 257; AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"]; SharesheetStarted sharesheet_started = 259 [(module) = "framework"]; - RankingSelected ranking_selected = 260 [(module) = "framework"]; + RankingSelected ranking_selected = 260 [(module) = "framework", (module) = "sysui"]; TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"]; LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"]; PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"]; @@ -444,7 +443,48 @@ message Atom { TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"]; MediaOutputOpSwitchReported mediaoutput_op_switch_reported = 277 [(module) = "settings"]; - SdkExtensionStatus sdk_extension_status = 354; + CellBroadcastMessageFiltered cb_message_filtered = + 278 [(module) = "cellbroadcast"]; + TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"]; + TvCasSessionOpenStatus tv_cas_session_open_status = + 280 [(module) = "framework"]; + AssistantInvocationReported assistant_invocation_reported = 281 [(module) = "framework"]; + DisplayWakeReported display_wake_reported = 282 [(module) = "framework"]; + CarUserHalModifyUserRequestReported car_user_hal_modify_user_request_reported = + 283 [(module) = "car"]; + CarUserHalModifyUserResponseReported car_user_hal_modify_user_response_reported = + 284 [(module) = "car"]; + CarUserHalPostSwitchResponseReported car_user_hal_post_switch_response_reported = + 285 [(module) = "car"]; + CarUserHalInitialUserInfoRequestReported car_user_hal_initial_user_info_request_reported = + 286 [(module) = "car"]; + CarUserHalInitialUserInfoResponseReported car_user_hal_initial_user_info_response_reported = + 287 [(module) = "car"]; + CarUserHalUserAssociationRequestReported car_user_hal_user_association_request_reported = + 288 [(module) = "car"]; + CarUserHalSetUserAssociationResponseReported car_user_hal_set_user_association_response_reported = + 289 [(module) = "car"]; + NetworkIpProvisioningReported network_ip_provisioning_reported = + 290 [(module) = "network_stack"]; + NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"]; + NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"]; + NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"]; + MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported = + 294; + MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported = + 295; + MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported = + 296; + MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported = + 297; + BlobCommitted blob_committed = 298 [(module) = "framework"]; + BlobLeased blob_leased = 299 [(module) = "framework"]; + BlobOpened blob_opened = 300 [(module) = "framework"]; + ContactsProviderStatusReported contacts_provider_status_reported = 301; + KeystoreKeyEventReported keystore_key_event_reported = 302; + NetworkTetheringReported network_tethering_reported = + 303 [(module) = "network_tethering"]; + ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -542,10 +582,13 @@ message Atom { SimSlotState sim_slot_state = 10078 [(module) = "telephony"]; SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; SettingSnapshot setting_snapshot = 10080 [(module) = "framework"]; - DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"]; + BlobInfo blob_info = 10081 [(module) = "framework"]; DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"]; BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered = 10083 [(module) = "framework"]; + DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"]; + GeneralExternalStorageAccessStats general_external_storage_access_stats = + 10085 [(module) = "mediaprovider"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3019,6 +3062,18 @@ message ExclusionRectStateChanged { } /** + * Logs when IME is on. + * + * Logged from: /packages/SystemUI/src/com/android/systemui/ + statusbar/phone/NavigationBarView.java + * + */ +message ImeTouchReported { + optional int32 x_coordinate = 1; // X coordinate for ACTION_DOWN event. + optional int32 y_coordinate = 2; // Y coordinate for ACTION_DOWN event. +} + +/** * Logs when Launcher (HomeScreen) UI has changed or was interacted. * * Logged from: @@ -4500,6 +4555,31 @@ message VmsClientConnectionStateChanged { optional State state = 2; } +message MimeTypes { + repeated string mime_types = 1; +} + +/** + * Logs statistics regarding accesses to external storage. + * All stats are normalized for one day period. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java + */ +message GeneralExternalStorageAccessStats { + optional int32 uid = 1 [(is_uid) = true]; + // Total number of accesses like creation, open, delete and rename/update. + // Includes file path and ContentResolver accesses + optional uint32 total_accesses = 2; + // Number of file path accesses, as opposed to file path and ContentResolver. + optional uint32 file_path_accesses = 3; + // Number of accesses on secondary volumes like SD cards. + // Includes file path and ContentResolver accesses + optional uint32 secondary_storage_accesses = 4; + // Comma-separated list of mime types that were accessed. + optional MimeTypes mime_types_accessed = 5; +} + /** * Logs when MediaProvider has successfully finished scanning a storage volume. * @@ -4861,6 +4941,106 @@ message SnapshotMergeReported { // Number of reboots that occurred after issuing and before completing the // merge of all the snapshot devices. optional int32 intermediate_reboots = 3; + + // The device has been upgraded to Virtual A/B. + optional bool is_vab_retrofit = 4; + + // Space that has been temporarily allocated in the /data partition + // containing the dm-snapshot's copy-on-write data generated during a + // Virtual A/B update. + optional int64 cow_file_size_bytes = 5; +} + +/** + * Event representing when BlobStoreManager.Session#commit() is called + * + * Logged from: + * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java + */ +message BlobCommitted { + // Uid of the Blob committer + optional int32 uid = 1 [(is_uid) = true]; + + // Id of the Blob committed + optional int64 blob_id = 2; + + // Size of the Blob + optional int64 size = 3; + + enum Result { + UNKNOWN = 0; + // Commit Succeeded + SUCCESS = 1; + // Commit Failed: Error occurred during commit + ERROR_DURING_COMMIT = 2; + // Commit Failed: Digest of the data did not match Blob digest + DIGEST_MISMATCH = 3; + // Commit Failed: Allowed count limit exceeded + COUNT_LIMIT_EXCEEDED = 4; + } + optional Result result = 4; +} + +/** + * Event representing when BlobStoreManager#acquireLease() is called + * + * Logged from: + * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java + */ +message BlobLeased{ + // Uid of the Blob leasee + optional int32 uid = 1 [(is_uid) = true]; + + // Id of the Blob leased or 0 if the Blob does not exist + optional int64 blob_id = 2; + + // Size of the Blob or 0 if the Blob does not exist + optional int64 size = 3; + + enum Result { + UNKNOWN = 0; + // Lease Succeeded + SUCCESS = 1; + // Lease Failed: Blob does not exist + BLOB_DNE = 2; + // Lease Failed: Leasee does not have access to the Blob + ACCESS_NOT_ALLOWED = 3; + // Lease Failed: Leasee requested an invalid expiry duration + LEASE_EXPIRY_INVALID = 4; + // Lease Failed: Leasee has exceeded the total data lease limit + DATA_SIZE_LIMIT_EXCEEDED = 5; + // Leasee Failed: Allowed count limit exceeded + COUNT_LIMIT_EXCEEDED = 6; + } + optional Result result = 4; +} + +/** + * Event representing when BlobStoreManager#openBlob() is called + * + * Logged from: + * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java + */ +message BlobOpened{ + // Uid of the Blob opener + optional int32 uid = 1 [(is_uid) = true]; + + // Id of the Blob opened or 0 if the Blob does not exist + optional int64 blob_id = 2; + + // Size of the Blob or 0 if the Blob does not exist + optional int64 size = 3; + + enum Result { + UNKNOWN = 0; + // Open Succeeded + SUCCESS = 1; + // Open Failed: Blob does not exist + BLOB_DNE = 2; + // Open Failed: Opener does not have access to the Blob + ACCESS_NOT_ALLOWED = 3; + } + optional Result result = 4; } ////////////////////////////////////////////////////////////////////// @@ -5791,7 +5971,7 @@ message ProcessStatsProto { optional string process = 1; // Uid of the process. - optional int32 uid = 2; + optional int32 uid = 2 [(is_uid) = true]; // Information about how often kills occurred message Kill { @@ -5818,13 +5998,16 @@ message ProcessStatsProto { repeated ProcessStatsAssociationProto assocs = 7; } -// Next Tag: 5 +// Next Tag: 6 message ProcessStatsAssociationProto { // Procss Name of the associated process (client process of service binding) optional string assoc_process_name = 1; // Package Name of the associated package (client package of service binding) - optional string assoc_package_name = 2; + optional string assoc_package_name = 2 [deprecated = true]; + + // UID of the associated process/package (client package of service binding) + optional int32 assoc_uid = 5 [(is_uid) = true]; // Total count of the times this association (service binding) appeared. optional int32 total_count = 3; @@ -5979,6 +6162,10 @@ message ProcessStatsAvailablePagesProto { */ message ProcStats { optional ProcessStatsSectionProto proc_stats_section = 1; + // Data pulled from device into this is sometimes sharded across multiple atoms to work around + // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer + // with the number of this shard. + optional int32 shard_id = 2; } /** @@ -6052,6 +6239,76 @@ message PackageNotificationChannelPreferences { } /** + * Atom that represents an item in the list of Do Not Disturb rules, pulled from + * NotificationManagerService.java. + */ +message DNDModeProto { + enum Mode { + ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules. + ZEN_MODE_OFF = 0; + ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; + ZEN_MODE_NO_INTERRUPTIONS = 2; + ZEN_MODE_ALARMS = 3; + } + optional int32 user = 1; // Android user ID (0, 1, 10, ...) + optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled + optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG + optional Mode zen_mode = 4; + // id is one of the system default rule IDs, or empty + // May also be "MANUAL_RULE" to indicate app-activation of the manual rule. + optional string id = 5; + optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other + optional DNDPolicyProto policy = 7; +} + +/** + * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto. + */ +message DNDPolicyProto { + enum State { + STATE_UNSET = 0; + STATE_ALLOW = 1; + STATE_DISALLOW = 2; + } + optional State calls = 1; + optional State repeat_callers = 2; + optional State messages = 3; + optional State conversations = 4; + optional State reminders = 5; + optional State events = 6; + optional State alarms = 7; + optional State media = 8; + optional State system = 9; + optional State fullscreen = 10; + optional State lights = 11; + optional State peek = 12; + optional State status_bar = 13; + optional State badge = 14; + optional State ambient = 15; + optional State notification_list = 16; + + enum PeopleType { + PEOPLE_UNSET = 0; + PEOPLE_ANYONE = 1; + PEOPLE_CONTACTS = 2; + PEOPLE_STARRED = 3; + PEOPLE_NONE = 4; + } + + optional PeopleType allow_calls_from = 17; + optional PeopleType allow_messages_from = 18; + + enum ConversationType { + CONV_UNSET = 0; + CONV_ANYONE = 1; + CONV_IMPORTANT = 2; + CONV_NONE = 3; + } + + optional ConversationType allow_conversations_from = 19; +} + +/** * Atom that contains a list of a package's channel group preferences, pulled from * NotificationManagerService.java. */ @@ -6302,6 +6559,16 @@ message ContentCaptureServiceEvents { SET_WHITELIST = 3; SET_DISABLED = 4; ON_USER_DATA_REMOVED = 5; + ON_DATA_SHARE_REQUEST = 6; + ACCEPT_DATA_SHARE_REQUEST = 7; + REJECT_DATA_SHARE_REQUEST = 8; + DATA_SHARE_WRITE_FINISHED = 9; + DATA_SHARE_ERROR_IOEXCEPTION = 10; + DATA_SHARE_ERROR_EMPTY_DATA = 11; + DATA_SHARE_ERROR_CLIENT_PIPE_FAIL = 12; + DATA_SHARE_ERROR_SERVICE_PIPE_FAIL = 13; + DATA_SHARE_ERROR_CONCURRENT_REQUEST = 14; + DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 15; } optional Event event = 1; // component/package of content capture service. @@ -6673,6 +6940,24 @@ message AppCompacted { } /** + * Logs when a Tethering event occurs. + * + */ +message NetworkTetheringReported { + // tethering error code + optional android.stats.connectivity.ErrorCode error_code = 1; + + // tethering downstream type + optional android.stats.connectivity.DownstreamType downstream_type = 2; + + // transport type of upstream network + optional android.stats.connectivity.UpstreamType upstream_type = 3; + + // The user type of Tethering + optional android.stats.connectivity.UserType user_type= 4; +} + +/** * Logs a DNS lookup operation initiated by the system resolver on behalf of an application * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName(). * @@ -6710,14 +6995,180 @@ message NetworkDnsEventReported { } /** + * logs the CapportApiData info + * Logged from: + * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java + */ +message CapportApiData { + // The TTL of the network connection provided by captive portal + optional int32 remaining_ttl_secs = 1; + + // The limit traffic data of the network connection provided by captive portal + optional int32 remaining_bytes = 2; + + // Is portal url option included in the DHCP packet (Yes, No) + optional bool has_portal_url = 3; + + // Is venue info (e.g. store info, maps, flight status) included (Yes, No) + optional bool has_venue_info = 4; +} + +/** + * logs a network Probe Event + * Logged from: + * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java + */ +message ProbeEvent { + // The probe type (http or https, or captive portal API...) + optional android.stats.connectivity.ProbeType probe_type = 1; + + // The latency in microseconds of the probe event + optional int32 latency_micros = 2; + + // The result of the probe event + optional android.stats.connectivity.ProbeResult probe_result = 3; + + // The CaptivePortal API info + optional CapportApiData capport_api_data = 4; +} + +/** + * log each ProbeEvent in ProbeEvents + * Logged from: + * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java + */ +message ProbeEvents { + // Record probe event during the validation + repeated ProbeEvent probe_event = 1; +} + +/** + * The DHCP (Dynamic Host Configuration Protocol) session info + * Logged from: + * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java + */ +message DhcpSession { + // The DHCP Feature(s) enabled in this session + repeated android.stats.connectivity.DhcpFeature used_features = 1; + + // The discover packet (re)transmit count + optional int32 discover_count = 2; + + // The request packet (re)transmit count + optional int32 request_count = 3; + + // The IPv4 address conflict count + // (only be meaningful when duplicate address detection is enabled) + optional int32 conflict_count = 4; + + // The DHCP packet parsing error code in this session + // (defined in android.net.metrics.DhcpErrorEvent) + repeated android.stats.connectivity.DhcpErrorCode error_code = 5; + + // The result of DHCP hostname transliteration + optional android.stats.connectivity.HostnameTransResult ht_result = 6; +} + +/** + * Logs Network IP provisioning event + * Logged from: + * packages/modules/NetworkStack/src/com/android/networkstack/metrics/NetworkIpProvisioningMetrics.java + */ +message NetworkIpProvisioningReported { + // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) + optional android.stats.connectivity.TransportType transport_type = 1; + + // The latency in microseconds of IP Provisioning over IPV4 + optional int32 ipv4_latency_micros = 2; + + // The latency in microseconds of IP Provisioning over IPV6 + optional int32 ipv6_latency_micros = 3; + + // The time duration between provisioning start and end (success or failure) + optional int64 provisioning_duration_micros = 4; + + // The specific disconnect reason for this IP provisioning + optional android.stats.connectivity.DisconnectCode disconnect_code = 5; + + // Log DHCP session info (Only valid for IPv4) + optional DhcpSession dhcp_session = 6 [(log_mode) = MODE_BYTES]; + + // The random number between 0 ~ 999 for sampling + optional int32 random_number = 7; +} + +/** + * Logs Network DHCP Renew event + * Logged from: + * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java + */ +message NetworkDhcpRenewReported { + // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) + optional android.stats.connectivity.TransportType transport_type = 1; + + // The request packet (re)transmit count + optional int32 request_count = 2; + + // The latency in microseconds of DHCP Renew + optional int32 latency_micros = 3; + + // The DHCP error code is defined in android.net.metrics.DhcpErrorEvent + optional android.stats.connectivity.DhcpErrorCode error_code = 4; + + // The result of DHCP renew + optional android.stats.connectivity.DhcpRenewResult renew_result = 5; + + // The random number between 0 ~ 999 for sampling + optional int32 random_number = 6; +} + +/** + * Logs Network Validation event + * Logged from: + * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java + */ +message NetworkValidationReported { + // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) + optional android.stats.connectivity.TransportType transport_type = 1; + + // Record each probe event + optional ProbeEvents probe_events = 2 [(log_mode) = MODE_BYTES]; + + // The result of the network validation + optional android.stats.connectivity.ValidationResult validation_result = 3; + + // The latency in microseconds of network validation + optional int32 latency_micros = 4; + + // The validation index (the first validation attempt or second, third...) + optional int32 validation_index = 5; + + // The random number between 0 ~ 999 for sampling + optional int32 random_number = 6; +} + +/** + * Logs NetworkStack Quirk event + * Logged from: + * packages/modules/NetworkStack/src/com/android/networkstack/ + */ +message NetworkStackQuirkReported { + // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) + optional android.stats.connectivity.TransportType transport_type = 1; + + // Record each Quirk event + optional android.stats.connectivity.NetworkQuirkEvent event = 2; +} + +/** * Logs when a data stall event occurs. * * Log from: - * frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java + * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java */ message DataStallEvent { // Data stall evaluation type. - // See frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java + // See packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java // Refer to the definition of DATA_STALL_EVALUATION_TYPE_*. optional int32 evaluation_type = 1; // See definition in data_stall_event.proto. @@ -6730,6 +7181,10 @@ message DataStallEvent { optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES]; // See definition in data_stall_event.proto. optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES]; + // The tcp packets fail rate from the latest tcp polling. + optional int32 tcp_fail_rate = 7; + // Number of packets sent since the last received packet. + optional int32 tcp_sent_since_last_recv = 8; } /* @@ -7981,6 +8436,245 @@ message CarPowerStateChanged { } /** + * Logs when Car User Hal is requested to switch/create/remove user. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalModifyUserRequestReported { + // Request id for the request. + optional int32 request_id = 1; + // Request type. + enum RequestType { + UNKNOWN = 0; + // Car user manager requested user switch. + SWITCH_REQUEST_ANDROID = 1; + // OEM requested User switch. + SWITCH_REQUEST_OEM = 2; + // Hal switch requested after android switch using activity manager. + SWITCH_REQUEST_LEGACY = 3; + // Create User + CREATE_REQUEST = 4; + // Remove User + REMOVE_REQUEST = 5; + } + optional RequestType request_type = 2; + // Android User id of the current user which can only be 0, 10, 11 and so on. + // -1 if not available. + optional int32 user_id = 3; + // VHAL flags of the current user. (-1 if not available) + optional int32 user_flags = 4; + // Android User id of the target user for switch/create/remove. It can only + // be 0, 10, 11 and so on. -1 if not available. + optional int32 target_user_id = 5; + // VHAL flags of the target user for switch/create/remove. (-1 if not available) + optional int32 target_user_flags = 6; + // Request timeout Milliseconds (-1 if not available) + optional int32 timeout_millis = 7; +} + +/** + * Logs when Car User Hal responds to switch/create user request. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalModifyUserResponseReported { + // Request id of the request associated with the response. + optional int32 request_id = 1; + // Car user hal callback status. + enum CallbackStatus { + UNKNOWN = 0; + // Hal response was invalid. + INVALID = 1; + // Hal response was ok. + OK = 2; + // Hal timeout during set call. + HAL_SET_TIMEOUT = 3; + // Hal response timeout. + HAL_RESPONSE_TIMEOUT = 4; + // Hal responded with wrong info. + WRONG_HAL_RESPONSE = 5; + // Hal is processing multiple requests simultaneously. + CONCURRENT_OPERATION = 6; + } + optional CallbackStatus callback_status = 2; + + // Hal request status for user switch/create/remove. + enum HalRequestStatus { + UNSPECIFIED = 0; + // Hal request for user switch/create is successful. + SUCCESS = 1; + // Hal request for user switch/create failed. + FAILURE = 2; + } + optional HalRequestStatus request_status = 3; +} + +/** + * Logs when post switch response is posted to Car User Hal. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalPostSwitchResponseReported { + // Request id. + optional int32 request_id = 1; + + // Android user switch status. + enum UserSwitchStatus { + UNKNOWN = 0; + // Android user switch is successful. + SUCCESS = 1; + // Android user switch failed. + FAILURE = 2; + } + optional UserSwitchStatus switch_status = 2; +} + +/** + * Logs when initial user information is requested from Car User Hal. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalInitialUserInfoRequestReported { + // Request id for the request. + optional int32 request_id = 1; + + // Request type for initial user information. + enum InitialUserInfoRequestType { + UNKNOWN = 0; + // At the first time Android was booted (or after a factory reset). + FIRST_BOOT = 1; + // At the first time Android was booted after the system was updated. + FIRST_BOOT_AFTER_OTA = 2; + // When Android was booted "from scratch". + COLD_BOOT = 3; + // When Android was resumed after the system was suspended to memory. + RESUME = 4; + } + optional InitialUserInfoRequestType request_type = 2; + // Request timeout Milliseconds (-1 if not available) + optional int32 timeout_millis = 3; +} + +/** + * Logs when Car User Hal responds to initial user information requests. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalInitialUserInfoResponseReported { + // Request id of the request associated with the response. + optional int32 request_id = 1; + // Car user hal callback status. + enum CallbackStatus { + UNKNOWN = 0; + // Hal response was invalid. + INVALID = 1; + // Hal response was ok. + OK = 2; + // Hal timeout during set call. + HAL_SET_TIMEOUT = 3; + // Hal response timeout. + HAL_RESPONSE_TIMEOUT = 4; + // Hal responded with wrong info. + WRONG_HAL_RESPONSE = 5; + // Hal is processing multiple requests simultaneously. + CONCURRENT_OPERATION = 6; + } + optional CallbackStatus callback_status = 2; + // Response for initial user information request. + enum InitialUserInfoResponseAction { + UNSPECIFIED = 0; + // Let the Android System decide what to do. + DEFAULT = 1; + // Switch to an existing Android user. + SWITCH = 2; + // Create a new Android user (and switch to it). + CREATE = 3; + } + optional InitialUserInfoResponseAction response_action = 3; + // Android User id of the target user which can only be 0, 10, 11 and so on. + // -1 if not available. + optional int32 target_user = 4; + // VHAL flags of the current user. (-1 if not available) + optional int32 target_user_flags = 5; + // User locales + optional string user_locales = 6; +} + +/** + * Logs when set user association is requested from Car User Hal. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalUserAssociationRequestReported { + // Request id for the request. + optional int32 request_id = 1; + // Request type. + enum RequestType { + UNKNOWN = 0; + // For setting user association information. + SET = 1; + // For getting user association information. + GET = 2; + } + optional RequestType request_type = 2; + // Android User id of the current user which can only be 0, 10, 11 and so on. + // -1 if not available. + optional int32 current_user_id = 3; + // VHAL flags of the current user. (-1 if not available) + optional int32 current_user_flags = 4; + // Number of the set associations requested. + optional int32 number_associations = 5; + // Concatenated string for the types from set associations request. + // This is a string converted from an array of integers. + optional string user_identification_association_types = 6; + // Concatenated string for the values from set associations request. + // This is a string converted from an array of integers. + optional string user_identification_association_values = 7; +} + +/** + * Logs when Car User Hal responds to set user association requests. + * + * Logged from: + * packages/services/Car/service/src/com/android/car/hal/UserHalService.java + */ +message CarUserHalSetUserAssociationResponseReported { + // Request id of the request associated with the response. + optional int32 request_id = 1; + // Car user hal callback status. + enum CallbackStatus { + UNKNOWN = 0; + // Hal response was invalid. + INVALID = 1; + // Hal response was ok. + OK = 2; + // Hal timeout during set call. + HAL_SET_TIMEOUT = 3; + // Hal response timeout. + HAL_RESPONSE_TIMEOUT = 4; + // Hal responded with wrong info. + WRONG_HAL_RESPONSE = 5; + // Hal is processing multiple requests simultaneously. + CONCURRENT_OPERATION = 6; + } + optional CallbackStatus callback_status = 2; + // Number of the set associations in the response. + optional int32 number_associations = 3; + // Concatenated string for the types from set associations request. + // This is a string converted from an array of integers. + optional string user_identification_association_types = 4; + // Concatenated string for the values from set associations request. + // This is a string converted from an array of integers. + optional string user_identification_association_values = 5; +} + +/** * Logs whether GarageMode is entered. * * Logged from: @@ -8634,6 +9328,15 @@ message UpdateEngineUpdateAttemptReported { // system property when the device takes the update. e.g. // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys optional string source_fingerprint = 8; + + // Size of super partition. + optional int64 super_partition_size_bytes = 9; + + // Size of current slot within the super partition. + optional int64 slot_size_bytes = 10; + + // Free space available in the super partition. + optional int64 super_free_space_bytes = 11; } /** @@ -9022,6 +9725,7 @@ message RuntimeAppOpAccess { UNIFORM = 1; RARELY_USED = 2; BOOT_TIME_SAMPLING = 3; + UNIFORM_OPS = 4; } // sampling strategy used to collect this message @@ -9114,8 +9818,10 @@ message IntegrityRulesPushed { /** * Logs when a cell broadcast message is received on the device. * - * Logged from CellBroadcastService module: + * Logged from Cell Broadcast module and platform: * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ + * packages/apps/CellBroadcastReceiver/ + * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java */ message CellBroadcastMessageReported { // The type of Cell Broadcast message @@ -9126,8 +9832,40 @@ message CellBroadcastMessageReported { CDMA_SPC = 3; } + // The parts of the cell broadcast message pipeline + enum ReportSource { + UNKNOWN_SOURCE = 0; + FRAMEWORK = 1; + CB_SERVICE = 2; + CB_RECEIVER_APP = 3; + } + // GSM, CDMA, CDMA-SCP optional CbType type = 1; + + // The source of the report + optional ReportSource source = 2; +} + +/** + * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR. + * + * Logged from CellBroadcastService module: + * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ + */ +message CellBroadcastMessageFiltered { + enum FilterReason { + NOT_FILTERED = 0; + DUPLICATE_MESSAGE = 1; + GEOFENCED_MESSAGE = 2; + AREA_INFO_MESSAGE = 3; + } + + // GSM, CDMA, CDMA-SCP + optional CellBroadcastMessageReported.CbType type = 1; + + // The source of the report + optional FilterReason filter = 2; } /** @@ -9154,6 +9892,7 @@ message CellBroadcastMessageError { UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12; UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13; UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14; + NO_CONNECTION_TO_CB_SERVICE = 15; } // What kind of error occurred @@ -9164,30 +9903,6 @@ message CellBroadcastMessageError { } /** - * Logs when the SDK Extensions test app has polled the current version. - * This is atom ID 354. - * - * Logged from: - * vendor/google_testing/integration/packages/apps/SdkExtensionsTestApp/ - */ -message SdkExtensionStatus { - enum ApiCallStatus { - CALL_NOT_ATTEMPTED = 0; - CALL_SUCCESSFUL = 1; - CALL_FAILED = 2; - } - - optional ApiCallStatus result = 1; - - // The R extension version, i.e. android.os.ext.SdkExtension.getExtensionVersion(R). - optional int32 r_extension_version = 2; - - // A number identifying which particular symbol's call failed, if any. 0 means no missing symbol. - // "Failed" here can mean a symbol that wasn't meant to be visible was, or the other way around. - optional int32 failed_call_symbol = 3; -} - -/** * Logs when a tune occurs through device's Frontend. * This is atom ID 276. * @@ -9209,6 +9924,100 @@ message TvTunerStateChanged { // new state optional State state = 2; } + +/** + * Logs the status of a dvr playback or record. + * This is atom ID 279. + * + * Logged from: + * frameworks/base/media/java/android/media/tv/tuner/dvr + */ +message TvTunerDvrStatus { + enum Type { + UNKNOWN_TYPE = 0; + PLAYBACK = 1; // is a playback + RECORD = 2; // is a record + } + enum State { + UNKNOWN_STATE = 0; + STARTED = 1; // DVR is started + STOPPED = 2; // DVR is stopped + } + // The uid of the application that sent this custom atom. + optional int32 uid = 1 [(is_uid) = true]; + // DVR type + optional Type type = 2; + // DVR state + optional State state = 3; + // Identify the segment of a record or playback + optional int32 segment_id = 4; + // indicate how many overflow or underflow happened between started to stopped + optional int32 overflow_underflow_count = 5; +} + +/** + * Logs when a cas session opened through MediaCas. + * This is atom ID 280. + * + * Logged from: + * frameworks/base/media/java/android/media/MediaCas.java + */ +message TvCasSessionOpenStatus { + enum State { + UNKNOWN = 0; + SUCCEEDED = 1; // indicate that the session is opened successfully. + FAILED = 2; // indicate that the session isn’t opened successfully. + } + // The uid of the application that sent this custom atom. + optional int32 uid = 1 [(is_uid) = true]; + // Cas system Id + optional int32 cas_system_id = 2; + // State of the session + optional State state = 3; +} + +/** + * Logs for ContactsProvider general usage. + * This is atom ID 301. + * + * Logged from: + * packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java + */ +message ContactsProviderStatusReported { + enum ApiType { + UNKNOWN_API = 0; + QUERY = 1; + // INSERT includes insert and bulkInsert, and inserts triggered by applyBatch. + INSERT = 2; + // UPDATE and DELETE includes update/delete and the ones triggered by applyBatch. + UPDATE = 3; + DELETE = 4; + } + + enum ResultType { + UNKNOWN_RESULT = 0; + SUCCESS = 1; + FAIL = 2; + ILLEGAL_ARGUMENT = 3; + UNSUPPORTED_OPERATION = 4; + } + + enum CallerType { + UNSPECIFIED_CALLER_TYPE = 0; + CALLER_IS_SYNC_ADAPTER = 1; + CALLER_IS_NOT_SYNC_ADAPTER = 2; + } + + optional ApiType api_type = 1; + // Defined in + // packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java + optional int32 uri_type = 2; + optional CallerType caller_type = 3; + optional ResultType result_type = 4; + optional int32 result_count = 5; + optional int64 latency_micros = 6; +} + /** * Logs when an app is frozen or unfrozen. * @@ -9402,7 +10211,7 @@ message GnssStats { optional int64 time_to_first_fix_reports = 3; // Total pulled reported time to first fix (in milli-seconds) since boot - optional int64 time_to_first_fix_milli_s = 4; + optional int64 time_to_first_fix_millis = 4; // Number of position accuracy reports since boot optional int64 position_accuracy_reports = 5; @@ -9725,15 +10534,20 @@ message AccessibilityServiceReported { optional android.stats.accessibility.ServiceStatus service_status = 2; } -message DisplayWakeReason { +/** + * Logs when display wake up. + * + * Logged from: + * services/core/java/com/android/server/power/Notifier.java + */ + +message DisplayWakeReported { // Wake_up_reason code // If LOWORD(wake_up_reason) = 0 // reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX // else reference wake_up_reason to - // frameworks/base/services/core/java/com/android/server/power/Notifier.java#DispWakeupReason + // services/core/java/com/android/server/power/Notifier.java#onWakeUp optional int32 wake_up_reason = 1; - // Count of wake up by reason - optional int32 wake_times = 2; } /** @@ -9907,3 +10721,453 @@ message MediaOutputOpSwitchReported { // The amount of applied devices within a remote dynamic group after a switching is done. optional int32 applied_device_count_within_remote_group = 9; } + +/** + * Logs when the Assistant is invoked. + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java + */ +message AssistantInvocationReported { + + // The event_id (as for UiEventReported). + optional int32 event_id = 1; + + // The registered Assistant's uid and package (as for UiEventReported). + optional int32 uid = 2 [(is_uid) = true]; + optional string package_name = 3; + + // An identifier used to disambiguate which logs refer to a particular invocation of the + // Assistant (as for UiEventReported). + optional int32 instance_id = 4; + + // The state of the device at the time of invocation. + enum DeviceState { + UNKNOWN_DEVICE_STATE = 0; + AOD1 = 1; + AOD2 = 2; + BOUNCER = 3; + UNLOCKED_LOCKSCREEN = 4; + LAUNCHER_HOME = 5; + LAUNCHER_OVERVIEW = 6; + LAUNCHER_ALL_APPS = 7; + APP_DEFAULT = 8; + APP_IMMERSIVE = 9; + APP_FULLSCREEN = 10; + } + optional DeviceState device_state = 5; + + // Whether the Assistant handles were showing at the time of invocation. + optional bool assistant_handles_showing = 6; +} + +/** + * Logs when an AudioRecord finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioRecordDeviceUsageReported { + // The devices connected to this AudioRecord. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The client-server buffer framecount. + // The framecount is generally between 960 - 48000 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the AudioRecord. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio input flags used to construct the AudioRecord. + // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t + optional string flags = 8; + + // The santized package name of the audio client associated with the AudioRecord. + // See getSanitizedPackageNameAndVersionCode() in + // frameworks/av/services/mediametrics/MediaMetricsService.cpp + optional string package_name = 9; + + // The selected device id (nonzero if a non-default device is selected) + optional int32 selected_device_id = 10; + + // The caller of the AudioRecord. + // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string caller = 11; + + // The audio source for AudioRecord. + // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t + optional string source = 12; +} + +/** + * Logs when an AudioThread finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioThreadDeviceUsageReported { + // The devices connected to this audio thread. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // (for record threads): + // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp + // (for playback threads): + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The framecount of the buffer delivered to (or from) the HAL. + // The framecount is generally ~960 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the audio thread. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio flags used to construct the thread + // (for record threads): + // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t + // (for playback threads): + // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t + optional string flags = 8; + + // The number of underruns encountered for a playback thread or the + // number of overruns encountered for a capture thread. + optional int32 xruns = 9; + + // The type of thread + // A thread type enumeration from + // frameworks/av/mediametrics/services/Translate.h + optional string type = 10; +} + +/** + * Logs when an AudioTrack finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioTrackDeviceUsageReported { + // The output devices connected to this AudioTrack. + // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The client-server buffer framecount. + // The framecount is generally between 960 - 48000 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + // A static track (see traits) may have a very large framecount. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the AudioTrack. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio flags used to construct the AudioTrack. + // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t + optional string flags = 8; + + // The number of underruns encountered. + optional int32 xruns = 9; + + // The santized package name of the audio client associated with the AudioTrack. + // See getSanitizedPackageNameAndVersionCode() in + // frameworks/av/services/mediametrics/MediaMetricsService.cpp + optional string package_name = 10; + + // The latency of the last sample in the buffer in milliseconds. + optional float device_latency_millis = 11; + + // The startup time in milliseconds from start() to sample played. + optional float device_startup_millis = 12; + + // The average volume of the track on the device [ 0.f - 1.f ] + optional float device_volume = 13; + + // The selected device id (nonzero if a non-default device is selected) + optional int32 selected_device_id = 14; + + // The stream_type category for the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t + optional string stream_type = 15; + + // The usage for the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t + optional string usage = 16; + + // The content type of the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t + optional string content_type = 17; + + // The caller of the AudioTrack. + // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string caller = 18; + + // The traits of the AudioTrack. + // A string OR of different traits, may be empty string. + // Only "static" is supported for R. + // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string traits = 19; +} + +/** + * Logs the status of an audio device connection attempt. + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioDeviceConnectionReported { + // The input devices represented by this report. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string input_devices = 1; + + // The output devices represented by this report. + // A string OR of various output device categories. + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string output_devices = 2; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 3; + + // The result of the audio device connection. + // 0 indicates success: connection verified. + // 1 indicates unknown: connection not verified or not known if diverted properly. + // Other values indicate specific status. + // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h + optional int32 result = 4; + + // Average milliseconds of time to connect + optional float time_to_connect_millis = 5; + + // Number of connections if aggregated statistics, otherwise 1. + optional int32 connection_count = 6; +} + +/** + * Logs: i) creation of different types of cryptographic keys in the keystore, + * ii) operations performed using the keys, + * iii) attestation of the keys + * Logged from: system/security/keystore/key_event_log_handler.cpp + */ +message KeystoreKeyEventReported { + + enum Algorithm { + /** Asymmetric algorithms. */ + RSA = 1; + // 2 removed, do not reuse. + EC = 3; + /** Block cipher algorithms */ + AES = 32; + TRIPLE_DES = 33; + /** MAC algorithms */ + HMAC = 128; + }; + /** Algorithm associated with the key */ + optional Algorithm algorithm = 1; + + /** Size of the key */ + optional int32 key_size = 2; + + enum KeyOrigin { + /** Generated in keymaster. Should not exist outside the TEE. */ + GENERATED = 0; + /** Derived inside keymaster. Likely exists off-device. */ + DERIVED = 1; + /** Imported into keymaster. Existed as cleartext in Android. */ + IMPORTED = 2; + /** Keymaster did not record origin. */ + UNKNOWN = 3; + /** Securely imported into Keymaster. */ + SECURELY_IMPORTED = 4; + }; + /* Logs whether the key was generated, imported, securely imported, or derived.*/ + optional KeyOrigin key_origin = 3; + + enum HardwareAuthenticatorType { + NONE = 0; + PASSWORD = 1; + FINGERPRINT = 2; + // Additional entries must be powers of 2. + }; + /** + * What auth types does this key require? If none, + * then no auth required. + */ + optional HardwareAuthenticatorType user_auth_type = 4; + + /** + * If user authentication is required, is the requirement time based? If it + * is not time based then this field will not be used and the key is per + * operation. Per operation keys must be user authenticated on each usage. + */ + optional int32 user_auth_key_timeout_secs = 5; + + /** + * padding mode, digest, block_mode and purpose should ideally be repeated + * fields. However, since statsd does not support repeated fields in + * pushed atoms, they are represented using bitmaps. + */ + + /** Track which padding mode is being used.*/ + optional int32 padding_mode_bitmap = 6; + + /** Track which digest is being used. */ + optional int32 digest_bitmap = 7; + + /** Track what block mode is being used (for encryption). */ + optional int32 block_mode_bitmap = 8; + + /** Track what purpose is this key serving. */ + optional int32 purpose_bitmap = 9; + + enum EcCurve { + P_224 = 0; + P_256 = 1; + P_384 = 2; + P_521 = 3; + }; + /** Which ec curve was selected if elliptic curve cryptography is in use **/ + optional EcCurve ec_curve = 10; + + enum KeyBlobUsageRequirements { + STANDALONE = 0; + REQUIRES_FILE_SYSTEM = 1; + }; + /** Standalone or is a file system required */ + optional KeyBlobUsageRequirements key_blob_usage_reqs = 11; + + enum Type { + key_operation = 0; + key_creation = 1; + key_attestation = 2; + } + /** Key creation event, operation event or attestation event? */ + optional Type type = 12; + + /** Was the key creation, operation, or attestation successful? */ + optional bool was_successful = 13; + + /** Response code or error code */ + optional int32 error_code = 14; +} + +// Blob Committer stats +// Keep in sync between: +// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto +// frameworks/base/cmds/statsd/src/atoms.proto +message BlobCommitterProto { + // Committer app's uid + optional int32 uid = 1 [(is_uid) = true]; + + // Unix epoch timestamp of the commit in milliseconds + optional int64 commit_timestamp_millis = 2; + + // Flags of what access types the committer has set for the Blob + optional int32 access_mode = 3; + + // Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST + optional int32 num_whitelisted_package = 4; +} + +// Blob Leasee stats +// Keep in sync between: +// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto +// frameworks/base/cmds/statsd/src/atoms.proto +message BlobLeaseeProto { + // Leasee app's uid + optional int32 uid = 1 [(is_uid) = true]; + + // Unix epoch timestamp for lease expiration in milliseconds + optional int64 lease_expiry_timestamp_millis = 2; +} + +// List of Blob Committers +// Keep in sync between: +// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto +// frameworks/base/cmds/statsd/src/atoms.proto +message BlobCommitterListProto { + repeated BlobCommitterProto committer = 1; +} + +// List of Blob Leasees +// Keep in sync between: +// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto +// frameworks/base/cmds/statsd/src/atoms.proto +message BlobLeaseeListProto { + repeated BlobLeaseeProto leasee = 1; +} + +/** + * Logs the current state of a Blob committed with BlobStoreManager + * + * Pulled from: + * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java + */ +message BlobInfo { + // Id of the Blob + optional int64 blob_id = 1; + + // Size of the Blob data + optional int64 size = 2; + + // Unix epoch timestamp of the Blob's expiration in milliseconds + optional int64 expiry_timestamp_millis = 3; + + // List of committers of this Blob + optional BlobCommitterListProto committers = 4; + + // List of leasees of this Blob + optional BlobLeaseeListProto leasees = 5; +} diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 2d7f912dac84..e9875baf58c7 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -37,7 +37,8 @@ CombinationConditionTracker::~CombinationConditionTracker() { bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack) { + vector<bool>& stack, + vector<ConditionState>& initialConditionCache) { VLOG("Combination predicate init() %lld", (long long)mConditionId); if (mInitialized) { return true; @@ -73,9 +74,9 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf return false; } - - bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, - conditionIdIndexMap, stack); + bool initChildSucceeded = + childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap, + stack, initialConditionCache); if (!initChildSucceeded) { ALOGW("Child initialization failed %lld ", (long long)child); @@ -95,6 +96,11 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf childTracker->getLogTrackerIndex().end()); } + mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, + initialConditionCache); + initialConditionCache[mIndex] = + evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache); + // unmark this node in the recursion stack. stack[mIndex] = false; @@ -141,17 +147,14 @@ void CombinationConditionTracker::evaluateCondition( ConditionState newCondition = evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache); if (!mSliced) { + bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition); + mUnSlicedPartCondition = newCondition; - bool nonSlicedChanged = (mNonSlicedConditionState != newCondition); - mNonSlicedConditionState = newCondition; - - nonSlicedConditionCache[mIndex] = mNonSlicedConditionState; - + nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition; conditionChangedCache[mIndex] = nonSlicedChanged; - mUnSlicedPart = newCondition; } else { - mUnSlicedPart = evaluateCombinationCondition( - mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache); + mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, + nonSlicedConditionCache); for (const int childIndex : mChildren) { // If any of the sliced condition in children condition changes, the combination diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index e3d860127780..39ff0ab03266 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -32,8 +32,8 @@ public: bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack) override; + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, + std::vector<ConditionState>& initialConditionCache) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 26de88860ab4..62736c8160bb 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -36,7 +36,7 @@ public: mIndex(index), mInitialized(false), mTrackerIndex(), - mNonSlicedConditionState(ConditionState::kUnknown), + mUnSlicedPartCondition(ConditionState::kUnknown), mSliced(false){}; virtual ~ConditionTracker(){}; @@ -51,10 +51,12 @@ public: // need to call init() on children conditions) // conditionIdIndexMap: the mapping from condition id to its index. // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. + // initialConditionCache: tracks initial conditions of all ConditionTrackers. virtual bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack) = 0; + std::vector<bool>& stack, + std::vector<ConditionState>& initialConditionCache) = 0; // evaluate current condition given the new event. // event: the new log event @@ -72,11 +74,6 @@ public: std::vector<ConditionState>& conditionCache, std::vector<bool>& conditionChanged) = 0; - // Return the current condition state. - virtual ConditionState isConditionMet() const { - return mNonSlicedConditionState; - }; - // Query the condition with parameters. // [conditionParameters]: a map from condition name to the HashableDimensionKey to query the // condition. @@ -125,8 +122,9 @@ public: const std::vector<sp<ConditionTracker>>& allConditions, const vector<Matcher>& dimensions) const = 0; + // Return the current condition state of the unsliced part of the condition. inline ConditionState getUnSlicedPartConditionState() const { - return mUnSlicedPart; + return mUnSlicedPartCondition; } protected: @@ -141,10 +139,16 @@ protected: // the list of LogMatchingTracker index that this ConditionTracker uses. std::set<int> mTrackerIndex; - ConditionState mNonSlicedConditionState; + // This variable is only used for CombinationConditionTrackers. + // SimpleConditionTrackers technically don't have an unsliced part because + // they are either sliced or unsliced. + // + // CombinationConditionTrackers have multiple children ConditionTrackers + // that can be a mixture of sliced or unsliced. This tracks the + // condition of the unsliced part of the combination condition. + ConditionState mUnSlicedPartCondition; bool mSliced; - ConditionState mUnSlicedPart; }; } // namespace statsd diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 61760f3e29b2..efb4d4989425 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -85,12 +85,6 @@ SimpleConditionTracker::SimpleConditionTracker( mInitialValue = ConditionState::kUnknown; } - mNonSlicedConditionState = mInitialValue; - - if (!mSliced) { - mUnSlicedPart = mInitialValue; - } - mInitialized = true; } @@ -101,9 +95,11 @@ SimpleConditionTracker::~SimpleConditionTracker() { bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack) { + vector<bool>& stack, + vector<ConditionState>& initialConditionCache) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. + initialConditionCache[mIndex] = mInitialValue; return mInitialized; } @@ -141,9 +137,6 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio mInitialValue = ConditionState::kFalse; mSlicedConditionState.clear(); conditionCache[mIndex] = ConditionState::kFalse; - if (!mSliced) { - mUnSlicedPart = ConditionState::kFalse; - } } bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -305,9 +298,7 @@ void SimpleConditionTracker::evaluateCondition( conditionCache[mIndex] = itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; } - mUnSlicedPart = conditionCache[mIndex]; } - return; } @@ -333,9 +324,6 @@ void SimpleConditionTracker::evaluateCondition( } conditionCache[mIndex] = overallState; conditionChangedCache[mIndex] = overallChanged; - if (!mSliced) { - mUnSlicedPart = overallState; - } } void SimpleConditionTracker::isConditionMet( diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 5c5cc565f783..ea7f87bde2b8 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -37,8 +37,8 @@ public: bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack) override; + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, + std::vector<ConditionState>& initialConditionCache) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index 9df4d1f8ce24..bb5d0a6bab58 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -44,7 +44,8 @@ StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_ bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) { lock_guard<std::mutex> lock(mLock); - int64_t elapsedTimeNs = getElapsedRealtimeNs(); + const int64_t elapsedTimeNs = getElapsedRealtimeNs(); + const int64_t systemUptimeMillis = getSystemUptimeMillis(); StatsdStats::getInstance().notePull(mTagId); const bool shouldUseCache = (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs); @@ -67,16 +68,18 @@ bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<Lo if (!mHasGoodData) { return mHasGoodData; } - const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs; - StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs); - const bool pullTimeOut = pullDurationNs > mPullTimeoutNs; + const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs; + const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis; + StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs); + const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs; if (pullTimeOut) { // Something went wrong. Discard the data. mCachedData.clear(); mHasGoodData = false; - StatsdStats::getInstance().notePullTimeout(mTagId); + StatsdStats::getInstance().notePullTimeout( + mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs)); ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId, - (long long)pullDurationNs); + (long long)pullElapsedDurationNs); return mHasGoodData; } diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index c027fffd20a0..6e89038f4152 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -38,6 +38,7 @@ using android::util::ProtoOutputStream; using std::lock_guard; using std::shared_ptr; using std::string; +using std::to_string; using std::vector; const int FIELD_ID_BEGIN_TIME = 1; @@ -436,9 +437,18 @@ void StatsdStats::notePullDataError(int pullAtomId) { mPulledAtomStats[pullAtomId].dataError++; } -void StatsdStats::notePullTimeout(int pullAtomId) { +void StatsdStats::notePullTimeout(int pullAtomId, + int64_t pullUptimeMillis, + int64_t pullElapsedMillis) { lock_guard<std::mutex> lock(mLock); - mPulledAtomStats[pullAtomId].pullTimeout++; + PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId]; + pulledAtomStats.pullTimeout++; + + if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) { + pulledAtomStats.pullTimeoutMetadata.pop_front(); + } + + pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis); } void StatsdStats::notePullExceedMaxDelay(int pullAtomId) { @@ -630,6 +640,7 @@ void StatsdStats::resetInternalLocked() { pullStats.second.unregisteredCount = 0; pullStats.second.atomErrorCount = 0; pullStats.second.binderCallFailCount = 0; + pullStats.second.pullTimeoutMetadata.clear(); } mAtomMetricStats.clear(); mActivationBroadcastGuardrailStats.clear(); @@ -786,6 +797,20 @@ void StatsdStats::dumpStats(int out) const { pair.second.pullUidProviderNotFound, pair.second.pullerNotFound, pair.second.registeredCount, pair.second.unregisteredCount, pair.second.atomErrorCount); + if (pair.second.pullTimeoutMetadata.size() > 0) { + string uptimeMillis = "(pull timeout system uptime millis) "; + string pullTimeoutMillis = "(pull timeout elapsed time millis) "; + for (const auto& stats : pair.second.pullTimeoutMetadata) { + uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");; + pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(","); + } + uptimeMillis.pop_back(); + uptimeMillis.push_back('\n'); + pullTimeoutMillis.pop_back(); + pullTimeoutMillis.push_back('\n'); + dprintf(out, "%s", uptimeMillis.c_str()); + dprintf(out, "%s", pullTimeoutMillis.c_str()); + } } if (mAnomalyAlarmRegisteredStats > 0) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 8587e1452543..005048446fc3 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -101,7 +101,7 @@ public: // Per atom dimension key size limit static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap; - const static int kMaxConfigCountPerUid = 10; + const static int kMaxConfigCountPerUid = 20; const static int kMaxAlertCountPerConfig = 100; const static int kMaxConditionCountPerConfig = 300; const static int kMaxMetricCountPerConfig = 1000; @@ -352,7 +352,7 @@ public: /* * Records pull exceeds timeout for the puller. */ - void notePullTimeout(int pullAtomId); + void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis); /* * Records pull exceeds max delay for a metric. @@ -498,6 +498,14 @@ public: */ void dumpStats(int outFd) const; + typedef struct PullTimeoutMetadata { + int64_t pullTimeoutUptimeMillis; + int64_t pullTimeoutElapsedMillis; + PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) : + pullTimeoutUptimeMillis(uptimeMillis), + pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */} + } PullTimeoutMetadata; + typedef struct { long totalPull = 0; long totalPullFromCache = 0; @@ -519,6 +527,7 @@ public: long unregisteredCount = 0; int32_t atomErrorCount = 0; long binderCallFailCount = 0; + std::list<PullTimeoutMetadata> pullTimeoutMetadata; } PulledAtomStats; typedef struct { @@ -660,6 +669,8 @@ private: FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats); FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit); FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats); + + FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index d865c2176c1e..573961276e5b 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -68,14 +68,14 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; CountMetricProducer::CountMetricProducer( const ConfigKey& key, const CountMetric& metric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs, - + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap) { + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.has_bucket()) { mBucketSizeNs = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 26b3d3cc6722..f05fb061ccc1 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -43,7 +43,8 @@ class CountMetricProducer : public MetricProducer { public: CountMetricProducer( const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const int64_t timeBaseNs, const int64_t startTimeNs, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap = {}, diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 663365924829..e9b043876d3d 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -64,15 +64,16 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, - const bool nesting, const sp<ConditionWizard>& wizard, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, + const vector<ConditionState>& initialConditionCache, const size_t startIndex, + const size_t stopIndex, const size_t stopAllIndex, const bool nesting, + const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, + const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap), + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 53f0f28c3386..bfe1010c89de 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -40,10 +40,10 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer( const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, - const bool nesting, const sp<ConditionWizard>& wizard, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, - const int64_t startTimeNs, + const vector<ConditionState>& initialConditionCache, const size_t startIndex, + const size_t stopIndex, const size_t stopAllIndex, const bool nesting, + const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, + const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {}, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {}, const vector<int>& slicedStateAtoms = {}, diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index d68f64ae40b0..dc0036a687f3 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -54,13 +54,14 @@ const int FIELD_ID_ATOMS = 2; EventMetricProducer::EventMetricProducer( const ConfigKey& key, const EventMetric& metric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int64_t startTimeNs, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap) { + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.links().size() > 0) { for (const auto& link : metric.links()) { Metric2Condition mc; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index e8f2119a170c..bfb2de36fad4 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -35,7 +35,8 @@ class EventMetricProducer : public MetricProducer { public: EventMetricProducer( const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int64_t startTimeNs, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const int64_t startTimeNs, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap = {}, diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 1d4d0b3a5e5d..020f4b638f4d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -70,14 +70,15 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; GaugeMetricProducer::GaugeMetricProducer( const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, - const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, + const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, + const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap, /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}), + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, + eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{}, + /*stateGroupMap=*/{}), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 2eb584b097ea..2fc772b6b641 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -58,6 +58,7 @@ class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDa public: GaugeMetricProducer( const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 2518d85eb6a1..fe143e496373 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -45,7 +45,8 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; MetricProducer::MetricProducer( const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const sp<ConditionWizard>& wizard, + const int conditionIndex, const vector<ConditionState>& initialConditionCache, + const sp<ConditionWizard>& wizard, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap, @@ -56,7 +57,7 @@ MetricProducer::MetricProducer( mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), - mCondition(initialCondition(conditionIndex)), + mCondition(initialCondition(conditionIndex, initialConditionCache)), mConditionTrackerIndex(conditionIndex), mConditionSliced(false), mWizard(wizard), @@ -97,7 +98,7 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo // Stores atom id to primary key pairs for each state atom that the metric is // sliced by. - std::map<int, HashableDimensionKey> statePrimaryKeys; + std::map<int32_t, HashableDimensionKey> statePrimaryKeys; // For states with primary fields, use MetricStateLinks to get the primary // field values from the log event. These values will form a primary key @@ -293,6 +294,17 @@ void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) { } } +HashableDimensionKey MetricProducer::getUnknownStateKey() { + HashableDimensionKey stateKey; + for (auto atom : mSlicedStateAtoms) { + FieldValue fieldValue; + fieldValue.mField.setTag(atom); + fieldValue.mValue.setInt(StateTracker::kStateUnknown); + stateKey.addValue(fieldValue); + } + return stateKey; +} + DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) { DropEvent event; event.reason = reason; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 673f668cda82..be4cd6724bb1 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -129,7 +129,8 @@ struct SkippedBucket { class MetricProducer : public virtual android::RefBase, public virtual StateListener { public: MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const sp<ConditionWizard>& wizard, + const int conditionIndex, const vector<ConditionState>& initialConditionCache, + const sp<ConditionWizard>& wizard, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap, @@ -138,8 +139,9 @@ public: virtual ~MetricProducer(){}; - ConditionState initialCondition(const int conditionIndex) const { - return conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue; + ConditionState initialCondition(const int conditionIndex, + const vector<ConditionState>& initialConditionCache) const { + return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue; } /** @@ -386,6 +388,10 @@ protected: // If no state map exists, keep the original state value. void mapStateValue(const int32_t atomId, FieldValue* value); + // Returns a HashableDimensionKey with unknown state value for each state + // atom. + HashableDimensionKey getUnknownStateKey(); + DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason); // Returns true if the number of drop events in the current bucket has @@ -492,6 +498,8 @@ protected: FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); + + FRIEND_TEST(MetricsManagerTest, TestInitialConditions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index e8c575a1adea..60de1a24cce5 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -21,7 +21,6 @@ #include <private/android_filesystem_config.h> #include "CountMetricProducer.h" -#include "atoms_info.h" #include "condition/CombinationConditionTracker.h" #include "condition/SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" @@ -361,20 +360,17 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, protoOutput->end(token); } - mLastReportTimeNs = dumpTimeStampNs; - mLastReportWallClockNs = getWallClockNs(); + // Do not update the timestamps when data is not cleared to avoid timestamps from being + // misaligned. + if (erase_data) { + mLastReportTimeNs = dumpTimeStampNs; + mLastReportWallClockNs = getWallClockNs(); + } VLOG("=========================Metric Reports End=========================="); } bool MetricsManager::checkLogCredentials(const LogEvent& event) { - // TODO(b/154856835): Remove this check once we get whitelist from the config. - if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) != - android::util::AtomsInfo::kWhitelistedAtoms.end()) - { - return true; - } - if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) { return true; } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index dbec24bf3f6c..5987a723a421 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -78,6 +78,7 @@ const Value ZERO_DOUBLE((int64_t)0); // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently ValueMetricProducer::ValueMetricProducer( const ConfigKey& key, const ValueMetric& metric, const int conditionIndex, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, @@ -85,8 +86,9 @@ ValueMetricProducer::ValueMetricProducer( const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, + conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), @@ -187,10 +189,6 @@ void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d", (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), oldState.mValue.int_value, newState.mValue.int_value); - // If condition is not true, we do not need to pull for this state change. - if (mCondition != ConditionState::kTrue) { - return; - } // If old and new states are in the same StateGroup, then we do not need to // pull for this state change. @@ -202,6 +200,12 @@ void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, return; } + // If condition is not true or metric is not active, we do not need to pull + // for this state change. + if (mCondition != ConditionState::kTrue || !mIsActive) { + return; + } + bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs; if (isEventLate) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, @@ -409,7 +413,6 @@ void ValueMetricProducer::resetBase() { for (auto& slice : mCurrentBaseInfo) { for (auto& baseInfo : slice.second) { baseInfo.hasBase = false; - baseInfo.hasCurrentState = false; } } mHasGlobalBase = false; @@ -622,7 +625,6 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log auto it = mCurrentBaseInfo.find(whatKey); for (auto& baseInfo : it->second) { baseInfo.hasBase = false; - baseInfo.hasCurrentState = false; } } } @@ -772,22 +774,24 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff && mCondition != ConditionState::kTrue; if (shouldSkipForPushMetric || shouldSkipForPulledMetric) { - VLOG("ValueMetric skip event because condition is false"); + VLOG("ValueMetric skip event because condition is false and we are not using diff (for " + "pulled metric)"); return; } if (hitGuardRailLocked(eventKey)) { return; } + vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey]; if (baseInfos.size() < mFieldMatchers.size()) { VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); baseInfos.resize(mFieldMatchers.size()); } - for (auto baseInfo : baseInfos) { + for (BaseInfo& baseInfo : baseInfos) { if (!baseInfo.hasCurrentState) { - baseInfo.currentState = DEFAULT_DIMENSION_KEY; + baseInfo.currentState = getUnknownStateKey(); baseInfo.hasCurrentState = true; } } @@ -815,6 +819,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( Interval& interval = intervals[i]; interval.valueIndex = i; Value value; + baseInfo.hasCurrentState = true; + baseInfo.currentState = stateKey; if (!getDoubleOrLong(event, matcher, value)) { VLOG("Failed to get value %d from event %s", i, event.ToString().c_str()); StatsdStats::getInstance().noteBadValueType(mMetricId); @@ -902,7 +908,6 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( interval.hasValue = true; } interval.sampleSize += 1; - baseInfo.currentState = stateKey; } // Only trigger the tracker if all intervals are correct @@ -946,30 +951,39 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) { if (mCondition == ConditionState::kUnknown) { StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); + invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN); } + VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, + (int)mCurrentSlicedBucket.size()); + + int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); + int64_t bucketEndTime = fullBucketEndTimeNs; int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); - if (numBucketsForward > 1) { + + // Skip buckets if this is a pulled metric or a pushed metric that is diffed. + if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) { + VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); // Something went wrong. Maybe the device was sleeping for a long time. It is better // to mark the current bucket as invalid. The last pull might have been successful through. invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::MULTIPLE_BUCKETS_SKIPPED); + // End the bucket at the next bucket start time so the entire interval is skipped. + bucketEndTime = nextBucketStartTimeNs; + } else if (eventTimeNs < fullBucketEndTimeNs) { + bucketEndTime = eventTimeNs; } - VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, - (int)mCurrentSlicedBucket.size()); - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs; // Close the current bucket. int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime); bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; if (!isBucketLargeEnough) { skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL); } - bool bucketHasData = false; if (!mCurrentBucketIsSkipped) { + bool bucketHasData = false; // The current bucket is large enough to keep. for (const auto& slice : mCurrentSlicedBucket) { ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second); @@ -981,22 +995,22 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, bucketHasData = true; } } - } - - if (!bucketHasData && !mCurrentBucketIsSkipped) { - skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA); + if (!bucketHasData) { + skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA); + } } if (mCurrentBucketIsSkipped) { mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; - // Fill in the gap if we skipped multiple buckets. - mCurrentSkippedBucket.bucketEndTimeNs = - numBucketsForward > 1 ? nextBucketStartTimeNs : bucketEndTime; + mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; mSkippedBuckets.emplace_back(mCurrentSkippedBucket); } // This means that the current bucket was not flushed before a forced bucket split. - if (bucketEndTime < nextBucketStartTimeNs && numBucketsForward <= 1) { + // This can happen if an app update or a dump report with include_current_partial_bucket is + // requested before we get a chance to flush the bucket due to receiving new data, either from + // the statsd socket or the StatsPullerManager. + if (bucketEndTime < nextBucketStartTimeNs) { SkippedBucket bucketInGap; bucketInGap.bucketStartTimeNs = bucketEndTime; bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs; @@ -1005,7 +1019,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, mSkippedBuckets.emplace_back(bucketInGap); } - appendToFullBucket(eventTimeNs, fullBucketEndTimeNs); + appendToFullBucket(eventTimeNs > fullBucketEndTimeNs); initCurrentSlicedBucket(nextBucketStartTimeNs); // Update the condition timer again, in case we skipped buckets. mConditionTimer.newBucketStart(nextBucketStartTimeNs); @@ -1055,7 +1069,7 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) } else { it++; } - // TODO: remove mCurrentBaseInfo entries when obsolete + // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete } mCurrentBucketIsSkipped = false; @@ -1071,8 +1085,7 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) (long long)mCurrentBucketStartTimeNs); } -void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) { - bool isFullBucketReached = eventTimeNs > fullBucketEndTimeNs; +void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { if (mCurrentBucketIsSkipped) { if (isFullBucketReached) { // If the bucket is invalid, we ignore the full bucket since it contains invalid data. diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index bb4a66164860..b359af745c91 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -52,6 +52,7 @@ class ValueMetricProducer : public virtual MetricProducer, public virtual PullDa public: ValueMetricProducer( const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, @@ -74,7 +75,7 @@ public: if (!mSplitBucketForAppUpgrade) { return; } - if (mIsPulled && mCondition) { + if (mIsPulled && mCondition == ConditionState::kTrue) { pullAndMatchEventsLocked(eventTimeNs); } flushCurrentBucketLocked(eventTimeNs, eventTimeNs); @@ -83,7 +84,7 @@ public: // ValueMetric needs special logic if it's a pulled atom. void onStatsdInitCompleted(const int64_t& eventTimeNs) override { std::lock_guard<std::mutex> lock(mMutex); - if (mIsPulled && mCondition) { + if (mIsPulled && mCondition == ConditionState::kTrue) { pullAndMatchEventsLocked(eventTimeNs); } flushCurrentBucketLocked(eventTimeNs, eventTimeNs); @@ -142,8 +143,10 @@ private: // Mark the data as invalid. void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); + void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, const BucketDropReason reason); + // Skips the current bucket without notifying StatsdStats of the skipped bucket. // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that // causes the bucket to be invalidated will not notify StatsdStats. @@ -209,6 +212,7 @@ private: // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); + bool hasReachedGuardRailLimit() const; bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); @@ -220,8 +224,10 @@ private: ValueBucket buildPartialBucket(int64_t bucketEndTime, const std::vector<Interval>& intervals); + void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs); - void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs); + + void appendToFullBucket(const bool isFullBucketReached); // Reset diff base and mHasGlobalBase void resetBase(); @@ -307,6 +313,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestSlicedState); FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap); FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition); FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 19b2fe89989d..0d49bbc269a3 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -329,7 +329,10 @@ void OringDurationTracker::onConditionChanged(bool condition, const int64_t time void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId, const FieldValue& newState) { - // If no keys are being tracked, update the current state key and return. + // Nothing needs to be done on a state change if we have not seen a start + // event, the metric is currently not active, or condition is false. + // For these cases, no keys are being tracked in mStarted, so update + // the current state key and return. if (mStarted.empty()) { updateCurrentStateKey(atomId, newState); return; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 210d382b1363..8917c36bb608 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -285,11 +285,14 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, const unordered_map<int64_t, int>& logTrackerMap, unordered_map<int64_t, int>& conditionTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, - unordered_map<int, std::vector<int>>& trackerToConditionMap) { + unordered_map<int, std::vector<int>>& trackerToConditionMap, + vector<ConditionState>& initialConditionCache) { vector<Predicate> conditionConfigs; const int conditionTrackerCount = config.predicate_size(); conditionConfigs.reserve(conditionTrackerCount); allConditionTrackers.reserve(conditionTrackerCount); + initialConditionCache.reserve(conditionTrackerCount); + std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown); for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); @@ -321,7 +324,7 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, for (size_t i = 0; i < allConditionTrackers.size(); i++) { auto& conditionTracker = allConditionTrackers[i]; if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, - stackTracker)) { + stackTracker, initialConditionCache)) { return false; } for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) { @@ -351,14 +354,14 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt } bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, - const sp<StatsPullerManager>& pullerManager, + const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, const unordered_map<int64_t, int>& logTrackerMap, const unordered_map<int64_t, int>& conditionTrackerMap, const vector<sp<LogMatchingTracker>>& allAtomMatchers, const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, + const vector<ConditionState>& initialConditionCache, vector<sp<MetricProducer>>& allMetricProducers, unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& trackerToMetricMap, @@ -441,9 +444,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t eventDeactivationMap); if (!success) return false; - sp<MetricProducer> countProducer = new CountMetricProducer( - key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); + sp<MetricProducer> countProducer = + new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, + timeBaseTimeNs, currentTimeNs, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(countProducer); } @@ -547,10 +551,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (!success) return false; sp<MetricProducer> durationMetric = new DurationMetricProducer( - key, metric, conditionIndex, trackerIndices[0], trackerIndices[1], - trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, - currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap); + key, metric, conditionIndex, initialConditionCache, trackerIndices[0], + trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions, + timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap, + slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(durationMetric); } @@ -593,9 +597,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t eventDeactivationMap); if (!success) return false; - sp<MetricProducer> eventMetric = new EventMetricProducer( - key, metric, conditionIndex, wizard, timeBaseTimeNs, eventActivationMap, - eventDeactivationMap); + sp<MetricProducer> eventMetric = + new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, + timeBaseTimeNs, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(eventMetric); } @@ -683,9 +687,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (!success) return false; sp<MetricProducer> valueProducer = new ValueMetricProducer( - key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, - timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap); + key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, + matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(valueProducer); } @@ -778,9 +782,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (!success) return false; sp<MetricProducer> gaugeProducer = new GaugeMetricProducer( - key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, - triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, pullerManager, - eventActivationMap, eventDeactivationMap); + key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, + matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, + pullerManager, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(gaugeProducer); } for (int i = 0; i < config.no_report_metric_size(); ++i) { @@ -930,6 +934,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& std::set<int64_t>& noReportMetricIds) { unordered_map<int64_t, int> logTrackerMap; unordered_map<int64_t, int> conditionTrackerMap; + vector<ConditionState> initialConditionCache; unordered_map<int64_t, int> metricProducerMap; unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; @@ -941,7 +946,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& VLOG("initLogMatchingTrackers succeed..."); if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers, - trackerToConditionMap)) { + trackerToConditionMap, initialConditionCache)) { ALOGE("initConditionTrackers failed"); return false; } @@ -952,10 +957,10 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& } if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap, conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, - allConditionTrackers, allMetricProducers, - conditionToMetricMap, trackerToMetricMap, metricProducerMap, - noReportMetricIds, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + allConditionTrackers, initialConditionCache, allMetricProducers, + conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)) { ALOGE("initMetricProducers failed"); return false; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 6af7a9adca20..96b5c26ff789 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -60,12 +60,14 @@ bool initLogTrackers(const StatsdConfig& config, // [allConditionTrackers]: stores the sp to all the ConditionTrackers // [trackerToConditionMap]: contain the mapping from index of // log tracker to condition trackers that use the log tracker +// [initialConditionCache]: stores the initial conditions for each ConditionTracker bool initConditions(const ConfigKey& key, const StatsdConfig& config, const std::unordered_map<int64_t, int>& logTrackerMap, std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks); + std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, + std::vector<ConditionState>& initialConditionCache); // Initialize State maps using State protos in the config. These maps will // eventually be passed to MetricProducers to initialize their state info. @@ -103,6 +105,7 @@ bool initMetrics( const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, + const std::vector<ConditionState>& initialConditionCache, std::vector<sp<MetricProducer>>& allMetricProducers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp index 361b161c76ac..fd883c29dba0 100644 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ b/cmds/statsd/src/shell/ShellSubscriber.cpp @@ -41,13 +41,8 @@ void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) { { std::unique_lock<std::mutex> lock(mMutex); - if (myToken != mToken) { - // Some other subscription has already come in. Stop. - return; - } mSubscriptionInfo = mySubscriptionInfo; - - spawnHelperThreadsLocked(mySubscriptionInfo, myToken); + spawnHelperThread(myToken); waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec); if (mSubscriptionInfo == mySubscriptionInfo) { @@ -57,14 +52,9 @@ void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) { } } -void ShellSubscriber::spawnHelperThreadsLocked(shared_ptr<SubscriptionInfo> myInfo, int myToken) { - if (!myInfo->mPulledInfo.empty() && myInfo->mPullIntervalMin > 0) { - std::thread puller([this, myToken] { startPull(myToken); }); - puller.detach(); - } - - std::thread heartbeatSender([this, myToken] { sendHeartbeats(myToken); }); - heartbeatSender.detach(); +void ShellSubscriber::spawnHelperThread(int myToken) { + std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); }); + t.detach(); } void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo, @@ -114,13 +104,7 @@ bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) subscriptionInfo->mPushedMatchers.push_back(pushed); } - int minInterval = -1; for (const auto& pulled : config.pulled()) { - // All intervals need to be multiples of the min interval. - if (minInterval < 0 || pulled.freq_millis() < minInterval) { - minInterval = pulled.freq_millis(); - } - vector<string> packages; vector<int32_t> uids; for (const string& pkg : pulled.packages()) { @@ -136,18 +120,18 @@ bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) uids); VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id()); } - subscriptionInfo->mPullIntervalMin = minInterval; return true; } -void ShellSubscriber::startPull(int myToken) { - VLOG("ShellSubscriber: pull thread %d starting", myToken); +void ShellSubscriber::pullAndSendHeartbeats(int myToken) { + VLOG("ShellSubscriber: helper thread %d starting", myToken); while (true) { + int64_t sleepTimeMs = INT_MAX; { std::lock_guard<std::mutex> lock(mMutex); if (!mSubscriptionInfo || mToken != myToken) { - VLOG("ShellSubscriber: pulling thread %d done!", myToken); + VLOG("ShellSubscriber: helper thread %d done!", myToken); return; } @@ -168,11 +152,27 @@ void ShellSubscriber::startPull(int myToken) { pullInfo.mPrevPullElapsedRealtimeMs = nowMillis; } + + // Send a heartbeat, consisting of a data size of 0, if perfd hasn't recently received + // data from statsd. When it receives the data size of 0, perfd will not expect any + // atoms and recheck whether the subscription should end. + if (nowMillis - mLastWriteMs > kMsBetweenHeartbeats) { + attemptWriteToPipeLocked(/*dataSize=*/0); + } + + // Determine how long to sleep before doing more work. + for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) { + int64_t nextPullTime = pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval; + int64_t timeBeforePull = nextPullTime - nowMillis; // guaranteed to be non-negative + if (timeBeforePull < sleepTimeMs) sleepTimeMs = timeBeforePull; + } + int64_t timeBeforeHeartbeat = (mLastWriteMs + kMsBetweenHeartbeats) - nowMillis; + if (timeBeforeHeartbeat < sleepTimeMs) sleepTimeMs = timeBeforeHeartbeat; } - VLOG("ShellSubscriber: pulling thread %d sleeping for %d ms", myToken, - mSubscriptionInfo->mPullIntervalMin); - std::this_thread::sleep_for(std::chrono::milliseconds(mSubscriptionInfo->mPullIntervalMin)); + VLOG("ShellSubscriber: helper thread %d sleeping for %lld ms", myToken, + (long long)sleepTimeMs); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); } } @@ -200,7 +200,7 @@ void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEve } } - if (count > 0) attemptWriteToSocketLocked(mProto.size()); + if (count > 0) attemptWriteToPipeLocked(mProto.size()); } void ShellSubscriber::onLogEvent(const LogEvent& event) { @@ -214,26 +214,24 @@ void ShellSubscriber::onLogEvent(const LogEvent& event) { util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM); event.ToProto(mProto); mProto.end(atomToken); - attemptWriteToSocketLocked(mProto.size()); + attemptWriteToPipeLocked(mProto.size()); } } } -// Tries to write the atom encoded in mProto to the socket. If the write fails +// Tries to write the atom encoded in mProto to the pipe. If the write fails // because the read end of the pipe has closed, signals to other threads that // the subscription should end. -void ShellSubscriber::attemptWriteToSocketLocked(size_t dataSize) { - // First write the payload size. +void ShellSubscriber::attemptWriteToPipeLocked(size_t dataSize) { + // First, write the payload size. if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) { mSubscriptionInfo->mClientAlive = false; mSubscriptionShouldEnd.notify_one(); return; } - if (dataSize == 0) return; - - // Then, write the payload. - if (!mProto.flush(mSubscriptionInfo->mOutputFd)) { + // Then, write the payload if this is not just a heartbeat. + if (dataSize > 0 && !mProto.flush(mSubscriptionInfo->mOutputFd)) { mSubscriptionInfo->mClientAlive = false; mSubscriptionShouldEnd.notify_one(); return; @@ -242,28 +240,6 @@ void ShellSubscriber::attemptWriteToSocketLocked(size_t dataSize) { mLastWriteMs = getElapsedRealtimeMillis(); } -// Send a heartbeat, consisting solely of a data size of 0, if perfd has not -// recently received any writes from statsd. When it receives the data size of -// 0, perfd will not expect any data and recheck whether the shell command is -// still running. -void ShellSubscriber::sendHeartbeats(int myToken) { - while (true) { - { - std::lock_guard<std::mutex> lock(mMutex); - if (!mSubscriptionInfo || myToken != mToken) { - VLOG("ShellSubscriber: heartbeat thread %d done!", myToken); - return; - } - - if (getElapsedRealtimeMillis() - mLastWriteMs > kMsBetweenHeartbeats) { - VLOG("ShellSubscriber: sending a heartbeat to perfd"); - attemptWriteToSocketLocked(/*dataSize=*/0); - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(kMsBetweenHeartbeats)); - } -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h index 26c8a2a0b683..4c05fa7f71c2 100644 --- a/cmds/statsd/src/shell/ShellSubscriber.h +++ b/cmds/statsd/src/shell/ShellSubscriber.h @@ -92,7 +92,6 @@ private: int mOutputFd; std::vector<SimpleAtomMatcher> mPushedMatchers; std::vector<PullInfo> mPulledInfo; - int mPullIntervalMin; bool mClientAlive; }; @@ -100,27 +99,25 @@ private: bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo); - void spawnHelperThreadsLocked(std::shared_ptr<SubscriptionInfo> myInfo, int myToken); + void spawnHelperThread(int myToken); void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo, int myToken, std::unique_lock<std::mutex>& lock, int timeoutSec); - void startPull(int myToken); + // Helper thread that pulls atoms at a regular frequency and sends + // heartbeats to perfd if statsd hasn't recently sent any data. Statsd must + // send heartbeats for perfd to escape a blocking read call and recheck if + // the user has terminated the subscription. + void pullAndSendHeartbeats(int myToken); void writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data, const SimpleAtomMatcher& matcher); void getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo); - void attemptWriteToSocketLocked(size_t dataSize); - - // Send ocassional heartbeats for two reasons: (a) for statsd to detect when - // the read end of the pipe has closed and (b) for perfd to escape a - // blocking read call and recheck if the user has terminated the - // subscription. - void sendHeartbeats(int myToken); + void attemptWriteToPipeLocked(size_t dataSize); sp<UidMap> mUidMap; diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp index 5514b446b306..c29afeb794fa 100644 --- a/cmds/statsd/src/state/StateManager.cpp +++ b/cmds/statsd/src/state/StateManager.cpp @@ -19,10 +19,18 @@ #include "StateManager.h" +#include <private/android_filesystem_config.h> + namespace android { namespace os { namespace statsd { +StateManager::StateManager() + : mAllowedPkg({ + "com.android.systemui", + }) { +} + StateManager& StateManager::getInstance() { static StateManager sStateManager; return sStateManager; @@ -33,8 +41,14 @@ void StateManager::clear() { } void StateManager::onLogEvent(const LogEvent& event) { - if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { - mStateTrackers[event.GetTagId()]->onLogEvent(event); + // Only process state events from uids in AID_* and packages that are whitelisted in + // mAllowedPkg. + // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000) + if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) || + mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) { + if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { + mStateTrackers[event.GetTagId()]->onLogEvent(event); + } } } @@ -79,6 +93,20 @@ bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKe return false; } +void StateManager::updateLogSources(const sp<UidMap>& uidMap) { + mAllowedLogSources.clear(); + for (const auto& pkg : mAllowedPkg) { + auto uids = uidMap->getAppUid(pkg); + mAllowedLogSources.insert(uids.begin(), uids.end()); + } +} + +void StateManager::notifyAppChanged(const string& apk, const sp<UidMap>& uidMap) { + if (mAllowedPkg.find(apk) != mAllowedPkg.end()) { + updateLogSources(uidMap); + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h index 577a0f51e38b..18c404c29c4e 100644 --- a/cmds/statsd/src/state/StateManager.h +++ b/cmds/statsd/src/state/StateManager.h @@ -18,7 +18,12 @@ #include <inttypes.h> #include <utils/RefBase.h> +#include <set> +#include <string> +#include <unordered_map> + #include "HashableDimensionKey.h" +#include "packages/UidMap.h" #include "state/StateListener.h" #include "state/StateTracker.h" @@ -32,7 +37,7 @@ namespace statsd { */ class StateManager : public virtual RefBase { public: - StateManager(){}; + StateManager(); ~StateManager(){}; @@ -62,6 +67,11 @@ public: bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, FieldValue* output) const; + // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log. + void updateLogSources(const sp<UidMap>& uidMap); + + void notifyAppChanged(const string& apk, const sp<UidMap>& uidMap); + inline int getStateTrackersCount() const { return mStateTrackers.size(); } @@ -79,6 +89,13 @@ private: // Maps state atom ids to StateTrackers std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers; + + // The package names that can log state events. + const std::set<std::string> mAllowedPkg; + + // The combined uid sources (after translating pkg name to uid). + // State events from uids that are not in the list will be ignored to avoid state pollution. + std::set<int32_t> mAllowedLogSources; }; } // namespace statsd diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 6bfa26761b2f..ddd2725c9cb9 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -467,6 +467,11 @@ message StatsdStatsReport { optional int64 binder_call_failed = 19; optional int64 failed_uid_provider_not_found = 20; optional int64 puller_not_found = 21; + message PullTimeoutMetadata { + optional int64 pull_timeout_uptime_millis = 1; + optional int64 pull_timeout_elapsed_millis = 2; + } + repeated PullTimeoutMetadata pull_atom_metadata = 22; } repeated PulledAtomStats pulled_atom_stats = 10; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index bafdfcba59b2..423bae8bc0a4 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -81,6 +81,9 @@ const int FIELD_ID_ATOM_ERROR_COUNT = 18; const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19; const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20; const int FIELD_ID_PULLER_NOT_FOUND = 21; +const int FIELD_ID_PULL_TIMEOUT_METADATA = 22; +const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1; +const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2; // for AtomMetricStats proto const int FIELD_ID_ATOM_METRIC_STATS = 17; @@ -497,6 +500,16 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> (long long)pair.second.pullUidProviderNotFound); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND, (long long)pair.second.pullerNotFound); + for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) { + uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE | + FIELD_ID_PULL_TIMEOUT_METADATA | + FIELD_COUNT_REPEATED); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS, + pullTimeoutMetadata.pullTimeoutUptimeMillis); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS, + pullTimeoutMetadata.pullTimeoutElapsedMillis); + protoOutput->end(timeoutMetadataToken); + } protoOutput->end(token); } @@ -542,6 +555,10 @@ int64_t getElapsedRealtimeMillis() { return ::android::elapsedRealtime(); } +int64_t getSystemUptimeMillis() { + return ::android::uptimeMillis(); +} + int64_t getWallClockNs() { return time(nullptr) * NS_PER_SEC; } diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index 20d93b5a5365..eb65dc6979c5 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -61,6 +61,9 @@ int64_t getElapsedRealtimeMillis(); // Gets the elapsed timestamp in seconds. int64_t getElapsedRealtimeSec(); +// Gets the system uptime in millis. +int64_t getSystemUptimeMillis(); + // Gets the wall clock timestamp in ns. int64_t getWallClockNs(); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 72decf2c7bd0..acdffd3d4712 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -198,6 +198,9 @@ message EventMetric { optional int64 condition = 3; repeated MetricConditionLink links = 4; + + reserved 100; + reserved 101; } message CountMetric { @@ -218,6 +221,9 @@ message CountMetric { repeated MetricStateLink state_link = 9; optional FieldMatcher dimensions_in_condition = 7 [deprecated = true]; + + reserved 100; + reserved 101; } message DurationMetric { @@ -245,6 +251,9 @@ message DurationMetric { optional TimeUnit bucket = 7; optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; + + reserved 100; + reserved 101; } message GaugeMetric { @@ -281,6 +290,9 @@ message GaugeMetric { optional int32 max_pull_delay_sec = 13 [default = 30]; optional bool split_bucket_for_app_upgrade = 14 [default = true]; + + reserved 100; + reserved 101; } message ValueMetric { @@ -333,6 +345,9 @@ message ValueMetric { optional bool split_bucket_for_app_upgrade = 17 [default = true]; optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; + + reserved 100; + reserved 101; } message Alert { diff --git a/cmds/statsd/statsd_test.xml b/cmds/statsd/statsd_test.xml new file mode 100644 index 000000000000..8f9bb1cb6b2a --- /dev/null +++ b/cmds/statsd/statsd_test.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 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. +--> +<configuration description="Runs statsd_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + <option name="test-suite-tag" value="mts" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="statsd_test->/data/local/tmp/statsd_test" /> + <option name="append-bitness" value="true" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="statsd_test" /> + </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.os.statsd" /> + </object> +</configuration> diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index 23f8ca4e74e6..a21eb9b9147f 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -33,6 +33,12 @@ namespace android { namespace os { namespace statsd { +// These constants must be kept in sync with those in StatsDimensionsValue.java. +const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2; +const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3; +const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6; +const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7; + namespace { void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, const vector<int>& attributionUids, const vector<string>& attributionTags, @@ -291,34 +297,76 @@ TEST(AtomMatcherTest, TestWriteDimensionPath) { } } -//TODO(b/149050405) Update this test for StatsDimensionValueParcel -//TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { -// HashableDimensionKey dim; -// -// int pos1[] = {1, 1, 1}; -// int pos2[] = {1, 1, 2}; -// int pos3[] = {1, 1, 3}; -// int pos4[] = {2, 0, 0}; -// -// Field field1(10, pos1, 2); -// Field field2(10, pos2, 2); -// Field field3(10, pos3, 2); -// Field field4(10, pos4, 0); -// -// Value value1((int32_t)10025); -// Value value2("tag"); -// Value value3((int32_t)987654); -// Value value4((int32_t)99999); -// -// dim.addValue(FieldValue(field1, value1)); -// dim.addValue(FieldValue(field2, value2)); -// dim.addValue(FieldValue(field3, value3)); -// dim.addValue(FieldValue(field4, value4)); -// -// SubscriberReporter::getStatsDimensionsValue(dim); -// // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't -// // have any read api. -//} +void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel, + int32_t nodeDepthInAttributionChain, + int32_t uid, string tag) { + EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/); + ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); + ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2); + + StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0]; + EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/); + EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE); + EXPECT_EQ(uidParcel.intValue, uid); + + StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1]; + EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/); + EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE); + EXPECT_EQ(tagParcel.stringValue, tag); +} + +// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel +TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { + int atomId = 10; + // First four fields form an attribution chain + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 2, 1}; + int pos4[] = {1, 2, 2}; + int pos5[] = {2, 1, 1}; + + Field field1(atomId, pos1, /*depth=*/2); + Field field2(atomId, pos2, /*depth=*/2); + Field field3(atomId, pos3, /*depth=*/2); + Field field4(atomId, pos4, /*depth=*/2); + Field field5(atomId, pos5, /*depth=*/0); + + Value value1((int32_t)1); + Value value2("string2"); + Value value3((int32_t)3); + Value value4("string4"); + Value value5((float)5.0); + + HashableDimensionKey dimensionKey; + dimensionKey.addValue(FieldValue(field1, value1)); + dimensionKey.addValue(FieldValue(field2, value2)); + dimensionKey.addValue(FieldValue(field3, value3)); + dimensionKey.addValue(FieldValue(field4, value4)); + dimensionKey.addValue(FieldValue(field5, value5)); + + StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel(); + EXPECT_EQ(rootParcel.field, atomId); + ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); + ASSERT_EQ(rootParcel.tupleValue.size(), 2); + + // Check that attribution chain is populated correctly + StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0]; + EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/); + ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); + ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2); + checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0], + /*nodeDepthInAttributionChain=*/1, + value1.int_value, value2.str_value); + checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1], + /*nodeDepthInAttributionChain=*/2, + value3.int_value, value4.str_value); + + // Check that the float is populated correctly + StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1]; + EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/); + EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE); + EXPECT_EQ(floatParcel.floatValue, value5.float_value); +} TEST(AtomMatcherTest, TestWriteDimensionToProto) { HashableDimensionKey dim; diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index b3b095bf4bff..6259757fe092 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -276,11 +276,157 @@ StatsdConfig buildCirclePredicates() { return config; } +StatsdConfig buildConfigWithDifferentPredicates() { + StatsdConfig config; + config.set_id(12345); + + auto pulledAtomMatcher = + CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE); + *config.add_atom_matcher() = pulledAtomMatcher; + auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = screenOnAtomMatcher; + auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOffAtomMatcher; + auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher(); + *config.add_atom_matcher() = batteryNoneAtomMatcher; + auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher(); + *config.add_atom_matcher() = batteryUsbAtomMatcher; + + // Simple condition with InitialValue set to default (unknown). + auto screenOnUnknownPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = screenOnUnknownPredicate; + + // Simple condition with InitialValue set to false. + auto screenOnFalsePredicate = config.add_predicate(); + screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse")); + SimplePredicate* simpleScreenOnFalsePredicate = + screenOnFalsePredicate->mutable_simple_predicate(); + simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id()); + simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id()); + simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); + + // Simple condition with InitialValue set to false. + auto onBatteryFalsePredicate = config.add_predicate(); + onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse")); + SimplePredicate* simpleOnBatteryFalsePredicate = + onBatteryFalsePredicate->mutable_simple_predicate(); + simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id()); + simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id()); + simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); + + // Combination condition with both simple condition InitialValues set to false. + auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate(); + screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse")); + screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation( + LogicalOperation::AND); + addPredicateToPredicateCombination(*screenOnFalsePredicate, + screenOnFalseOnBatteryFalsePredicate); + addPredicateToPredicateCombination(*onBatteryFalsePredicate, + screenOnFalseOnBatteryFalsePredicate); + + // Combination condition with one simple condition InitialValue set to unknown and one set to + // false. + auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate(); + screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse")); + screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation( + LogicalOperation::AND); + addPredicateToPredicateCombination(screenOnUnknownPredicate, + screenOnUnknownOnBatteryFalsePredicate); + addPredicateToPredicateCombination(*onBatteryFalsePredicate, + screenOnUnknownOnBatteryFalsePredicate); + + // Simple condition metric with initial value false. + ValueMetric* metric1 = config.add_value_metric(); + metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse")); + metric1->set_what(pulledAtomMatcher.id()); + *metric1->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + metric1->set_bucket(FIVE_MINUTES); + metric1->set_condition(screenOnFalsePredicate->id()); + + // Simple condition metric with initial value unknown. + ValueMetric* metric2 = config.add_value_metric(); + metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown")); + metric2->set_what(pulledAtomMatcher.id()); + *metric2->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + metric2->set_bucket(FIVE_MINUTES); + metric2->set_condition(screenOnUnknownPredicate.id()); + + // Combination condition metric with initial values false and false. + ValueMetric* metric3 = config.add_value_metric(); + metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse")); + metric3->set_what(pulledAtomMatcher.id()); + *metric3->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + metric3->set_bucket(FIVE_MINUTES); + metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id()); + + // Combination condition metric with initial values unknown and false. + ValueMetric* metric4 = config.add_value_metric(); + metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse")); + metric4->set_what(pulledAtomMatcher.id()); + *metric4->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + metric4->set_bucket(FIVE_MINUTES); + metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id()); + + return config; +} + bool isSubset(const set<int32_t>& set1, const set<int32_t>& set2) { return std::includes(set2.begin(), set2.end(), set1.begin(), set1.end()); } } // anonymous namespace +TEST(MetricsManagerTest, TestInitialConditions) { + UidMap uidMap; + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> periodicAlarmMonitor; + StatsdConfig config = buildConfigWithDifferentPredicates(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + std::vector<sp<AnomalyTracker>> allAnomalyTrackers; + std::vector<sp<AlarmTracker>> allAlarmTrackers; + unordered_map<int, std::vector<int>> conditionToMetricMap; + unordered_map<int, std::vector<int>> trackerToMetricMap; + unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; + vector<int> metricsWithActivation; + std::set<int64_t> noReportMetricIds; + + EXPECT_TRUE(initStatsdConfig( + kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, + allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, + trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + noReportMetricIds)); + ASSERT_EQ(4u, allMetricProducers.size()); + ASSERT_EQ(5u, allConditionTrackers.size()); + + ConditionKey queryKey; + vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated); + + allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); + allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); + EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); + EXPECT_EQ(ConditionState::kFalse, conditionCache[1]); + EXPECT_EQ(ConditionState::kFalse, conditionCache[2]); + EXPECT_EQ(ConditionState::kFalse, conditionCache[3]); + EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]); + + EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition); + EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition); + EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition); + EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition); +} + TEST(MetricsManagerTest, TestGoodConfig) { UidMap uidMap; sp<StatsPullerManager> pullerManager = new StatsPullerManager(); diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 076f32752223..1e6680c47567 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -13,6 +13,11 @@ // limitations under the License. #include "StatsLogProcessor.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <stdio.h> + #include "StatsService.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" @@ -20,16 +25,10 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "packages/UidMap.h" -#include "storage/StorageManager.h" #include "statslog_statsdtest.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - +#include "storage/StorageManager.h" #include "tests/statsd_test_util.h" -#include <stdio.h> - using namespace android; using namespace testing; using ::ndk::SharedRefBase; @@ -324,6 +323,41 @@ TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) { EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); } +TEST(StatsLogProcessorTest, InvalidConfigRemoved) { + // Setup simple config key corresponding to empty config. + StatsdStats::getInstance().reset(); + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, + {String16("p1"), String16("p2")}, {String16(""), String16("")}); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); + ConfigKey key(3, 4); + StatsdConfig config = MakeConfig(true); + p.OnConfigUpdated(0, key, config); + EXPECT_EQ(1, p.mMetricsManagers.size()); + EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end()); + // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset. + EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(), + StatsdStats::getInstance().mConfigStats.find(key)); + EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size()); + + StatsdConfig invalidConfig = MakeConfig(true); + invalidConfig.clear_allowed_log_source(); + p.OnConfigUpdated(0, key, invalidConfig); + EXPECT_EQ(0, p.mMetricsManagers.size()); + // The current configs should not contain the invalid config. + EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(), + StatsdStats::getInstance().mConfigStats.find(key)); + // Both "config" and "invalidConfig" should be in the icebox. + EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size()); + +} + + TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { int uid = 1111; @@ -1796,6 +1830,53 @@ TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionCha EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); } +TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) { + int hostUid = 20; + int isolatedUid = 30; + sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); + ConfigKey key(3, 4); + + // TODO: All tests should not persist state on disk. This removes any reports that were present. + ProtoOutputStream proto; + StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false); + + StatsdConfig config = MakeConfig(false); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap); + vector<uint8_t> bytes; + + int64_t dumpTime1Ns = 1 * NS_PER_SEC; + processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */, + true /* erase_data */, ADB_DUMP, FAST, &bytes); + + ConfigMetricsReportList output; + output.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_EQ(output.reports_size(), 1); + EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns); + + int64_t dumpTime2Ns = 5 * NS_PER_SEC; + processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */, + false /* erase_data */, ADB_DUMP, FAST, &bytes); + + // Check that the dump report without clearing data is successful. + output.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_EQ(output.reports_size(), 1); + EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns); + EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); + + int64_t dumpTime3Ns = 10 * NS_PER_SEC; + processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */, + true /* erase_data */, ADB_DUMP, FAST, &bytes); + + // Check that the previous dump report that didn't clear data did not overwrite the first dump's + // timestamps. + output.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_EQ(output.reports_size(), 1); + EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns); + EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); + +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp index 322cfaf68a41..64ea219c8465 100644 --- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp @@ -43,23 +43,47 @@ TEST(AlarmTrackerTest, TestTriggerTimestamp) { alarm.set_offset_millis(15 * MS_PER_SEC); alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr int64_t startMillis = 100000000 * MS_PER_SEC; + int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15; AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); EXPECT_TRUE(firedAlarmSet.empty()); tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); currentTimeSec = startMillis / MS_PER_SEC + 7000; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); ASSERT_EQ(firedAlarmSet.size(), 1u); tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); + + // Alarm fires exactly on time. + currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); + ASSERT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); + + // Alarm fires exactly 1 period late. + currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); + ASSERT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); } } // namespace statsd diff --git a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp index 6529d65a5825..1d501fd5a87c 100644 --- a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp @@ -24,6 +24,7 @@ using namespace android::os::statsd; using std::vector; #ifdef __ANDROID__ + TEST(ConditionTrackerTest, TestUnknownCondition) { LogicalOperation operation = LogicalOperation::AND; diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 86e24fb0c80b..07b5311b1207 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -112,6 +112,114 @@ std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey( return outputKeyMap; } +TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueFalse) { + SimplePredicate simplePredicate; + simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); + simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); + simplePredicate.set_count_nesting(false); + simplePredicate.set_initial_value(SimplePredicate_InitialValue_FALSE); + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; + trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), + 0 /*tracker index*/, simplePredicate, + trackerNameIndexMap); + + ConditionKey queryKey; + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + + // Check that initial condition is false. + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + + vector<MatchingState> matcherState; + vector<bool> changedCache(1, false); + + // Matched stop event. + // Check that condition is still false. + unique_ptr<LogEvent> screenOffEvent = + CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched + matcherState.push_back(MatchingState::kMatched); // Off matcher matched + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + EXPECT_FALSE(changedCache[0]); + + // Matched start event. + // Check that condition has changed to true. + unique_ptr<LogEvent> screenOnEvent = + CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); // On matcher matched + matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + EXPECT_TRUE(changedCache[0]); +} + +TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueUnknown) { + SimplePredicate simplePredicate; + simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); + simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); + simplePredicate.set_count_nesting(false); + simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; + trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), + 0 /*tracker index*/, simplePredicate, + trackerNameIndexMap); + + ConditionKey queryKey; + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + + // Check that initial condition is unknown. + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); + + vector<MatchingState> matcherState; + vector<bool> changedCache(1, false); + + // Matched stop event. + // Check that condition is changed to false. + unique_ptr<LogEvent> screenOffEvent = + CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched + matcherState.push_back(MatchingState::kMatched); // Off matcher matched + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + EXPECT_TRUE(changedCache[0]); + + // Matched start event. + // Check that condition has changed to true. + unique_ptr<LogEvent> screenOnEvent = + CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); // On matcher matched + matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + EXPECT_TRUE(changedCache[0]); +} + TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { SimplePredicate simplePredicate; simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp index 13cdfc292d49..85a60886349e 100644 --- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp @@ -201,9 +201,9 @@ TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) { int64_t actualPullDurationNs = endTimeNs - startTimeNs; // Pull should take at least the timeout amount of time, but should stop early because the delay - // is bigger. + // is bigger. Make sure that the time is closer to the timeout, than to the intended delay. EXPECT_LT(pullTimeoutNs, actualPullDurationNs); - EXPECT_GT(pullDelayNs, actualPullDurationNs); + EXPECT_GT(pullDelayNs / 5, actualPullDurationNs); ASSERT_EQ(0, dataHolder.size()); // Let the pull return and make sure that the dataHolder is not modified. diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 5cc10cd9840c..428c46f8a0d2 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -306,6 +306,8 @@ TEST(StatsdStatsTest, TestPullAtomStats) { stats.notePullUidProviderNotFound(util::DISK_SPACE); stats.notePullerNotFound(util::DISK_SPACE); stats.notePullerNotFound(util::DISK_SPACE); + stats.notePullTimeout(util::DISK_SPACE, 3000L, 6000L); + stats.notePullTimeout(util::DISK_SPACE, 4000L, 7000L); vector<uint8_t> output; stats.dumpStats(&output, false); @@ -328,6 +330,13 @@ TEST(StatsdStatsTest, TestPullAtomStats) { EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed()); EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found()); EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found()); + ASSERT_EQ(2, report.pulled_atom_stats(0).pull_atom_metadata_size()); + EXPECT_EQ(3000L, report.pulled_atom_stats(0).pull_atom_metadata(0).pull_timeout_uptime_millis()); + EXPECT_EQ(4000L, report.pulled_atom_stats(0).pull_atom_metadata(1).pull_timeout_uptime_millis()); + EXPECT_EQ(6000L, report.pulled_atom_stats(0).pull_atom_metadata(0) + .pull_timeout_elapsed_millis()); + EXPECT_EQ(7000L, report.pulled_atom_stats(0).pull_atom_metadata(1) + .pull_timeout_elapsed_millis()); } TEST(StatsdStatsTest, TestAtomMetricsStats) { diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 74ecaac0f9e3..bb8e7bfd90f4 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -74,8 +74,8 @@ TEST(CountMetricProducerTest, TestFirstBucket) { metric.set_bucket(ONE_MINUTE); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, countProducer.mCurrentBucketNum); EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); @@ -94,8 +94,8 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - bucketStartTimeNs, bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, bucketStartTimeNs, bucketStartTimeNs); // 2 events in bucket 1. LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -157,8 +157,8 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, - bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, + bucketStartTimeNs, bucketStartTimeNs); countProducer.onConditionChanged(true, bucketStartTimeNs); @@ -220,12 +220,14 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); - CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, - bucketStartTimeNs, bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/, + {ConditionState::kUnknown}, wizard, bucketStartTimeNs, + bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); @@ -261,7 +263,8 @@ TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { alert.set_trigger_if_sum_gt(2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); @@ -327,7 +330,8 @@ TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { metric.set_bucket(ONE_MINUTE); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, bucketStartTimeNs, bucketStartTimeNs); // Bucket is flushed yet. @@ -391,8 +395,9 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { metric.set_bucket(ONE_MINUTE); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - bucketStartTimeNs, bucketStartTimeNs); + + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); @@ -453,8 +458,8 @@ TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { int64_t oneDayNs = 24 * 60 * 60 * 1e9; int64_t fiveWeeksNs = 5 * 7 * oneDayNs; - CountMetricProducer countProducer( - kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard, + oneDayNs, fiveWeeksNs); int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index ddda71db4bd4..05cfa37b0ee1 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -70,9 +70,11 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { metric.set_aggregation_type(DurationMetric_AggregationType_SUM); FieldMatcher dimensions; - DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC/2); + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, durationProducer.mCurrentBucketNum); @@ -96,7 +98,8 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); @@ -138,10 +141,11 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); 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); + + DurationMetricProducer durationProducer( + kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); @@ -187,10 +191,11 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); 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); + + DurationMetricProducer durationProducer( + kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, + 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()); @@ -232,7 +237,8 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { metric.set_aggregation_type(DurationMetric_AggregationType_SUM); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); @@ -294,7 +300,8 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo metric.set_aggregation_type(DurationMetric_AggregationType_SUM); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); @@ -357,7 +364,8 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); @@ -402,7 +410,8 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); @@ -455,7 +464,8 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 5bae3648bd80..dfbb9da568b0 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -65,8 +65,8 @@ TEST(EventMetricProducerTest, TestNoCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - bucketStartTimeNs); + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); @@ -101,7 +101,8 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); + EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, + {ConditionState::kUnknown}, wizard, bucketStartTimeNs); eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); @@ -155,7 +156,8 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { // Condition is true for second event. EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); - EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); + EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, + {ConditionState::kUnknown}, wizard, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index cc5f45922d45..5997bedcdf2d 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -104,10 +104,9 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { // statsd started long ago. // The metric starts in the middle of the bucket - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, - -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, - pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1, + tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); gaugeProducer.prepareFirstBucket(); EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); @@ -147,9 +146,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -225,8 +224,8 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) { new EventMatcherWizard({new SimpleLogMatchingTracker( atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -308,7 +307,6 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({new SimpleLogMatchingTracker( atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); @@ -322,9 +320,9 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -393,9 +391,9 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) .WillOnce(Return(false)); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -450,7 +448,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, + {ConditionState::kUnknown}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -536,7 +535,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, + {ConditionState::kUnknown}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -584,9 +584,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { new EventMatcherWizard({new SimpleLogMatchingTracker( atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); Alert alert; @@ -683,9 +683,10 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { .WillOnce(Return(true)); int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, + triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); @@ -761,9 +762,10 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { .WillOnce(Return(true)); int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, + triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); @@ -823,9 +825,10 @@ TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { })); int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, + triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 14246cab0d96..5666501d7d51 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -111,15 +111,17 @@ public: EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) .WillRepeatedly(Return()); - sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); return valueProducer; } static sp<ValueMetricProducer> createValueProducerWithCondition( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, + ConditionState conditionAfterFirstBucketPrepared) { UidMap uidMap; SimpleAtomMatcher atomMatcher; atomMatcher.set_atom_id(tagId); @@ -133,15 +135,18 @@ public: .WillRepeatedly(Return()); sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); - valueProducer->mCondition = ConditionState::kFalse; + valueProducer->mCondition = conditionAfterFirstBucketPrepared; return valueProducer; } - static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition( - sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { + static sp<ValueMetricProducer> createValueProducerWithState( + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, + vector<int32_t> slicedStateAtoms, + unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) { UidMap uidMap; SimpleAtomMatcher atomMatcher; atomMatcher.set_atom_id(tagId); @@ -155,16 +160,18 @@ public: .WillRepeatedly(Return()); sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, + {}, slicedStateAtoms, stateGroupMap); valueProducer->prepareFirstBucket(); return valueProducer; } - static sp<ValueMetricProducer> createValueProducerWithState( + static sp<ValueMetricProducer> createValueProducerWithConditionAndState( sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, vector<int32_t> slicedStateAtoms, - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) { + unordered_map<int, unordered_map<int, int64_t>> stateGroupMap, + ConditionState conditionAfterFirstBucketPrepared) { UidMap uidMap; SimpleAtomMatcher atomMatcher; atomMatcher.set_atom_id(tagId); @@ -176,11 +183,13 @@ public: .WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) .WillRepeatedly(Return()); + sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, - {}, slicedStateAtoms, stateGroupMap); + kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown}, + wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap); valueProducer->prepareFirstBucket(); + valueProducer->mCondition = conditionAfterFirstBucketPrepared; return valueProducer; } @@ -205,6 +214,13 @@ public: metric.add_slice_by_state(StringToId(state)); return metric; } + + static ValueMetric createMetricWithConditionAndState(string state) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + metric.set_condition(StringToId("SCREEN_ON")); + metric.add_slice_by_state(StringToId(state)); + return metric; + } }; // Setup for parameterized tests. @@ -232,9 +248,9 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { // statsd started long ago. // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase, - 22, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, -1, + startTimeBase, 22, pullerManager); valueProducer.prepareFirstBucket(); EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); @@ -262,8 +278,8 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { // statsd started long ago. // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, -1, 5, + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); valueProducer.prepareFirstBucket(); @@ -427,7 +443,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { })); sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, + kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); @@ -629,7 +645,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); @@ -689,7 +706,8 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) { atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -762,7 +780,8 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) { data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120)); return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -813,7 +832,8 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) .WillOnce(Return(true)); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -851,7 +871,8 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse return true; })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); @@ -875,6 +896,7 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse {bucketStartTimeNs}, {partialBucketSplitTimeNs}); EXPECT_FALSE(valueProducer->mCondition); } + TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); @@ -887,7 +909,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -931,9 +953,9 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; @@ -1001,9 +1023,11 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, logEventMatcherIndex, eventMatcherWizard, + -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); valueProducer.prepareFirstBucket(); sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); @@ -1158,7 +1182,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { return true; })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); @@ -1229,7 +1254,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); @@ -1300,7 +1326,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1344,7 +1370,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1387,7 +1413,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1435,7 +1461,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1479,7 +1505,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1551,7 +1577,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1944,7 +1970,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice @@ -1979,7 +2006,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { .WillOnce(Return(false)); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); @@ -2023,7 +2051,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Don't directly set mCondition; the real code never does that. Go through regular code path // to avoid unexpected behaviors. @@ -2057,9 +2086,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Max delay is set to 0 so pull will exceed max delay. valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); @@ -2080,9 +2108,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucket2StartTimeNs, - bucket2StartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; @@ -2105,9 +2133,8 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->mHasGlobalBase = false; valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); @@ -2142,9 +2169,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - - valueProducer->mCondition = ConditionState::kTrue; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kTrue); // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2218,8 +2244,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 2); EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped); @@ -2283,9 +2309,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - - valueProducer->mCondition = ConditionState::kTrue; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kTrue); // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2363,9 +2388,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - - valueProducer->mCondition = ConditionState::kTrue; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kTrue); // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2468,7 +2492,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -2518,7 +2543,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); valueProducer->onConditionChanged(false, bucketStartTimeNs + 11); @@ -2566,7 +2592,8 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -2673,8 +2700,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kUnknown; + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kUnknown); valueProducer->onConditionChanged(false, bucketStartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); @@ -2814,7 +2841,8 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); @@ -2866,7 +2894,8 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); vector<shared_ptr<LogEvent>> allData; @@ -2911,7 +2940,7 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -2949,7 +2978,7 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -3002,7 +3031,7 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -3058,8 +3087,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges return true; })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); @@ -3099,8 +3128,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { return true; })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); @@ -3124,8 +3153,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) { sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Now the alarm is delivered. Condition is off though. vector<shared_ptr<LogEvent>> allData; @@ -3152,8 +3181,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { })) .WillOnce(Return(false)); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kFalse; + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); @@ -3191,7 +3220,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequeste })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 20); @@ -3236,7 +3266,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWron })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); @@ -3264,11 +3295,15 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWron report.value_metrics().skipped(0).start_bucket_elapsed_millis()); EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); + ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size()); auto dropEvent = report.value_metrics().skipped(0).drop_event(0); EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason()); EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis()); + + dropEvent = report.value_metrics().skipped(0).drop_event(1); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis()); } /* @@ -3298,7 +3333,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWro })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); @@ -3363,8 +3399,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, - metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kUnknown); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); @@ -3413,7 +3449,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) { .WillOnce(Return(false)); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); @@ -3468,7 +3505,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSki })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); @@ -3542,7 +3580,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); @@ -3579,7 +3618,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) { sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kFalse); // Check dump report. ProtoOutputStream output; @@ -3605,50 +3645,156 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) { } /* - * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket - * was not flushed in time. + * Test that all buckets are dropped due to condition unknown until the first onConditionChanged. */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) { +TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) { ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10)); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15)); + return true; + })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kUnknown); - // App update event. - int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000; - valueProducer->notifyAppUpgrade(appUpdateTimeNs); + // Bucket should be dropped because of condition unknown. + int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; + valueProducer->notifyAppUpgrade(appUpgradeTimeNs); + + // Bucket also dropped due to condition unknown + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // This bucket is also dropped due to condition unknown. + int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC; + valueProducer->onConditionChanged(true, conditionChangeTimeNs); // Check dump report. ProtoOutputStream output; std::set<string> strSet; - int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, + int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds + valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true, NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); StatsLogReport report = outputStreamToProto(&output); EXPECT_TRUE(report.has_value_metrics()); ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(2, report.value_metrics().skipped_size()); + ASSERT_EQ(3, report.value_metrics().skipped_size()); EXPECT_EQ(NanoToMillis(bucketStartTimeNs), report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), report.value_metrics().skipped(0).end_bucket_elapsed_millis()); ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), report.value_metrics().skipped(1).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), report.value_metrics().skipped(1).end_bucket_elapsed_millis()); ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size()); dropEvent = report.value_metrics().skipped(1).drop_event(0); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis()); + + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + report.value_metrics().skipped(2).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(dumpReportTimeNs), + report.value_metrics().skipped(2).end_bucket_elapsed_millis()); + ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size()); + + dropEvent = report.value_metrics().skipped(2).drop_event(0); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis()); +} + +/* + * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket + * was not flushed in time. + */ +TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); + return true; + })) + // App Update. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1000, 15)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, + ConditionState::kFalse); + + // Condition changed event + int64_t conditionChangeTimeNs = bucketStartTimeNs + 10; + valueProducer->onConditionChanged(true, conditionChangeTimeNs); + + // App update event. + int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000; + valueProducer->notifyAppUpgrade(appUpdateTimeNs); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds + valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(1, report.value_metrics().data_size()); + ASSERT_EQ(1, report.value_metrics().skipped_size()); + + ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size()); + auto data = report.value_metrics().data(0); + ASSERT_EQ(0, data.bucket_info(0).bucket_num()); + EXPECT_EQ(5, data.bucket_info(0).values(0).value_long()); + + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + report.value_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(appUpdateTimeNs), + report.value_metrics().skipped(0).end_bucket_elapsed_millis()); + ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); + + auto dropEvent = report.value_metrics().skipped(0).drop_event(0); EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); } @@ -3670,8 +3816,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, - metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kUnknown); // Condition change event. valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); @@ -3739,8 +3885,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) { })); sp<ValueMetricProducer> valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, - metric); + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, ConditionState::kUnknown); // First condition change event causes guardrail to be reached. valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); @@ -3872,12 +4018,13 @@ TEST(ValueMetricProducerTest, TestSlicedState) { return true; })); + StateManager::getInstance().clear(); sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerWithState( pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {}); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().clear(); StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); @@ -3887,10 +4034,18 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} auto it = valueProducer->mCurrentSlicedBucket.begin(); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(3, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(false, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( @@ -3900,10 +4055,18 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(5, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Bucket status after screen state change ON->OFF. @@ -3914,16 +4077,25 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(9, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(4, it->second[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Bucket status after screen state change OFF->ON. @@ -3934,22 +4106,34 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(21, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, OFF} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(12, it->second[0].value.long_value); // Value for dimension, state key {{}, ON} it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(4, it->second[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Start dump report and check output. @@ -3965,6 +4149,9 @@ TEST(ValueMetricProducerTest, TestSlicedState) { auto data = report.value_metrics().data(0); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); @@ -4043,12 +4230,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { } } + StateManager::getInstance().clear(); sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerWithState( pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap); // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().clear(); StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); @@ -4058,10 +4245,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} auto it = valueProducer->mCurrentSlicedBucket.begin(); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(3, itBase->second[0].base.long_value); - // Value for dimension, state key {{}, {}} - EXPECT_EQ(false, it->second[0].hasValue); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, {kStateUnknown}} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( @@ -4071,10 +4266,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(5, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + itBase->second[0].currentState.getValues()[0].mValue.long_value); // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Bucket status after screen state change ON->VR. @@ -4086,10 +4289,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(5, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Bucket status after screen state change VR->ON. @@ -4097,14 +4308,22 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12, android::view::DisplayStateEnum::DISPLAY_STATE_ON); StateManager::getInstance().onLogEvent(*screenEvent); - EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(5, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Bucket status after screen state change VR->OFF. @@ -4115,16 +4334,26 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(21, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(screenOffGroup.group_id(), + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), it->first.getStateValuesKey().getValues()[0].mValue.long_value); - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(16, it->second[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; - EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Start dump report and check output. @@ -4140,6 +4369,9 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { auto data = report.value_metrics().data(0); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); @@ -4250,12 +4482,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { return true; })); + StateManager::getInstance().clear(); sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerWithState( pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}); // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().clear(); StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); @@ -4264,23 +4496,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {uid 1}. auto it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(3, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{uid 1}, kStateUnknown} - // TODO(tsaichristine): test equality of state values key - // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Base for dimension key {uid 2} it++; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(7, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{uid 2}, kStateUnknown} - // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after uid 1 process state change kStateUnknown -> Foreground. auto uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4289,25 +4534,37 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {uid 1}. it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(6, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown}. - // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(3, it->second[0].value.long_value); // Base for dimension key {uid 2} it++; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(7, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, kStateUnknown} - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after uid 2 process state change kStateUnknown -> Background. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4316,23 +4573,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {uid 1}. it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(6, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown}. - EXPECT_EQ(true, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(3, it->second[0].value.long_value); // Base for dimension key {uid 2} it++; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(9, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, kStateUnknown} - // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); // Pull at end of first bucket. @@ -4350,33 +4620,54 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it = valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(15, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, BACKGROUND}. + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(false, it->second[0].hasValue); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Base for dimension key {uid 1} it++; - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(10, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(0, it->first.getStateValuesKey().getValues().size()); - // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* kStateTracker::kUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Value for key {uid 1, FOREGROUND} it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(false, it->second[0].hasValue); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Value for key {uid 2, kStateUnknown} it++; - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* kStateTracker::kUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after uid 1 process state change from Foreground -> Background. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4388,29 +4679,53 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); // Base for dimension key {uid 2}. it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(15, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, BACKGROUND}. - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Base for dimension key {uid 1} it++; - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(13, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown} - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Value for key {uid 1, FOREGROUND} it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(3, it->second[0].value.long_value); // Value for key {uid 2, kStateUnknown} it++; - EXPECT_EQ(false, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Bucket status after uid 1 process state change Background->Foreground. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4421,30 +4736,66 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); // Base for dimension key {uid 2} it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_EQ(false, it->second[0].hasValue); - - it++; - EXPECT_EQ(false, it->second[0].hasValue); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for key {uid 2, BACKGROUND} + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); // Base for dimension key {uid 1} it++; - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second[0].hasBase); EXPECT_EQ(17, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for key {uid 1, kStateUnknown} + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); + // Value for key {uid 1, BACKGROUND} - EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(4, it->second[0].value.long_value); + // Value for key {uid 1, FOREGROUND} it++; - EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_EQ(true, it->second[0].hasValue); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); EXPECT_EQ(3, it->second[0].value.long_value); + // Value for key {uid 2, kStateUnknown} + it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + // Start dump report and check output. ProtoOutputStream output; std::set<string> strSet; @@ -4466,6 +4817,9 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); data = report.value_metrics().data(2); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); @@ -4479,6 +4833,9 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { data = report.value_metrics().data(3); ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size()); EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); data = report.value_metrics().data(4); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); @@ -4490,6 +4847,212 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long()); } +TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { + // Set up ValueMetricProducer. + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState( + "BATTERY_SAVER_MODE_STATE"); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) + // Condition changed to true. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3)); + return true; + })) + // Battery saver mode state changed to OFF. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5)); + return true; + })) + // Condition changed to false. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data, bool) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( + pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}, + ConditionState::kFalse); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, + valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + util::BATTERY_SAVER_MODE_STATE_CHANGED)); + + // Bucket status after battery saver mode ON event. + // Condition is false so we do nothing. + unique_ptr<LogEvent> batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); + + // Bucket status after condition change to true. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator + itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); + EXPECT_TRUE(itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for key {{}, -1} + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second[0].hasValue); + + // Bucket status after battery saver mode OFF event. + unique_ptr<LogEvent> batterySaverOffEvent = + CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOffEvent); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); + EXPECT_TRUE(itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Pull at end of first bucket. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); + EXPECT_TRUE(itBase->second[0].hasBase); + EXPECT_EQ(11, itBase->second[0].base.long_value); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + + // Bucket 2 status after condition change to false. + valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); + EXPECT_FALSE(itBase->second[0].hasBase); + EXPECT_TRUE(itBase->second[0].hasCurrentState); + ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + itBase->second[0].currentState.getValues()[0].mValue.int_value); + // Value for key {{}, OFF} + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_TRUE(it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(2, report.value_metrics().data_size()); + + ValueMetricData data = report.value_metrics().data(0); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(1); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value()); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(6, data.bucket_info(0).values(0).value_long()); + EXPECT_EQ(4, data.bucket_info(1).values(0).value_long()); +} + +/* + * Test bucket splits when condition is unknown. + */ +TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition( + pullerManager, metric, + ConditionState::kUnknown); + + // App update event. + int64_t appUpdateTimeNs = bucketStartTimeNs + 1000; + valueProducer->notifyAppUpgrade(appUpdateTimeNs); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds + valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(0, report.value_metrics().data_size()); + ASSERT_EQ(1, report.value_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucketStartTimeNs), + report.value_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(appUpdateTimeNs), + report.value_metrics().skipped(0).end_bucket_elapsed_millis()); + ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); + + auto dropEvent = report.value_metrics().skipped(0).drop_event(0); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index 530ac5e01f3e..6516c1529514 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -16,6 +16,7 @@ #include "state/StateTracker.h" #include <gtest/gtest.h> +#include <private/android_filesystem_config.h> #include "state/StateListener.h" #include "state/StateManager.h" @@ -114,6 +115,55 @@ TEST(StateManagerTest, TestStateManagerGetInstance) { EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); } +TEST(StateManagerTest, TestOnLogEvent) { + sp<MockUidMap> uidMap = makeMockUidMapForPackage("com.android.systemui", {10111}); + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.updateLogSources(uidMap); + // Add StateTracker by registering a listener. + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); + + // log event using AID_ROOT + std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent( + timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + mgr.onLogEvent(*event); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); + + // log event using mocked uid + event = CreateScreenStateChangedEvent( + timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF, 10111); + mgr.onLogEvent(*event); + + // check StateTracker was updated by querying for state + queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); + + // log event using non-whitelisted uid + event = CreateScreenStateChangedEvent(timestampNs, + android::view::DisplayStateEnum::DISPLAY_STATE_ON, 10112); + mgr.onLogEvent(*event); + + // check StateTracker was NOT updated by querying for state + queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); + + // log event using AID_SYSTEM + event = CreateScreenStateChangedEvent( + timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON, AID_SYSTEM); + mgr.onLogEvent(*event); + + // check StateTracker was updated by querying for state + queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); +} + /** * Test registering listeners to StateTrackers * diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 582df0c1a2a3..cee83725d075 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -635,8 +635,17 @@ sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolated return uidMap; } -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state) { +sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids) { + sp<MockUidMap> uidMap = new StrictMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getAppUid(_)).Times(AnyNumber()); + EXPECT_CALL(*uidMap, getAppUid(pkg)).WillRepeatedly(Return(uids)); + + return uidMap; +} + +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs, + const android::view::DisplayStateEnum state, + int loggerUid) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); @@ -644,7 +653,7 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(loggerUid, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -654,6 +663,8 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); @@ -665,6 +676,8 @@ std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 6a5d5da2895c..3dcf4ecce054 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -243,9 +243,12 @@ std::shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeN sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids); +sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids); + // Create log event for screen state changed. -std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state); +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs, + const android::view::DisplayStateEnum state, + int loggerUid = 0); // Create log event for screen brightness state changed. std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp index db402a0dd658..32cecd3b9dbc 100644 --- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp +++ b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp @@ -50,13 +50,13 @@ TEST(MultiConditionTrigger, TestMultipleConditions) { }); vector<thread> threads; - vector<bool> done(numConditions, false); + vector<int> done(numConditions, 0); int i = 0; for (const string& conditionName : conditionNames) { threads.emplace_back([&done, &conditionName, &trigger, i] { sleep_for(chrono::milliseconds(3)); - done[i] = true; + done[i] = 1; trigger.markComplete(conditionName); }); i++; diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index 6384fb12ca68..51bcad115cc5 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -342,6 +342,9 @@ public class TestDrive { .addPullAtomPackages(PullAtomPackages.newBuilder() .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER) .addPackages("AID_STATSD")) + .addPullAtomPackages(PullAtomPackages.newBuilder() + .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER) + .addPackages("com.google.android.providers.media.module")) .setHashStringsInMetricReport(false); } } |